【问题标题】:converting a c++ lambda to a c function将 c++ lambda 转换为 c 函数
【发布时间】:2012-09-22 16:20:58
【问题描述】:

我正在编写一些包装代码,其中外部库调用 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 的更好方法(或者将捕获的 lambda 转换为 c 函数的方法)?

【问题讨论】:

  • 如果外部库好用,它会允许你将void*用户数据指针与回调一起传递,在调用时传回回调,你可以使用这个@987654326 @ 存储,例如,this 指针并转换回所需的类型。
  • 它实际上是为 Lua 准备的(我知道,已经有 1,000 个),所以应该可以将我自己的用户数据类型压入堆栈,以保留函数指针。但是,我宁愿完成所有这些编译时间。如果我不使用辅助函数,我可以只传递该函数而不会产生任何运行时损失。

标签: c++ static lambda type-conversion capture


【解决方案1】:

避免在注册代码中使用函数指针值的一种方法是将其设为模板参数。不幸的是,我无法想出一个非常好的符号。但是,如果可以使用类似下面的方法注册一个函数,那么这样做是相当简单的:

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

有了这个,RegisterHelper 是一个类模板,带有一个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 << "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");
}

【讨论】:

  • 经过大量实验,我还没有想出更好的方法来做到这一点。理论上,您可以通过执行以下操作来取消额外的类:“template ”,但是使用该类可以轻松获取参数的数量。为了使设计符合我最初的目标,我创建了宏:“#define function_(ctx, name, fn) RegisterHelper::registerFn(ctx, name)”,我可以这样做: function_(ctx, "name", fn);
猜你喜欢
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
相关资源
最近更新 更多