将c ++ lambda转换为c函数 [英] converting a c++ lambda to a c function

查看:119
本文介绍了将c ++ lambda转换为c函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一些包装代码,其中外部库调用c ++函数(使用可变参数模板等)。关键点是外部库需要一个c函数,这是正常的,因为这是合法的:

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

我想自动化的包装与类似:

  function_(context,name,myfunc); 

为此,我需要一个类似的函数:

  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);
});
}

其中第二个参数ctx.registerFunction的类型为LibraryFuncType。



但这当然是有问题的,因为lambda转换不再合法,由于捕获'fn'。然而,如果我不捕获'fn',那么我将无法访问它在lambda。



我认为唯一的办法来处理这是有一个静态变量,但是我不清楚的最好的方法来介绍它。我现在的解决方案是:

  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);
}

虽然从技术上讲,这是很危险的使用'function_helper'对具有相同签名的两个函数,它将为其中一个函数设置'fn'不正确。



此外,我可以做同样的危险静态变量通过在'function_'中声明一个静态变量。我让这个类希望它能导致一些洞察正确的方法来解决这个问题。



有没有人知道一个更好的方式使用lambda, t需要捕获(或者,一种将捕获的lambda转换为c函数的方法)?

解决方案

避免在注册代码中使用函数指针值的方法是使其成为模板参数。不幸的是,我不能提出一个非常好的符号。但是,如果可以使用类似下面的东西注册一个函数,那么它是相当直接的:

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

有了这个, RegisterHelper a static 函数 doRegister(),它将函数指针作为模板参数。这将是很好找到一种方法直接调用函数模板,并让它找出类型:

  doRegister< foo>(foo);但是,我还没有找到办法做到这一点,因为功能模板不能被部分专门化(否则)我认为这是可能的)。下面是代码如何看起来的大致轮廓。代码不会尝试执行任何您需要做的实际调用该函数的委派。它只是为了显示如何传入一个函数指针。演示硬编码某些类型,但只是因为添加任何编码代码将隐藏正在发生的事情。

  #include< string> 
#include< iostream>

struct State;
typedef std :: string(* function_type)(State *);
void registerFunction(std :: string const& name,function_type function)
{
std :: cout< 调用<名称<< ':<<函数(0)<< \\\
;
}

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){returnfoo; }
std :: string bar(int,double){returnbar; }

int main()
{

RegisterHelper< decltype(foo)> :: doRegister<& foo>(foo);
RegisterHelper< decltype(bar)> :: doRegister<& bar>(bar);
}


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)?

解决方案

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");
}

这篇关于将c ++ lambda转换为c函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆