简体   繁体   中英

Running C++ (with STL) functions in a C application

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

  • The compiler that I am using at the end is Arm Keil
  • There does not seem to be an option to include the 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.

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM