Question

I have a C library that I need to use in a C++ code, so I need to wrap the whole lib with an extern "C" block. The problem is that the library seems to include a C++ compiled code, so wrapping the whole lib would also wrap that C++ header.

includes

Inside lib.h I only include all the internal headers I want to expose, something like this:

#ifndef LIB_H
#define LIB_H

#include "lib_foo.h"
#include "lib_bar.h"
#include "lib_baz.h"

#endif

So the client will only need to include lib.h to use the lib.

In my first attempt I've done this:

#ifndef LIB_H
#define LIB_H

extern "C" {
  #include "lib_foo.h"
  #include "lib_bar.h"
  #include "lib_baz.h"
}

#endif

But then I get a symbol lookup error when I execute any function inside already_compiled_c++.h.

How can I avoid from applying extern "C" in the already_compiled_c++.h header file?


Edit:

Solved. This was not a problem using extern "C", it was a problem linking the compiled c++ library with gyp correctly: Using shared library in Gyp in node-sqlite3

Was it helpful?

Solution

Note that extern C isn't legal C, so it must only be included hen compiling as C++.

The already_compiled_c++.h header probably contains a guard against multiple includes, so just include it first:

#ifndef LIB_H
#define LIB_H

# This include added so that it won't get marked extern "C" when included by lob_foo.h.
#include <already_compiled_c++.h>

#ifdef __cplusplus
extern "C" {
#endif

  #include "lib_foo.h"
  #include "lib_bar.h"
  #include "lib_baz.h"

#ifdef __cplusplus
}
#endif

#endif

Note: You need to check if already_compiled_c++.h is conditionally included and add the corresponding conditionals.

OTHER TIPS

You cannot call C++ from a file compiled in plain C.

If lib_foo.h is plain C, it cannot directly use the functions in already_compiled_c++.h. You will most likely need to create a C wrapper for it.

This answer may help you further: Elegantly call C++ from C

If your diagram is accurate, then lib_foo.h should not be including already_compiled_c++.h. Edit it to stop including that.

If the functions declared in already_compiled_c++.h had their implementation compiled to use the C++ ABI, there is no way that your C program could link against them. (short of extremely ugly hacking).

To avoid snarls like this in the future, explicitly put #ifdef __cplusplus extern "C" { guards in every header file which will have a C implementation, but might be used in a C++ program, and vice versa.

A workaround for your current situation would be to make a .cpp file that has thunks for the functions you need. Publish your thunks under extern "C", and implement them by calling the functions in already_compiled_c++.h.

Rethink your design. You can't include C++ headers from C, only the reverse. A few suggestions:

1) Although the original idea behind 'extern "C"' was to wrap includes of C headers from C++ files, if you have headers that should be C-accessible but are implemented in C++, the common approach is to wrap the declarations in it and not the includes. (With the requisite #ifdef __cplusplus so the C code, which always treats headers as being C, doesn't choke on the unneeded C++-ism 'extern "C"')

2) Don't expose C++ as the public API of a library. C++ in general does not provide a binary stable ABI. If you add a method and it's not at the end, or if you add an instance variable to a class, or if you change something in a templated class, all clients would have to be recompiled (with static libraries that usually happens anyway, but this is a big problem for dylibs/DLLs/frameworks). So in general the best idea is to keep C++ as an implementation detail and only expose C features from your library.

3) If you need to expose C++ from your library, use the Pimpl (private implementation) pattern and no templates, and put it in a separate header that isn't included by the master C header, so C clients can just not include it. Such a separate header is also useful for #2 because your library's modules' implementation files (which are in C++) can include it and thus use the classes in it.

4) To C++, a pointer to a struct Foo and a pointer to a class Foo are the same thing. So if you need to return a C++ class from a C-only API implemented behind the scenes in C++, what you can do is just always use struct Foo* in the headers (which C understands correctly), and provide C wrapper functions for the class's methods like:

extern "C" int FooGetCount( struct Foo* thisFoo )
{
    return thisFoo->GetCount();
}

That way they can keep around pointers to C++ objects and access their properties, but don't need to actually use C++. Of course you also need to provide similar wrappers for creating/deleting the object, as C doesn't have the new/delete operators.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top