Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to include a C header using C11 atomics without modification in C++?

Tags:

c++

c

atomic

c11

I am attempting to write use the this C library without modification in a C++ application. It uses C11 atomics.

Consider the following program, which we can put in a file called main.cc.

#include "mpscq.h"


int main(){}

If I compile this with the g++ -std=c++11 -c main.cc, I get a whole collection of errors that look like the following.

usr/lib/gcc/x86_64-linux-gnu/4.9/include/stdatomic.h:68:9: error: ‘_Atomic’ does not name a type
 typedef _Atomic __UINT_FAST32_TYPE__ atomic_uint_fast32_t;
         ^
/usr/lib/gcc/x86_64-linux-gnu/4.9/include/stdatomic.h:69:9: error: ‘_Atomic’ does not name a type
 typedef _Atomic __INT_FAST64_TYPE__ atomic_int_fast64_t;

Is it possible to fix these errors without any modification to the library code?

That is, I am willing to use whatever magic incantations are needed in my c++ code or my compiler flags, but I'd rather not change the library code.

I have seen this answer but it requires modification of the header file.

like image 635
merlin2011 Avatar asked Aug 16 '18 18:08

merlin2011


2 Answers

Is it possible to fix these errors without any modification to the library code?

C++ does not have an _Atomic type qualifier, but the structure declared in the library header has members, including the first, with _Atomic-qualified type. C++ will not accept this (and wrapping the header inclusion in an extern "C" block will not help).

C allows objects of _Atomic-qualified type to have different representations than objects of the non-_Atomic version of the same type, so in a general sense, this means that C++ cannot directly access any members of any instance of that structure.

If your C implementation happened to use the same representation for corresponding _Atomic and non-_Atomic types, then you could probably make your C++ compiler accept the header by #defineing the symbol _Atomic to an empty string, but this would be a gateway to more subtle bugs. You could not expect C++ to provide the atomic access semantics that the library expects, so even in this case C++ cannot safely access the structure's members (directly).

If it is sufficient to manipulate instances of the struct only via the provided functions, then you can do so via opaque pointers. Create a version of the header that provides a forward declaration of the structure, but no definition, and that provides all the function declarations (with C linkage). You C++ code should accept the header and be able to call the functions, both receiving and returning pointers to instances of the structure that it must never try to dereference.

#ifndef __MPSCQ_H
#define __MPSCQ_H

#ifdef __cplusplus
extern "C" {
#endif

struct mpscq;

struct mpscq *mpscq_create(struct mpscq *n, size_t capacity);
// other functions ...

#ifdef __cplusplus
}
#endif

#endif

If you need more than the existing functions provide, then you'll need to write additional helper functions, in C.

like image 189
John Bollinger Avatar answered Sep 17 '22 22:09

John Bollinger


You do not need wrapper for using that mpscq in C++ code but you need different header to include it. That would work:

#ifndef MPSCQ_H_FOR_CPP
#define MPSCQ_H_FOR_CPP

extern "C"
{
    struct mpscq;

    mpscq *mpscq_create(mpscq *n, size_t capacity);

    bool mpscq_enqueue(mpscq *q, void *obj);

    void *mpscq_dequeue(mpscq *q);

    size_t mpscq_count(mpscq *q);

    size_t mpscq_capacity(mpscq *q);

    void mpscq_destroy(mpscq *q);
}

#endif

That said I would anyway write a wrapper to take care of RAII and such.

like image 24
Öö Tiib Avatar answered Sep 19 '22 22:09

Öö Tiib