Lua Gen Plus Plus

lua-users home
wiki

luagen++ 是一个 C++ 头文件,它使用模板元编程技术在编译时生成高效的 Lua C API 调用序列。

描述

警告:请查看下面的状态消息以了解该项目的当前状态。

这可能是一篇长文章,描述了 C++ 模板元编程技术在生成 *编译时* 执行效率高的 Lua C API 调用序列方面的实现。这将最终形成一个可重用的 C++ 头文件(luagen.hpp - 称为“luagen++”),它实现了这些技术。您可以使用它以一种干净但仍然非常有效的方式绑定 C++ 和 Lua 代码。

例如,以下 C++ 代码

#include <luagen.hpp>
using namespace luagen;
...
eval(L, global("print")(global("math")["sqrt"](lnumber(2.0))));

在编译时扩展为以下代码

lua_getglobal(L,"print")
lua_getglobal(L,"math")
lua_getfield(L,-1,"sqrt")
lua_remove(L,-2)
lua_pushnumber(L,2.0)
lua_call(L,1, -1)
lua_call(L,1, 0)

现在,已经有很多技术可以将 Lua 和 C++ 代码绑定在一起(BindingCodeToLua)。这里的方法具有以下特点

我发现与之最相关的项目是 Luabind 中的 object.hpp [3]。它也使用 C++ 模板元编程技术,但代码生成似乎没有达到上面第 1 点中提到的最佳品质。Luabind 包含 Boost 头文件 [4],并且大约有 10K 行复杂模板元编程代码的头文件,这些代码没有得到充分的文档记录,并且它提供了比这里需要的更高层次和更自动的定义(例如模块和类继承定义)。虽然 luagen.hpp 可能会包含几千行代码来完整地包装 C API 调用,但核心技术本身(可以隔离)可以在几百行合理易懂的代码中实现,本文将向你展示如何实现。

因此,本文的目的是双重的:演示“luagen++”头文件 (luagen.hpp) 并解释其实现。

描述

待办事项! - 这里可能会有一个关于实现的冗长且非常有趣的讨论。

反汇编

如果你不相信我,请查看反汇编(注意:luagen.hpp 中可能有一些额外的 `lua_gettop` 可以消除)

	.file	"test.cpp"
	.text
	.align 2
	.p2align 4,,15
	.def	__ZN6luagen11debugprintfEz;	.scl	3;	.type	32;	.endef
__ZN6luagen11debugprintfEz:
	pushl	%ebp
	movl	%esp, %ebp
	popl	%ebp
	ret
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC2:
	.ascii "sqrt\0"
LC0:
	.ascii "print\0"
LC1:
	.ascii "math\0"
	.text
	.align 2
	.p2align 4,,15
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	$16, %eax
	movl	%esp, %ebp
	pushl	%edi
	pushl	%esi
	pushl	%ebx
	subl	$92, %esp
	andl	$-16, %esp
	call	__alloca
	call	___main
	call	_luaL_newstate
	movl	%eax, (%esp)
	movl	%eax, %ebx
	call	_luaL_openlibs
	movl	$LC2, -28(%ebp)
	leal	-24(%ebp), %eax
	movl	-28(%ebp), %edx
	movl	%eax, -32(%ebp)
	movl	-32(%ebp), %eax
	movl	%edx, -36(%ebp)
	movl	%ebx, (%esp)
	movl	%eax, -40(%ebp)
	leal	-40(%ebp), %eax
	movl	%eax, -56(%ebp)
	leal	-48(%ebp), %eax
	movl	%eax, -52(%ebp)
	movl	-56(%ebp), %eax
	movl	$LC0, -20(%ebp)
	movl	-52(%ebp), %edx
	movl	$LC1, -24(%ebp)
	movl	%eax, -64(%ebp)
	leal	-20(%ebp), %eax
	movl	%eax, -72(%ebp)
	leal	-64(%ebp), %eax
	movl	%eax, -68(%ebp)
	movl	-72(%ebp), %eax
	movl	%edx, -60(%ebp)
	movl	-68(%ebp), %edx
	movl	$0, -48(%ebp)
	movl	$1073741824, -44(%ebp)
	movl	%edx, -76(%ebp)
	movl	%eax, -80(%ebp)
	call	_lua_gettop
	movl	-80(%ebp), %eax
	movl	(%eax), %eax
	movl	%ebx, (%esp)
	movl	%eax, 8(%esp)
	movl	$-10002, %eax
	movl	%eax, 4(%esp)
	call	_lua_getfield
	movl	%ebx, (%esp)
	call	_lua_gettop
	movl	%eax, -84(%ebp)
	movl	-76(%ebp), %edi
	movl	%ebx, (%esp)
	call	_lua_gettop
	movl	(%edi), %esi
	movl	(%esi), %eax
	movl	(%eax), %eax
	movl	%ebx, (%esp)
	movl	%eax, 8(%esp)
	movl	$-10002, %eax
	movl	%eax, 4(%esp)
	call	_lua_getfield
	movl	4(%esi), %eax
	movl	%ebx, (%esp)
	movl	%eax, 8(%esp)
	movl	$-1, %eax
	movl	%eax, 4(%esp)
	call	_lua_getfield
	movl	%ebx, (%esp)
	movl	$-2, %eax
	movl	%eax, 4(%esp)
	call	_lua_remove
	movl	%ebx, (%esp)
	call	_lua_gettop
	movl	%eax, %esi
	movl	4(%edi), %eax
	fldl	(%eax)
	movl	%ebx, (%esp)
	fstpl	4(%esp)
	call	_lua_pushnumber
	movl	%ebx, (%esp)
	call	_lua_gettop
	movl	%ebx, (%esp)
	subl	%esi, %eax
	movl	$-1, %esi
	movl	%esi, 8(%esp)
	movl	%eax, 4(%esp)
	call	_lua_call
	movl	%ebx, (%esp)
	call	_lua_gettop
	movl	%ebx, (%esp)
	movl	-84(%ebp), %ecx
	xorl	%edx, %edx
	movl	%edx, 8(%esp)
	subl	%ecx, %eax
	movl	%eax, 4(%esp)
	call	_lua_call
	movl	%ebx, (%esp)
	call	_lua_close
	leal	-12(%ebp), %esp
	xorl	%eax, %eax
	popl	%ebx
	popl	%esi
	popl	%edi
	popl	%ebp
	ret
	.def	_lua_pushnumber;	.scl	3;	.type	32;	.endef
	.def	_lua_remove;	.scl	3;	.type	32;	.endef
	.def	_lua_getfield;	.scl	3;	.type	32;	.endef
	.def	_lua_call;	.scl	3;	.type	32;	.endef
	.def	_lua_gettop;	.scl	3;	.type	32;	.endef
	.def	_lua_close;	.scl	3;	.type	32;	.endef
	.def	_luaL_openlibs;	.scl	3;	.type	32;	.endef
	.def	_luaL_newstate;	.scl	3;	.type	32;	.endef

以下是 g++ 4.3 在“-fdump-tree-optimized”标志下(“test.cpp.126t.optimized”)的输出,显示了优化后的代码的 C 类表示

;; Function int main() (main)

Analyzing Edge Insertions.
int main() ()
{
  int D.5767;
  int pos;
  int D.5766;
  int pos;
  const struct getfield_ * this.27;
  const struct call_ * this.24;
  struct lua_State * L;
  struct global D.4728;
  struct global D.4729;
  const struct getfield_ D.4820;
  struct lnumber D.4830;
  const struct call_ D.4917;

<bb 2>:
  L = luaL_newstate ();
  luaL_openlibs (L);
  D.4830.v_ = 2.0e+0;
  D.4729.name_ = &"math"[0];
  D.4820.k_ = &"sqrt"[0];
  D.4820.t_ = (struct val *) &D.4729;
  D.4917.p1_ = (const struct val *) &D.4830;
  D.4917.f_ = (const struct val *) &D.4820;
  D.4728.name_ = &"print"[0];
  lua_gettop (L);
  lua_getfield (L, -10002, ((const struct global *) (struct val *) &D.4728)->name_);
  pos = lua_gettop (L);
  this.24 = (const struct call_ *) (const struct val *) &D.4917;
  lua_gettop (L);
  this.27 = (const struct getfield_ *) this.24->f_;
  lua_getfield (L, -10002, ((const struct global *) this.27->t_)->name_);
  lua_getfield (L, -1, this.27->k_);
  lua_remove (L, -2);
  pos = lua_gettop (L);
  lua_pushnumber (L, ((const struct lnumber *) this.24->p1_)->v_);
  D.5766 = lua_gettop (L);
  lua_call (L, D.5766 - pos, -1);
  D.5767 = lua_gettop (L);
  lua_call (L, D.5767 - pos, 0);
  lua_close (L);
  return 0;

注意:以上代码生成结果来自旧版本。实际上,在所有输入下生成最佳代码输出是困难的。可能会构造临时对象。

下载源代码

警告:此代码仍在开发中。可能存在错误和缺少的 API 功能,尽管它通过了初始测试套件。请在下面的评论部分发布任何问题。

状态

[2008-07-16]

我对这个项目有点沮丧。方法是合理的,但困难在于上面目标 #1 依赖于编译器对模板的有效扩展和内联。这种行为是实现相关的。我发现自己经常需要反汇编,检查 g++ 4.x 的 gimple 输出,检查链接器 .map 文件等,只是为了确保编译器没有生成不必要的模板实例化或未能内联临时对象。有时生成非常有效,特别是对于具有浅层 AST 的简单表达式,有时会出现代码膨胀,这取决于编译器(例如 g++ 3.x、g++ 4.x、MSVC++9)和编译器选项。在代码中调整 const 与 const & 以及 inline 与 forceinline 可以有所帮助,但结果似乎不像预期的那样一致。也许其他人想弄清楚什么对流行的编译器有效。此外,复杂的模板会增加构建时间并导致难以理解的编译器错误。所有这些似乎并没有简化事情,反而导致了生产力的下降。

所以...我开始了一个新项目:LuaToCee。我不再依赖可怕的 C++ 模板进行元编程,而是使用 Lua 进行元编程。这令人满意得多。它可能无法实现上面所有相同目标,但它有自己的用途。

Lua C API 并不那么糟糕。事实上,为了避免过度使用 C API,尽可能用 Lua 编写代码,将其编译成字节码,运行 BinToCee,并从 C 调用该函数,将剩余数据从 C 传递给它。还有一些技术可以避免使用 C API 时遇到的陷阱(例如堆栈损坏)。

作者

DavidManura

用户评论

...

另请参阅


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2009 年 10 月 31 日下午 7:37 GMT (差异)