#include <functional>
template<typename return_type, typename... argument_types>
struct stateful_function_pointer
{
using function_type = std::function<return_type(argument_types...)>;
using function_pointer_type = return_type (*) (argument_types..., void*);
function_pointer_type callback ()
{
return [ ] (argument_types... arguments, void* user_data)
{
return (*static_cast<function_type*>(user_data))(arguments...);
};
}
void* user_data()
{
return static_cast<void*>(&function);
}
function_type function;
};
The C style callback must be providing a void* user data as its final parameter (which is a de-facto standard). It may be possible to pass additional user data other than the function state, but I do not see the point as the lambda can already capture user state.
An eccentricity: It is instantiated as e.g. stateful_function_pointer<void, int, char*> instead of stateful_function_pointer<void(int, char*)>.
Example usage as requested (wrapping a HDF5 C function in C++):
class link_access_property_list : public property_list
{
public:
using function_pointer_type = stateful_function_pointer<herr_t, const char*, const char*, const char*, const char*, unsigned*, hid_t>;
using callback_type = function_pointer_type::function_type;
bool set_external_link_callback(const callback_type& callback)
{
function_pointer_.function = callback;
return H5Pset_elink_cb(id_, function_pointer_.callback(), function_pointer_.user_data()) >= 0;
}
protected:
function_pointer_type function_pointer_;
};
Then I could:
lapl.set_external_link_callback(
[CAPTURE_WHATEVER_YOU_WANT]
(const char*, const char*, const char*, const char*, unsigned*, hid_t)
{
USE_WHATEVER_YOU_WANT
return herr_t(0);
});
void*back to its original type if you don't know the type information? \$\endgroup\$void*. So you have to convert it to avoid*at some point and then you need to convert it back to the original type. You can't pass a lambda to be registered as a C function. \$\endgroup\$