4

我正在编写一些包装代码,其中外部库调用 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 类型。

但这当然是有问题的,因为由于捕获了“fn”,lambda 转换不再合法。但是,如果我不捕获“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 函数的方法)?

4

1 回答 1

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");
}
于 2012-09-22T22:41:39.427 回答