How to mix C ++ and C correctly

I am having some problems with this: I need to write a C shell for the C ++ library. Let's say I have 3 files:

  • wrapper.h

    typedef struct Foo Foo; Foo* create_foo(); 
  • wrapper.cpp

     extern "C" { #include "wrapper.h" } #include "foo.h" Foo* create_foo() { return new Foo; } 
  • foo.h

     class Foo { public: Foo(); }; 

This compiles fine:

clang++ -std=c++14 wrapper.cpp foo.h wrapper.h -shared -fPIC

clang++ -shared -o libbindings.so a.out

but when compiling a program using the C shell (it is a compiler and linked by a programming language using the Crystal shell), I get an undefined link to create_foo () and a linker error collect2: error: ld returned 1 exit status . How can I debug this (and what am I doing wrong)?

+4
source share
2 answers

Here is a working example:

wrapper.h (known C and C ++)

 #ifndef WRAPPER_H_ #define WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif typedef struct CPPClass CPPClass; CPPClass* CPPClass_new(); void CPPClass_do_something(CPPClass* cppclass); int CPPClass_get_state(CPPClass* cppclass); void CPPClass_delete(CPPClass* cppclass); #ifdef __cplusplus } #endif #endif /* WRAPPER_H_ */ 

wrapper.cpp (C ++ only)

 #include "wrapper.h" class CPPClass { int state; public: CPPClass(): state(0) {} void do_something() { ++state; } int get_state() const { return state; } }; extern "C" CPPClass* CPPClass_new() { return new CPPClass; } extern "C" void CPPClass_do_something(CPPClass* cppclass) { cppclass->do_something(); } extern "C" int CPPClass_get_state(CPPClass* cppclass) { return cppclass->get_state(); } extern "C" void CPPClass_delete(CPPClass* cppclass) { delete cppclass; } 

use-wrapper.c (C only)

 #include <stdio.h> #include "wrapper.h" int main(void) { CPPClass* cppclass = CPPClass_new(); if(!cppclass) { printf("ERROR: failed to create CPPClass:\n"); return 1; } printf("state: %d\n", CPPClass_get_state(cppclass)); CPPClass_do_something(cppclass); printf("state: %d\n", CPPClass_get_state(cppclass)); CPPClass_delete(cppclass); } 

Compile CPP

 g++ -std=c++11 -shared -fPIC -o libwrapper.so wrapper.cpp 

Compile C

 gcc -o use-wrapper use-wrapper.c -L. -lwrapper -lstdc++ 

Output:

 $ ./use-wrapper state: 0 state: 1 

Hope this helps.

+2
source

A shared object is created with the name a.out , and then another shared object called libbindings.so , which supposedly refers to a.out but does not refer to anything. Now, if there are no undefined characters in the input file set, no libraries are searched or added to the output. So libbindings.so is essentially an empty library. Make sure that:

  % nm a.out | grep create_foo 00000000000006bc T create_foo % nm libbindings.so | grep create_foo % 

If you have several source files, you must create an object file from each source (use the -c compilation flag) (then optionally merge the objects into a static library --- skip this step if you are not releasing static libraries), then create a shared object from previously constructed objects:

  clang++ -c -fPIC foo.cpp clang++ -c -fPIC bar.cpp clang++ -shared -o libfoobar.so foo.o bar.o 

If you have only one source or very few source files that you can easily compile together, you can create a shared library in one step:

  clang++ -std=c++14 wrapper.cpp somethingelse.cpp -shared -fPIC -o libbindings.so 

This is not recommended for large projects.

+1
source

All Articles