Write a C wrapper around C++ classes with C++ callbacks
Asked Answered
P

1

7

I need to wrap a C++ library with C. This C++ library defines callback functions. For example:

    // from C++ library

    typedef X CallbackFn(Y y); // X and Y are classes

    class Z
    {
    public:
            void addCallback( CallbackFn* fn ) { callbackFn = fn; }
    private:
            CallbackFn* callbackFn;
    };

In the C wrapper I could define new C callbacks, which call the C++ callbacks. Something like this:

    // in C wrapper

    extern "C" {

    typedef int CallbackFnC(int n, ... );

    CallbackFnC *callbackFnC;

    int addCallback(void* handle, CallbackFnC* fn) // handle is pointer to Z instance
    {
        callbackFnC = fn;
        ((Z*)handle)->addCallback(callbackFnCPP);
    }

    }

    X callbackFnCPP(Y y)
    {
        int rtn = callbackFnC(y.n, ...);
        return X(rtn);
    }

where I assume I can map the relevant members of Y to the arguments of the C callback function and that I can sufficiently construct the return type X from the C return.

Is this going to work? There's no way around defining the new C callbacks?

The new C callback should be inside the extern "C" and the defined instance of the C++ callback should be outside?

Psychological answered 18/12, 2012 at 13:56 Comment(3)
Mixing c and c++ callbacks / function pointers sometimes works and sometimes doesn't, it depends on how things are defined. Your method of providing a translation callback is the safest pattern to follow for general use.Wanda
this is a standard way of loading dynamic libraries during run-timeConcert
Whether extern "C" function pointers are compatible with C++ linkage function pointers is implementation-defined. For portability, you need to use the approach you show.Pointsman
L
2

Yes, replacing the C++ callback with a C callback and a C++ wrapper function will work (with some caveats) and no, if the C++ callback insists on passing/returning classes or references (as opposed to primitive types supported also by C), then the wrapper method is the only possible solution.

The main caveat of using a wrapper function is that the wrapper function needs some way to find the correct C function to call. Using a global is an easy solution, but now you are limited to one callback for the entire program (regardless of the number of Z objects you have). More flexible solutions will depend on the design of the C++ callback and will range from easy (on registration, you can provide arbitrary user-data that will be provided to the callback) to very hard.

Languet answered 18/12, 2012 at 14:28 Comment(1)
Thanks! As I'm starting to implement the above solution, I immediately stumbled into the issue you mention. The C++ library can actually support arbitrary number of callbacks added with the same type and I'm struggling with how to handle this. Perhaps once I understand this better it'll lead to another question posted.Psychological

© 2022 - 2024 — McMap. All rights reserved.