Create automatic C wrapper for C++ library?
Asked Answered
T

3

7

Let say I have a C++ DLL. AFAIK, there is no widely-adopted ABI standard for C++, therefore to make sure it works and does not depend on the compiler of the target application I would need to wrap my library in a C interface.

Are there any tools that can automatically generate such interface? Would also be nice if they could generate wrappers around C interface to look as if they are original C++ objects, e.g.

Foo* f = new Foo();  // FooWrapper* fw = Foo_create();
f->bar("test");      // Foo_bar(fw, "test")

translates into C functions that are invoked in my library using generated C ABI. I understand that C++ is fairly complicated language and not everything can be easily wrapped in a C interface, but I was wondering if there are any such solutions that even support a subset of the C++ language (maybe with the help of some manually written IDL/XML files)?

Tolmach answered 18/7, 2013 at 23:27 Comment(4)
You're going to run into trouble when you reach templates and exceptions.Edema
@MooingDuck: I was expecting that templates may be issue. But aren't templates always get compiled by the target compiler? Isn't that why they need to be always defined in the header file?Tolmach
true, if you aren't wrappipng template classes/functions but instead only wrap template instantiations, then you're fine.Edema
cpp2c.webs.com claims to do this but I haven't tried it and it says: “Cpp2C is a Python script. Cpp2C was tested with Python 2.6, … It hasNOT been tested with Python 3.”Frenchman
L
3

there is no widely-adopted ABI standard for C++

I'm pretty sure that is a bit exaggerated - there aren't THAT many different compilers available for any given platform, so it would probably be easier to just produce a DLL for each vendor (e.g. Microsoft, GCC on Windows, GCC on Linux, Sun and GCC for Solaris, GCC for MacOS - CLANG is compatible with GCC as far as I know).

To add a C layer interface basically means that the interface layer must not: 1. Use any objects of that require special copy/assignment/construction behaviour. 2. Use any "throw" exceptions. 3. Use virtual functions. across that interface.

It is my opinion that it's easier to "fix" the problems caused by "lack of ABI" than it is to make a good interface suitable for C++ use with a C interface in the middle of it.

Lemos answered 18/7, 2013 at 23:36 Comment(2)
I still think that is a better solution. Can't you link against Release libraries from debug code? You used to be able, if my memory serves.Lemos
You can in theory, but if those link with the other of the release/debug versions of the CRT, then you get linker errors.Edema
L
4

If you want a way to make C++ code callable from other compilers/standard libraries, you can use cppcomponents from https://github.com/jbandela/cppcomponents. Full disclosure - I am the author of the library.

Here is a simple hello world example

First make a file called library.h In this file you will define the Component

#include <cppcomponents/cppcomponents.hpp>

struct IPerson
:public cppcomponents::define_interface<cppcomponents::uuid<0xc618fd04,0xaa62,0x46e0,0xaeb8,0x6605eb4a1e64>>
{

    std::string SayHello();

    CPPCOMPONENTS_CONSTRUCT(IPerson,SayHello);



};

inline std::string PersonId(){return "library!Person";}

typedef cppcomponents::runtime_class<PersonId,cppcomponents::object_interfaces<IPerson>> Person_t;
typedef cppcomponents::use_runtime_class<Person_t> Person;

Next create library.cpp In this file you will implement the interface and component

#include "library.h"

struct PersonImplementation:cppcomponents::implement_runtime_class<PersonImplementation,Person_t>
{

    std::string SayHello(){return "Hello World\n";}

};

CPPCOMPONENTS_DEFINE_FACTORY(PersonImplementation);

Finally here is you main program (call it example1.cpp) that uses your implementation

#include "library.h"
#include <iostream>

int main(){
    Person p;
    std::cout << p.SayHello();

}

To build the program you will need to download cppcomponents (just clone from the git link above). It is a header only library and needs only a c++11 compiler.

Here is how you would build it on Windows

cl /EHsc example1.cpp /I pathtocppcomponents

g++ -std=c++11 library.cpp -o library.dll -shared -I pathtocppcomponents

where pathocppcomponents is the directory of cppcomponents. I am assuming you have cl and g++ in your path.

To run the program, make sure library.dll is in the same directory as example1.exe and run example1.exe

This library requires fairly compliant c++11 support, so it needs MSVC 2013 Preview, and at least g++ 4.7. This library works on both Windows and Linux.

Lynd answered 22/7, 2013 at 17:54 Comment(4)
That's much closer to what I need, though it still requires a lot of code to be written manually, which increases time necessary to define the interface and probability of an error. However, I believe some kind of translator from IDL to C using your library will allow to make it simpler to create such interfaces. Have you considered such approach?Tolmach
What do you have in mind for the IDL? Could you give a simple example?Lynd
I have started to sketch something here: tinypaste.net/gQnpIB8j, however it's incomplete.Tolmach
it seems the web-site has changed it's hosting and is currently not available. I've already deleted the snipper that I have uploaded there as the question is not relevant for me anymore.Tolmach
L
3

there is no widely-adopted ABI standard for C++

I'm pretty sure that is a bit exaggerated - there aren't THAT many different compilers available for any given platform, so it would probably be easier to just produce a DLL for each vendor (e.g. Microsoft, GCC on Windows, GCC on Linux, Sun and GCC for Solaris, GCC for MacOS - CLANG is compatible with GCC as far as I know).

To add a C layer interface basically means that the interface layer must not: 1. Use any objects of that require special copy/assignment/construction behaviour. 2. Use any "throw" exceptions. 3. Use virtual functions. across that interface.

It is my opinion that it's easier to "fix" the problems caused by "lack of ABI" than it is to make a good interface suitable for C++ use with a C interface in the middle of it.

Lemos answered 18/7, 2013 at 23:36 Comment(2)
I still think that is a better solution. Can't you link against Release libraries from debug code? You used to be able, if my memory serves.Lemos
You can in theory, but if those link with the other of the release/debug versions of the CRT, then you get linker errors.Edema
G
0

As far as I know the answer is no and you are supposed to handle this by yourself with a little bit of "hacking" and modifications, for example your t variable which is an std::string can possibly be "externed" to a C interface by t.c_str() because c_str returns a const char * which is a type that C understands without any problem at all.

I personally don't find C++ complicated, I can't see that "ABI issue" either, I mean nothing is perfect but you are externalizing to C your entire code base to "solve" this issue ? Just use C in the first place, also C it's no easy language to deal with either, for example in C there is not even the notion of "string", and problems that are trivial to solve in C++ while keeping everything type-safe, are really challenging in C if you want to meet the same goal.

I think that you are going a little bit too far with this, and you are complicating things, as it is now you have 3 + 1 main options on the most popular platforms :

  • libsupc++
  • libcxxrt
  • libc++abi
  • plus the whetever ABI is for the MSVC of your choice ( aka "only god knows")

for me, on linux, libsupc++ works very well, I'm following the libc++abi project and I don't see any big problem either, the only real problem with this is that llvm is basically an Apple oriented project for now, so there isn't that real and good support for the other platforms, but libc++abi compiles and works quite well on linux too ( although it's basically useless and pointless, on linux there is libsupc++ already.) .

I also would never ever use MSVC under Windows, in my opinion it's better to stick with a GCC-like compiler such as mingw, you got bleeding edge features, and you can simplify your codebase and your building phase a lot.

Guay answered 18/7, 2013 at 23:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.