21

I have some C++ code that returns a std::function. I would like to call this from some C code. Is this possible? As an example I have the following code:

typedef std::function<int(int)> AdderFunction;

AdderFunction makeAdder(int amount) {
    return [amount] (int n) {
        return n + amount;
    };
}

extern "C" {
    AdderFunction makeCAdder(int amount) {
        return makeAdder(amount);
    }
}

with clang++ -std=c++11 test.cpp it results in the following warning:

'makeCAdder' has C-linkage specified, but returns user-defined type 'AdderFunction' (aka 'function<int (int)>') which is incompatible with C

I understand why this is happening, but wondering if there is a pattern to make it possible?

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Stuart K
  • 3,212
  • 4
  • 22
  • 27
  • 3
    Why do you need this? It might be an instance of the [XY Problem](http://xyproblem.info). – Iharob Al Asimi Nov 01 '17 at 21:00
  • 2
    There are a few options. You can return a `void*` and allocate the `std::function` on the heap, and also provide functions to call back into C++, cast, and perform whatever operations on the function you wanted. Exactly how you convert the `std::function` into a C compatible data type is up to you; you could even put it into a raw character array (wrapped in a struct perhaps). There are a lot of options – Justin Nov 01 '17 at 21:00
  • https://stackoverflow.com/questions/2399344/calling-c-code-from-c – Karina Kozarova Nov 01 '17 at 21:01
  • 1
    You could make a pass through that returned a raw function pointer (e.g. `int (*AdderFunction)(int)`), but you cannot pass a `std::function`. This is essentially what [`std::function::target`](http://en.cppreference.com/w/cpp/utility/functional/function/target) does – Cory Kramer Nov 01 '17 at 21:01
  • @IharobAlAsimi context is I'm trying to manually define an interface to C++ code for Javascript with Emscripten. Embind works, but creates a lot of excess code and uses `eval` to achieve it. – Stuart K Nov 01 '17 at 21:08
  • That's interesting. Emscripten? Never heard of it. I will check that to learn about it. – Iharob Al Asimi Nov 01 '17 at 21:09
  • Regarding my comment: "*BTW: I cannot reproduce it with gcc*" Of course, it compiles with g++ but it's not possible to call it from c. – Iharob Al Asimi Nov 01 '17 at 21:14
  • @CoryKramer Isn’t language linkage part of a function type, so `std::function::target` would still not work? – Daniel H Nov 01 '17 at 21:26

5 Answers5

17

The most portable method to interface between C/C++ will be to use pointers to pass data between languages and use non-member functions to make function calls.

The .h file:

#ifdef __cplusplus
extern "C" {
#endif

   // Declare the struct.
   struct Adder;

   // Declare functions to work with the struct.
   Adder* makeAdder(int amount);

   int invokeAdder(Adder* adder, int n);

   void deleteAdder(Adder* adder);

#ifdef __cplusplus
}
#endif

Implement them in a .cpp file as:

#include <functional>

typedef std::function<int(int)> AdderFunction;
struct Adder
{
   AdderFunction f;
};

AdderFunction makeAdderFunction(int amount) {
    return [amount] (int n) {
        return n + amount;
    };
}

Adder* makeAdder(int amount)
{
   Adder* adder = new Adder;
   adder->f = makeAdderFunction(amount);
   return adder;
}

int invokeAdder(Adder* adder, int n)
{
   return adder->f(n);
}

void deleteAdder(Adder* adder)
{
   delete adder;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Will that let you actually *call* `f`, though? – Daniel H Nov 01 '17 at 21:08
  • @DanielH, as in, use `makeCAdder(10).f(20);`? – R Sahu Nov 01 '17 at 21:09
  • 1
    Thanks for the answer, unfortunately this results in a similar/the same warning: `'makeCAdder' has C-linkage specified, but returns user-defined type 'AdderWrapper' which is incompatible with C` – Stuart K Nov 01 '17 at 21:13
  • 1
    @RSahu Yes, that’s what the OP said he actually wants to do. I think the only way to do that is an `extern C` wrapper function which actually makes that call, but I’m not confident enough in my ability to write it to make that an answer. – Daniel H Nov 01 '17 at 21:25
  • 1
    Great, the expanded `make`/`invoke`/`delete` pattern works. Thanks! – Stuart K Nov 01 '17 at 22:46
  • You can avoid one level of indirection by creating the `function` using new and C casting to/from your struct type. – Michael Anderson Nov 02 '17 at 00:19
  • 1
    `__cplusplus` should be in lowercase if you're intending to use [the standard macro](http://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros). – Ruslan Nov 02 '17 at 06:23
8

It's not possible to call a std::function from C, because C doesn't support the language features that are required. C doesn't have templates, access modifiers, callable objects, virtual methods, or anything else that std::function could use under the hood. You need to come up with a strategy that C can understand.

One such strategy is to copy/move your std::function to the heap and return it as an opaque pointer. Then, you would provide another function through your C++ interface that takes that opaque pointer and calls the function that it contains.

// C side
struct function_opaque;
int call_opaque(struct function_opaque*, int param);

// C++ side
extern "C" {
    struct function_opaque {
        std::function<int(int)> f;
    };

    int call_opaque(function_opaque* func, int param) {
        return func->f(param);
    }
};

Of course, this comes with memory management implications.

zneak
  • 134,922
  • 42
  • 253
  • 328
5

You need to put the typedef inside the extern "C" block at the minimum (to get it to compile as C++). I'm not sure that will work from C, however. What will work from C is just to use plain function pointers, e.g.

extern "C" {
using AdderFunction = int(int);
// old-style: typedef int(*AdderFunction)(int);
}

Edit: If you're using an API that gives you std::function objects, you can use the std::function::target() method to obtain the (C-callable) raw function pointer it refers to.

using AdderFunction = std::function<int(int)>;
extern "C" {
using CAdderFunction = int(int);
CAdderFunction makeCAdder(int amount)
{
        return makeAdder(amount).target<CAdderFunction>();
}
}
Ray Hamel
  • 1,289
  • 6
  • 16
  • 1
    Yep, I tried this, unfortunately `target` returns a null pointer. The code looks like this: `CAdderFunction* makeCAdder(int amount) { return makeAdder(amount).target(); }` – Stuart K Nov 01 '17 at 21:18
  • 1
    I believe the above is because the `target_type` is different, as mentioned here http://en.cppreference.com/w/cpp/utility/functional/function/target_type – Stuart K Nov 01 '17 at 21:20
  • @StuartK Try making the return type of makeAdder `std::function`? – Ray Hamel Nov 01 '17 at 21:24
  • Another possibility is `return makeAdder(amount).target();` , I believe this should implicitly convert to CAdderFunction. – Ray Hamel Nov 01 '17 at 21:38
1

Another solution is to split the std::function into a pointer to the closure and a pointer to the member function, and pass three things to the C function that wants to invoke the lambda:

  • The address of a C++ function that knows how to invoke the function on the closure type-safely
  • The closure pointer (unsafely cast to void *)
  • The member function pointer (hidden inside a wrapper struct and cast to void * as well)

Here’s a sample implementation.

#include <functional>
#include <iostream>

template<typename Closure, typename Result, typename... Args>
struct MemberFunctionPointer
{
    Result (Closure::*value)(Args...) const;
};

template<typename Closure, typename Result, typename... Args>
MemberFunctionPointer<Closure, Result, Args...>
member_function_pointer(
    Result (Closure::*const value)(Args...) const)
{
    return MemberFunctionPointer<Closure, Result, Args...>{value};
}

template<typename Closure, typename Result, typename... Args>
Result
call(
    const void *const function,
    const void *const closure,
    Args... args)
{
    return
        ((reinterpret_cast<const Closure *>(closure))
        ->*(reinterpret_cast<const MemberFunctionPointer<Closure, Result, Args...>*>(function)->value))
        (std::forward<Args>(args)...);
}

Sample usage from the C side:

int
c_call(
    int (*const caller)(const void *, const void *, int),
    const void *const function,
    const void *const closure,
    int argument)
{
    return caller (function, closure, argument);
}

Sample usage from the C++ side:

int
main()
{
    int captured = 5;
    auto unwrapped = [captured] (const int argument) {
        return captured + argument;
    };
    std::function<int(int)> wrapped = unwrapped;

    auto function = member_function_pointer(&decltype(unwrapped)::operator());
    auto closure = wrapped.target<decltype(unwrapped)>();
    auto caller = &call<decltype(unwrapped), int, int>;
    std::cout
        << c_call(
            caller,
            reinterpret_cast<const void *>(&function),
            reinterpret_cast<const void *>(closure),
            10)
        << '\n';
}

The reason for the wrapper struct is that you can’t cast a member function pointer to void * or any other object pointer type, not even with reinterpret_cast, so instead we pass the address of the member function pointer. You can choose to place the MemberFunctionPointer structure on the heap, e.g. with unique_ptr, if it needs to live longer than it does in this simple example.

You can also wrap these three arguments in a single structure on the C side, rather than pass them individually:

struct IntIntFunction
{
    int (*caller)(const void *, const void *, int);
    const void *function;
    const void *closure;
};

#define INVOKE(f, ...) ((f).caller((f).function, (f).closure, __VA_ARGS__))

int
c_call(IntIntFunction function)
{
    return INVOKE(function, 10);
}
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
0

The problem with this solution is when you call makeAdder with parameter values.. Couldn't solve it but I'm posting just in case someone else can..

template <typename FunctionPointerType, typename Lambda, typename ReturnType, typename ...Args>
inline FunctionPointerType MakeFunc(Lambda&& lambda, ReturnType (*)(Args...))
{
    thread_local std::function<ReturnType(Args...)> func(lambda);

    struct Dummy
    {
        static ReturnType CallLambda(Args... args)
        {
            return func(std::forward<Args>(args)...);
        }
    };

    return &Dummy::CallLambda;
}

template <typename FunctionPointerType, typename Lambda>
FunctionPointerType MakeFunc(Lambda&& lambda)
{
    return MakeFunc<FunctionPointerType, Lambda>(std::forward<Lambda>(lambda), FunctionPointerType());
}


typedef int(*AdderFunction)(int);

AdderFunction makeAdder(int amount) {
    return MakeFunc<int(*)(int)>([amount] (int n) {
        return n + amount;
    });
}

extern "C" {
    typedef int(*CAdderFunction)(int);

    CAdderFunction makeCAdder(int amount)
    {
        return makeAdder(amount);
    }
}

It works by storing the lambda a thread local std::function. Then return a pointer to a static function which will call the lambda with the parameters passed in.

I thought about using an unordered_map and keeping track of each makeAdder call but then you can't reference it from static context..

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • 1
    This is an unsolvable problem (except by using run time code generation). There are a bunch of workarounds such as using thread-local variables, or templates if the argument value is known at compile time. Many C APIs allow passing an arbitrary ``void *data`` in addition to the callback function, though. In simple cases, you could just ``reinterpret_cast(myInt)``, or else point to a data structure. – Arne Vogel Nov 03 '17 at 13:06