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

ここで、2 番目のパラメータ「ctx.registerFunction」は LibraryFuncType 型です。

しかし、これにはもちろん問題があります。なぜなら、'fn' のキャプチャにより、ラムダ変換がもはや合法ではないからです。ただし、「fn」をキャプチャしないと、ラムダでアクセスできなくなります。

これに対処する唯一の方法は静的変数を持つことだと思いますが、それを導入する最善の方法は私には明らかではありません。私が持っている現在の解決策は次のとおりです。

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

技術的にはこれは機能しますが、明らかに危険です (そしてハッキーです)。同じシグネチャを持つ 2 つの関数で「function_helper」を使用すると、そのうちの 1 つに「fn」が正しく設定されないためです。

さらに、「function_」で静的変数を宣言するだけで、同じ危険な静的変数を実行できます。問題を回避する正しい方法についての洞察につながることを期待して、クラスを作成しました。

キャプチャを必要としないラムダを使用するより良い方法 (または、キャプチャを行うラムダを c 関数に変換する方法) を知っている人はいますか?

4

1 に答える 1

1

登録コード内で関数ポインター値を使用しないようにする 1 つの方法は、それをテンプレート引数にすることです。残念ながら、本当に素晴らしい表記法を思いつくことはできません。ただし、以下のようなものを使用して関数を登録することが許容される場合は、かなり簡単に実行できます。

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

これで、関数ポインタをテンプレート引数として取得する関数をRegisterHelper持つクラステンプレートです。関数テンプレートを直接呼び出す方法を見つけて、型を把握させるとよいでしょう:staticdoRegister()

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 に答える