8

I have a question related to embedding one library in another.

I have a code that is pure C and my users rely on that, they don't want to depend on C++ libraries. However, the need arose to embed a 3rd party library (ICU) into mine. None of the ICU functions would be exported, they would only be used internally in my library. Unfortunately ICU is a C++ library, though it does have a C wrapper. ICU does not use exceptions, but it does use RTTI (abstract base classes).

The question is how could I create my static library so that

  1. ICU is embedded in my library (all references to ICU functions are resolved within my library)
  2. all references to libstdc++ are also resolved and the necessary code is embedded into my library
  3. if a user does not even have libstdc++ installed on their system things work just fine
  4. if a user does happen to use my library within a C++ project then there are no conflicts with whatever libstdc++ (presumably the system libstdc++) he uses.

Is this possible at all? The targeted platforms are pretty much everything: windows (there my library is dynamic), and all sort of unix versions (linux, solaris, aix, hpux - here my library needs to be static).

gcc-4.5 and later does have --static-libstdc++, but as far as I understand it is only for creating shared libs or executables, and not static libs.

Thanks for any help!

Daniel Heilper
  • 1,182
  • 2
  • 17
  • 34
LaszloLadanyi
  • 973
  • 4
  • 12
  • 1
    This MIGHT not be possible.. Well in "my experience" at least. A library compiled with a specific version of libstdc++ doesn't always seem to be compatible with another version. Usually gives linker errors. Even for Visual Studio. I notices libraries like OpenCV ships different builds. VC10, VC11, VC12, etc (I built it last night).. What I used to do was compile everything down to their .o files. Then run `ar -rcs *.o` Where the o files would be all the dependencies like zlib, libpng, etc.. Don't think you can get away from libstd though.. I'd love an answer to this question too. – Brandon May 24 '14 at 02:28
  • 1
    dlopen+dlsym might be the way to use ICU from C without creating compile-time dependencies. – ArtemGr May 24 '14 at 09:17
  • @ArtemGr : that would be fine except for one thing: I need to distribute the icu lib as I cannot assume the user has it. But then, of course, I need to compile it. And (say I'm on linux) the C++ ABI for the compiler I have on my build machine may not be compatible (e.g., for RTTI resolution) with the C++ ABI the user has on his machine. So if I dlopen+dlsym the icu lib I have distributed I may run into problems when I start to run. That's why I want to resolve and bind everything and create my library that way. – LaszloLadanyi May 24 '14 at 16:27
  • The right thing to do on a UNIX-related OS is to install the system ICU. If you absolutely can't do that, then the beaten path is to build ICU from source (one example of building instructions: ceph.com/docs/master/install/build-ceph), but this is a wrong path unless you want your users to manually upgrade the ICU with future security patches. – ArtemGr May 24 '14 at 17:12
  • I'm not saying that it would not be the best to use a system wide ICU installation. I'm saying that is not an option for me :-(. And given that it is not an option I want to spare my users any possible C++ ABI conflict. – LaszloLadanyi May 24 '14 at 17:37
  • Maybe you need to dynamically find the best ICU: if ICU is already linked in into the running executable (on Linux you can check the `/proc/$pid/maps`) then you'll want to use the one linked. If not then you can try to link in the system ICU, if the system ICU is not available then you can link the one deployed with your library. – ArtemGr May 24 '14 at 20:25

2 Answers2

4

The solution to this problem is pretty simple, but may not fall inside the parameters you have set.

The simple rules are:

  1. You can dynamically link a C++ library to a C caller, but you have to wrap the library inside an extern C layer. You design the extern C API and implement it using C++ internals, which are forever hidden from view. [You can also use COM or .NET to do this on Windows.]
  2. You cannot statically link a C++ library to a C caller. The libraries are different and the calling sequences/linker symbols are different. [You often can't even statically link between different versions of the same compiler, and almost never between different compilers.]

In other words, the solution is simple: use dynamic linking. If that's not the right answer, then I don't think there is one.

Just to make things interesting, you can even implement your own plug-in architecture. That's just another name for dynamic linking, but you get to choose the API.


Just to be clear, the only viable portable option I can see is that you link ICU inside its own dynamic library (DLL or SO). Its symbols, C++ libs, RTTI and exceptions all stay inside that. Your static lib links to the ICU dynamic lib by extern C. That's exactly how much of Windows is built: C++ inside DLL, extern C.

You can debug across the boundary, but you cannot export type information. If you need to do that, you will have to use a different API, such as .NET or COM.

david.pfx
  • 10,520
  • 3
  • 30
  • 63
  • Yes, that is how the ICU C wrapper works, it wraps the ICU C++ library to an external C interface. And that is the interface I want to use in my C library. So that part is clear. What is not clear whether I can bind the symbols of the ICU library, its C wrapper and libstdc++ all at the time I create my library? I am afraid that it is not possible – LaszloLadanyi May 24 '14 at 16:10
  • if I want my lib static and I hope this is possible if my it is a shared (if it is, how?). Note that the binding must include all RTTI info, which is not out of question as it is guaranteed that I already have all possible instances of calls, and maybe I can indicate this to the binder. On the other hand ICU does not use exceptions (so far) so I don't need to deal with that. – LaszloLadanyi May 24 '14 at 16:19
  • From your edit it looks like if I can move away from the static lib requirement for my lib then in a shared lib I could put in all my stuff, ICU, ICU's symbols, C++ libs, RTTI and exceptions in a way so that it hides all the C++ parts and an executable that links with my shared lib does not reach out for C++ libs when runtime linking happens at the beginning of execution (or any time during execution). Can you give some hints which command line flags I should look at while RTFM? – LaszloLadanyi May 25 '14 at 19:11
  • You could only put your 'stuff' in a shared lib with ICU if both are compiled with the same compiler. Beyond that I'm really not clear what you're saying. – david.pfx May 26 '14 at 01:58
  • Yes, I have the source of both libraries and I can use the same compiler to compile (well, my code would be compiled with the C front-end and ICU with the C++ front-end). What I'm trying to say is that the ultimate goal is to have everything in one libfoo.so that provides only my C interface, though inside there are C and C++ (mine and ICU's). However, everything related to the C++ part inside is already resolved, properly bound within libfoo.so, and if my user links to libfoo.so no system C++ library dependency will sneak in. – LaszloLadanyi May 26 '14 at 02:43
0

I don't know if this will work, but let me at least suggest that you try it!

The excellent LLVM project (origin of clang compiler) has many front-ends and back-ends for different languages, such as C++ and C. And according to this S.O. question it should be possible for LLVM to compile C++ into C which in turn can be compiled as normal.

I imagine this route is a bumpy one, but if it works, it might solve your problem without the need to link dynamically. It all depends on if ICU will compile with LLVM C++.

If you do decide to give it a go, please let us know how you fare!

Community
  • 1
  • 1
Mr. Developerdude
  • 9,118
  • 10
  • 57
  • 95
  • I got really excited by this comment, but it's a no-go :-(. I found this post at https://groups.google.com/forum/#!topic/llvm-dev/Uu72xwp9z2w : The C Backend was removed in 3.1 (http://llvm.org/releases/3.1/docs/ReleaseNotes.html): "Major Changes and Removed Features ... The C backend has been removed. It had numerous problems, to the point of not being able to compile any nontrivial program." – LaszloLadanyi May 25 '14 at 19:01