I'd like to build a C++ library which is usable from C as well. This is the header file I want to be able to compile in C:
typedef void (*log_function_t)(const char *);
typedef void (*delay_function_callback_t)(uint32_t);
typedef void (*delay_function_t)(uint32_t, delay_function_callback_t);
extern "C" void core_init(log_function_t logFunction, delay_function_t delayFunction);
However, since I'm writing the library in C++, it would be nice to work with std::function objects instead of function pointers, so I'd like to call functions like this:
using LogFunction = std::function<void(const char*)>;
using DelayFunctionCallback = std::function<void(uint32_t)>;
using DelayFunction = std::function<void(uint32_t, DelayFunctionCallback)>;
void setLogFunction(const LogFunction& logFunction);
void setDelayFunction(const DelayFunction& delayFunction);
Calling the setLogFunction
works just fine, but when I try to call setDelayFunction
it doesn't work.
void core_init(log_function_t logFunction, delay_function_t delayFunction)
{
Utility::getInstance().setLogFunction(logFunction);
Utility::getInstance().setDelayFunction(delayFunction);
}
It says: Reference to type 'const DelayFunction' (aka 'const function<void (unsigned int, function<void (unsigned int)>)>') could not bind to an lvalue of type 'delay_function_t' (aka 'void(*)(unsigned int, void (*)(unsigned int))')
Obviously I understand why it doesn't work, but I have a feeling that it should be possible to solve and I'm just not experienced enough to solve it.
What you're asking seem to be passing a function pointer from C to C++ where the function takes a std::function
as argument. I'm afraid this is not possible just as C can't pass a function pointer that takes a std::vector
as argument.
When calling Utility::getInstance().setDelayFunction(delayFunction)
, the ctor of a specialized std::function
(ie DelayFunction
) is matched to construct from a function pointer. However, the match fails because the ctor (of DelayFunction
) accepts as its 2nd argument a specialized std::function
(ie DelayFunctionCallback
), rather than a function pointer (ie delay_function_callback_t
).
I think the problem lies in the implementation of std::function
, which encapsulates the function pointer and erases the latter's type. (See How is std::function implemented? ) As a result, a C++ std::function
is a different type than a plain-C function pointer.
To workaround this, you could relax the C++-ishness a bit and declare DelayFunction
as accepting void(*)(unsigned)
instead. Ie, in the C++ file:
using LogFunction = std::function<void(const char*)>;
using DelayFunction = std::function<void(unsigned, delay_function_callback_t)>;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
EDIT: Re. the comment on calling the DelayFunction
object from C++, instead of passing a lamba function as the callback (which would fail with the workaround above, since the lambda function can only construct a DelayFunctionCallback
, not a delay_function_callback_t
), it might be easier to implement the callback as a static member function and use it directly:
Utility::getInstance().delay(delay, (delay_function_callback_t)&Utility::next);
BTW, if Utility
is going to store the std::function
objects internally, then it may be more efficient to pass-by-value, since the LogFunction
and DelayFunction
objects will always be constructed anyway (ie they are rvalue in core_init
).
A void(*)()
is fundamentally different from a std::function<void()>
.
You can get closer with a void(*)( void* ), void*
; a std function has both callable-ness and state, a function pointer only has callable-ness. (std function also carries RTTI and how-to-cleanup-state and how-to-copy-state).
Now you can convert a void(*)()
into a std function that is stronger; but not the other way. And the arguments to a function are converted the other way when the call happens.
struct callback {
void* state;
void(*action)(int32_t);
void(*cleanup)(void*);
void*(*copy)(void*);
};
that is the rough C equivalent of a std::function<void(int32_t)>
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.