Search code examples
c++cclassword-wrap

Wrapping C++ class API for C consumption


I have a set of related C++ classes which must be wrapped and exported from a DLL in such a way that it can be easily consumed by C / FFI libraries. I'm looking for some "best practices" for doing this. For example, how to create and free objects, how to handle base classes, alternative solutions, etc...

Some basic guidelines I have so far is to convert methods into simple functions with an extra void* argument representing the 'this' pointer, including any destructors. Constructors can retain their original argument list, but must return a pointer representing the object. All memory should be handled via the same set of process-wide allocation and free routines, and should be hot-swappable in a sense, either via macros or otherwise.


Solution

  • Foreach public method you need a C function.
    You also need an opaque pointer to represent your class in the C code.
    It is simpler to just use a void* though you could build a struct that contains a void* and other information (For example if you wanted to support arrays?).

    Fred.h
    --------------------------------
    
    #ifdef  __cplusplus
    class Fred
    {
        public:
        Fred(int x,int y);
        int doStuff(int p);
    };
    #endif
    
    //
    // C Interface.
    typedef void*   CFred;
    
    //
    // Need an explicit constructor and destructor.
    extern "C" CFred  newCFred(int x,int y);
    extern "C" void   delCFred(CFred);
    
    //
    // Each public method. Takes an opaque reference to the object
    // that was returned from the above constructor plus the methods parameters.
    extern "C" int    doStuffCFred(CFred,int p);
    

    The the implementation is trivial.
    Convert the opaque pointer to a Fred and then call the method.

    CFred.cpp
    --------------------------------
    
    // Functions implemented in a cpp file.
    // But note that they were declared above as extern "C" this gives them
    // C linkage and thus are available from a C lib.
    CFred newCFred(int x,int y)
    {
        return reinterpret_cast<void*>(new Fred(x,y));
    }
    
    void delCFred(CFred fred)
    {
        delete reinterpret_cast<Fred*>(fred);
    }
    
    int doStuffCFred(CFred fred,int p)
    {
        return reinterpret_cast<Fred*>(fred)->doStuff(p);
    }