I'm currently trying to link a big C++ program to a C "wrapper," to allow integration with a program in another language whose compiler understands C but not C++ (Haskell GHC, to be precise.) But my attempts to do so, either with GHC or GCC, meet strange problems.
To model the situation concisely, say I have a main program file in C:
cmain.c
#include "header.h"
#include <stdio.h>
int main () {
printf("%d\n", cppfun(12));
return 0;
}
and a helper function defined in a .cpp file:
cppmodule.cpp
#include "header.h"
#include "further.h"
class foobar {
public:
int getfive () {return 5;}
};
extern "C" {
int cppfun(int foo) {
foobar fb;
return fb.getfive();
}
}
This much will compile just fine. But if, instead, cppmodule.cpp refers to a further .cpp file, like so:
cppmodule.cpp mk II
#include "header.h"
#include "further.h"
class foobar {
public:
int getfive () {return 5;}
};
extern "C" {
int cppfun(int foo) {
foobar fb;
return fb.getfive() + morecpp();
}
}
where the new .cpp file is something similar;
morecpp.cpp
#include "further.h"
class moreobjects {
public:
int getsix() {return 6;}
};
#ifdef __cplusplus
extern "C" {
#endif
int morecpp() {
moreobjects mo;
return mo.getsix();
}
#ifdef __cplusplus
}
#endif
I suddenly get an error when I'm trying to compile with a command like "gcc cmain.o cppmodule.o morecpp.o"; compiling with g++ works but, as I mentioned, this kind of solution doesn't fit my purposes.
The error I get trying to compile this example is
max@SoutheastCorner:~/Projectsync/maketest$ gcc cmain.o cppmodule.o morecpp.o
cppmodule.o:(.eh_frame+0x4b): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
The same kind of attempt with my actual project code additionally gives screenfulls of errors of the form
hopnode.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt10_List_nodeI4nodeIPcS3_EEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt10_List_nodeI4nodeIPcS3_EEE8allocateEmPKv]+0x4d): undefined reference to `operator new(unsigned long)'
/tmp/ccaoEEFM.o: In function `__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<char* const, node<char*, char*> > > >::allocate(unsigned long, void const*)':
hopnode.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt13_Rb_tree_nodeISt4pairIKPc4nodeIS3_S3_EEEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt13_Rb_tree_nodeISt4pairIKPc4nodeIS3_S3_EEEE8allocateEmPKv]+0x2c): undefined reference to `std::__throw_bad_alloc()'
hopnode.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt13_Rb_tree_nodeISt4pairIKPc4nodeIS3_S3_EEEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt13_Rb_tree_nodeISt4pairIKPc4nodeIS3_S3_EEEE8allocateEmPKv]+0x46): undefined reference to `operator new(unsigned long)'
Any insights would be greatly appreciated.
The problem is at the link stage. Your program is missing the symbols from the C++ standard library. To fix this, you have to either link with the g++ driver or you have to explicitly link in the C++ standard library.
Linking with g++ is the easiest solution, but you can also try adding -lstdc++
as a library flag.
Keep in mind that there are still a lot of pitfalls associated with this. The C++ ABI is not simple, and is not necessarily consistent across compilers (clang/g++/etc.) or even different versions of GCC. This can be a problem if your Haskell program dynamically links to other C++ code compiled with a different C++ ABI.
Also note that you also must catch all exceptions at the C/C++ boundary. Haskell expects a straight C ABI and cannot deal with C++ exceptions that leak through the C/C++ boundary.