Reputation: 4614
I am creating a C++ library with exported C functions that use some STL functionality. I want to include the this library in a C application.
I have reduced the problem as much as I could to the following 4 files.
main.c
#include "aaa.h"
#include <stdio.h>
int main()
{
printf("Version: %u.%u\n", GetAPIMajorVersion(), GetAPIMinorVersion());
return 0;
}
aaa.h
#ifndef AAA_H
#define AAA_H
#ifdef __cplusplus
#define DllExport extern "C"
#else // __cplusplus
#define DllExport
#endif // __cplusplus
#include <stdint.h>
DllExport uint32_t GetAPIMajorVersion();
DllExport uint32_t GetAPIMinorVersion();
#endif // AAA_H
aaa.cpp
#include "aaa.h"
#include <string>
#include <vector>
// Builds and works fine.
uint32_t GetAPIMajorVersion()
{
std::string val = "hello world";
return val.size();
}
// Produces the error messages
uint32_t GetAPIMinorVersion()
{
std::vector<bool> test;
test.push_back(true);
return test.size();
}
I am using the following script to build the library and the application.
build.sh
# Build the C++ library
g++ -m64 -Wall -O3 -c -fmessage-length=0 -fPIC -MMD -MP aaa.cpp -o aaa.o
ar rcs libaaa.a aaa.o
# Build the executable
gcc -m64 -Wall -static main.c -o main -L./ -laaa
I get the following errors when I try to build the C application
.//libaaa.a(aaa.o): In function `GetAPIMinorVersion':
aaa.cpp:(.text+0xeb): undefined reference to `operator delete(void*)'
aaa.cpp:(.text+0x1c7): undefined reference to `operator delete(void*)'
.//libaaa.a(aaa.o): In function `std::vector<bool, std::allocator<bool> >::_M_insert_aux(std::_Bit_iterator, bool)':
aaa.cpp:(.text._ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb[_ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb]+0x1d8): undefined reference to `operator new(unsigned long)'aaa.cpp:(.text._ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb[_ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb]+0x339): undefined reference to `operator delete(void*)'
aaa.cpp:(.text._ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb[_ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb]+0x3cb): undefined reference to `std::__throw_length_error(char const*)'.//libaaa.a(aaa.o):(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
I looked into this error and it seems to be because the C application does not have access to the STL libraries but If we alter main.c to only make a call to the GetAPIMajorVersion()
and remove the GetAPIMinorVersion()
function from the library. The application compiles and runs as expected.
That leads me to believe that the issue is not with the STL library in general but with some of the functions in the STL library.
My next guess is that it is possible that the std::vector<bool>::push_back()
function could throw a exception and this is including elements into the aaa.a library that the C application can not find.
If this is the issue then, how do I include the require parts of the STL library in the aaa.a library so it can be used by the C Application?
I have found that if I change the C application to be build with g++
instead of gcc
it builds and runs fine. Unfortunately the compiler that I am using in the end only supports C99 and this is not an option for me.
g++ -m64 -Wall -static main.c -o main -L./ -laaa
How should I build this library, that includes STL functions, in a way that the library functions can be called from a C application?
Edit
stdc++
as a library in the Arm Keil IDE/Compiler. I can't change the command to build the C application to gcc -m64 -Wall -static main.c -o main -L./ -laaa -lstdc++
as far as I am aware. Upvotes: 0
Views: 406
Reputation: 1
You could try to build a C++ shared library, linking -lstdc++
.
So let -laaa
be a shared library libaaa.so
(from source files aaa1.cc
and aaa2.cc
, and having position-independent code) that you would build with:
g++ -fPIC -O3 -g aaa1.cc -o aaa1.pic.o
g++ -fPIC -O3 -g aaa2.cc -o aaa2.pic.o
g++ -fPIC -shared -O3 -g aaa1.pic.o aaa2.pic.o -lstdc++ -o libaaa.so
You might also set some rpath.
Read Program Library HowTo and Drepper's How to write shared libraries
The compiler that I am using at the end is Arm Keil
You'll better use instead some recent version of a GCC cross-compiler (or of Clang one). Either you build that cross-compiler yourself from the source code of GCC 8 (in autumn 2018), or you install some cross-compiler on your Linux distribution. For example, Debian/Sid has gcc-8-arm-linux-gnueabi
and gcc-8-arm-linux-gnueabihf
By experience, hardware vendors provide ancient cross-compilers (and are not good in software engineering). That is why I recommend using a recent GCC cross-compiler, on the command line.
And you'll better link your application with g++
.
My next guess is that it is possible that the std::vector::push_back() function could throw a exception
Exceptions need some support at the crt0 level (for std::terminate). If your library throws some exception, the main program has to be linked with g++
(if you want a C++ library usable from C, it should not throw exception outside).
However, it is possible, with some care, to build a C++ library usable from gcc
-compiled C code. The libgccjit is such a library (but it does not throw exceptions outside).
I can't change the command to build the C application to gcc -m64 -Wall -static main.c -o main -L./ -laaa -lstdc++ as far as I am aware
You surely could. You need to avoid using Arm Kell and use directly the appropriate cross-compiler on the command line (either the one supplied inside it, or preferably a more recent one that you build from GCC source code or Clang one).
Upvotes: 1