converting a c++ lambda to a c function

2019-05-08 14:19发布

I'm writing some wrapping code, where an external library calls a c++ function (using variadic templates and such). The crucial point is that the external library requires a c-function, which would normally be fine, as this is legal:

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

While I can easily do this by hand, I would like to automate the wrapping with something like:

function_(context, "name", myfunc);

To do this, I would 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 the type LibraryFuncType.

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

I think the only way to deal with this is to have a static variable, but it's not clear to me the best way to introduce it. The current solution I have is:

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's clearly dangerous (and hacky), because if I use 'function_helper' on two functions with the same signature, it will set 'fn' incorrectly for one of them.

Additionally, I could do the same dangerous-static variable by just declaring a static variable in 'function_'. I made the class hoping it would lead to some insight into a correct way of getting around the problem.

Does anyone know of a better way of using a lambda that doesn't require a capture (or alternatively, a way of converting a lambda that does capture to a c-function)?

1条回答
相关推荐>>
2楼-- · 2019-05-08 14:32

One way to avoid using a function pointer value within your registration code is to make it a template argument. Unfortunately, I can't quite come up with a really nice notation. However, if it is acceptable to register a function using something like below, it is fairly straight forward to do:

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

With this, RegisterHelper is class template with a static function doRegister() which gets the function pointer as template argument. It would be nice to find a way to call a function template directly and have it figure out the type:

doRegister<&foo>("foo");

However, I haven't found a way to do this because function templates cannot be partially specialized (otherwise I think it would be possible). Below is a rough outline of how the code could look. The code doesn't try to do any of the delegation you'd need to do to actually call the function. It is merely intended to show how a function pointer can be passed in. The demo hard-codes some types but only because adding any marshaling code would hide what is going on.

#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");
}
查看更多
登录 后发表回答