Convert C ++ lambda to c function

I am writing some packaging code where an external library calls a C ++ function (using variable templates, etc.). The decisive point is that the external library requires a c-function, which, as a rule, will be beautiful, since it is legal:

LibraryFuncType fn = [](params) { ... } 

While I can easily do this manually, I would like to automate the packaging with something like:

 function_(context, "name", myfunc); 

To do this, I will need a function similar to:

 template <ReturnType, ParamTypes...> static void function_(Context &ctx, const std::string &name, std::function<ReturnType(ParamTypes...)> fn) { ctx.registerFunction(name, [fn](State *state) -> int { Context ctx(state); return apply_helper<sizeof..(ParamTypes)>::apply(ctx, fn); }); } 

where the second parameter "ctx.registerFunction" is of type LibraryFuncType.

But this, of course, is problematic because lambda conversion is no longer legal due to the capture of 'fn'. However, if I did not capture 'fn', then I would not have access to it in lambda.

I think the only way to handle this is to have a static variable, but I don’t understand how best to implement it. My current solution:

 template <typename ReturnType, typename... ParamTypes> struct function_helper { static std::function<ReturnType(ParamTypes...)> fn; function_helper(std::function<ReturnType(ParamTypes...)> _fn) { fn = _fn; } static void registerFn(Context &ctx, const std::string &name) { ctx.registerFn(name, [](state *state) -> int { Context ctx(state); return apply_helper<sizeof...<ParamTypes>>::apply(ctx, fn); }); } }; template <typename ReturnType, typename... ParamTypes> std::function<ReturnType(ParamTypes...)> function_helper<ReturnType, ParamTypes...>::fn; template <typename ReturnType, typename... ParamTypes> void function_(Context &ctx, const std::string &name, std::function<ReturnType(ParamTypes...)> fn) { function_helper<ReturnType, ParamTypes...> reg(fn); reg.registerFn(ctx, name); } 

While technically this works, it is clearly dangerous (and hacked) because if I use the function_helper function for two functions with the same signature, it will set fn incorrectly for one of them.

Alternatively, I could make the same dangerous static variable by simply declaring the static variable in 'function_'. I made a class hoping this would lead to some understanding of the correct way to solve the problem.

Does anyone know of a better way to use a lambda that does not require capture (or, alternatively, a method of converting a lambda that performs capture into a c function)?

+4
source share
1 answer

One way to avoid using the value of a function pointer in your registration code is to make it a template argument. Unfortunately, I cannot come up with a very good notation. However, if it is valid to register the function using something like below, it is quite simple:

 RegisterHelper<decltype(foo)>::doRegister<&foo>("foo"); 

In this case, RegisterHelper is a class template with a static function doRegister() , which receives a pointer to the function as an argument to the template. It would be nice to find a way to directly call the function template and find out its type:

 doRegister<&foo>("foo"); 

However, I did not find a way to do this, because function templates cannot be partially specialized (otherwise I think that would be possible). The following is a brief overview of how the code looks. The code does not attempt to execute any delegation necessary to actually call the function. It is simply intended to show how a function pointer can be passed. Demonstration of hard codes of some types, but only because adding any marshaling code hides what is happening.

 #include <string> #include <iostream> struct State; typedef std::string (*function_type)(State*); void registerFunction(std::string const& name, function_type function) { std::cout << "calling '" << name << "': " << function(0) << "\n"; } template <typename T> class RegisterHelper; template <typename RC, typename... Args> class RegisterHelper<RC(Args...)> { public: template <RC (*function)(Args...)> static void doRegister(std::string const& name) { registerFunction(name, [](State*) -> std::string { return function(17, 4.0); }); } }; std::string foo(int, double) { return "foo"; } std::string bar(int, double) { return "bar"; } int main() { RegisterHelper<decltype(foo)>::doRegister<&foo>("foo"); RegisterHelper<decltype(bar)>::doRegister<&bar>("bar"); } 
+1
source

Source: https://habr.com/ru/post/1435674/


All Articles