【问题标题】:How do you use JIT compilation to produce a C pointer callback from a captured lambda?如何使用 JIT 编译从捕获的 lambda 生成 C 指针回调?
【发布时间】:2020-04-30 17:50:22
【问题描述】:

在 C++11 及更高版本中,转换不带捕获变量的 lambda 表达式非常简单,但要正确执行具有捕获变量的 lambda,需要根据 answer 的这些家伙生成动态代码。

基本上我认为必须动态生成的是这个。 (注意这里的代码表示我想要的语义而不是任何真正的代码)

UserData *userdata;
api_return_value callback(api_data arg) {
    return customized_callback(arg, userdata);
}

我会生成这个,这样我就可以将动态生成的代码绑定到某个 api 中定义的相应函数指针回调

api_return_value (*callback) (api_data);

是否有一种使用 LLVM 或 NativeJIT 之类的相当干净且可移植的方法来解决这个问题?我一直在使用没有提供用户数据指针的 C api,所以这似乎是我唯一的选择。

【问题讨论】:

  • 您可以控制userdata 指向的位置? IE。 C api 是否提供将用户上下文传递给回调?
  • 不,他们认为您只需要一个实例。我正在使用的 api 在这个头文件github.com/libretro/RetroArch/blob/master/libretro-common/… 中定义和记录,据我所知,没有一个 api 函数允许这种功能
  • 您链接的文件有 2700 行。回调在哪里?它是如何通过的?它是如何相关的?请编辑问题以提供所有相关信息。所以无论如何你都要使用全局变量。那么是什么阻止您使用 std::function<api_return_value()> func_pointer 并将 lambda 存储在其中?请发布示例代码。你到底想做什么?您发布的与 lambdas 相关的小代码是如何剪断的?
  • 糟糕的是,我写这篇文章的时候很着急。我尝试添加更多上下文。任何地方都没有编写 lambda 函数,因为我基本上打算在运行时手动生成我自己的函数,这就是问题的重点。
  • 你有一个c++ 标签(没有c 标签),但你说你想要一个C API?那是什么意思?如果您真的想要 C(而不是 C++),请更改标签并使其清楚。如果你想要 C++,这就是 std::function 的用途——不需要 JIT 或其他任何东西。

标签: c++ lambda llvm function-pointers jit


【解决方案1】:

您可以在编译时“预创建”一个(外部“C”)函数池并管理它们的分配池,而不是在运行时生成 JIT 代码。比如:

#define REP10(P, M)  M(P##0) M(P##1) M(P##2) M(P##3) M(P##4) M(P##5) M(P##6) M(P##7) M(P##8) M(P##9)
#define REP100(M) REP10(,M) REP10(1,M) REP10(2,M) REP10(3,M) REP10(4,M) REP10(5,M) REP10(6,M) REP10(7,M) REP10(8,M) REP10(9,M)

extern struct func_wrap_t {
    func_wrap_t              *next;
    extern "C" void          (*c_fn)();
    std::function<void()>    fn;
} func_wrap_table[];
#define FUNC_WRAP_INIT(M) { M ? func_wrap_table+M-1 : 0, func_wrap_cfunc##M },
#define FUNC_WRAP_DEF(M) extern "C" void func_wrap_cfunc##M() { func_wrap_table[M].fn(); }
REP100(FUNC_WRAP_DEF)
func_wrap_t func_wrap_table[] = { REP100(FUNC_WRAP_INIT) };
func_wrap_t *func_wrap_freelist = &func_wrap_table[99];

将静态创建 100 个这样的函数并将它们链接到一个空闲列表中。然后,您可以编写一个包装器 RAII 类型,从该列表中分配它们并在完成时返回它们。

【讨论】:

  • 我无法确定我从来不需要这样做是否意味着我做对了,还是做错了。
  • @3Dave:这意味着您无需处理设计不佳且无法更改的外部界面...
  • 谢谢,这是一个非常巧妙的技巧,非常适合我的工作。不过,我花了一分钟的时间来解析发生了什么。为了后代,我还必须删除 extern "C" 限定,以让我的编译器停止抱怨。我希望这不会产生任何不利影响。
猜你喜欢
  • 1970-01-01
  • 2020-01-13
  • 2014-07-09
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 1970-01-01
相关资源
最近更新 更多