如何使用静态c ++库扩展Lua?

时间:2012-08-21 15:23:38

标签: c++ lua

我有一个使用Lua 5.2.1的Visual Studio 2008 C ++ 03应用程序。我想用一个名为" foo"的模块扩展Lua,但是当我在Lua脚本中调用require("foo")时,我收到错误:

foo_test.lua:1: module 'foo' not found:
    no field package.preload['process']
    no file '!\lua\process.lua'
    no file '!\lua\process\init.lua'
    no file '!\process.lua'
    no file '!\process\

我的Lua脚本:

foo.bar()

我的lua_foo.h文件:

#include <lua.h>
extern "C" int luaopen_foo( lua_State* L );

我的lua_foo.cpp文件:

#include "lua_foo.h"
#include <lua.hpp>

static int l_bar( lua_State *L )
{
    puts( "in bar()" );
    return 1;
}

int luaopen_foo( lua_State *L ) 
{
    static const luaL_Reg foo[] = {
        { "bar", l_bar },
        { NULL, NULL }
    };

    luaL_newlib( L, foo );
    return 1;
}

这些被编译到静态库&#34; lua_foo.lib&#34;它与我的主要Lua可执行文件静态链接。

有人可以帮我理解我哪里出错吗?谢谢。我宁愿避免使用c ++包装器(现在),我不想将这个库打包为主Lua引擎的单独DLL。


修改

问题出在lua引擎代码中。我根据@NicolBolas的建议添加了luaL_requiref

lua_State* L = luaL_newstate();
if( NULL != L )
{
    luaL_openlibs( L );
    luaL_requiref( token.get(), "foo", luaopen_foo, 1 );
    luaL_dofile( L, "foo_test.lua" );
    lua_close( L );
}

2 个答案:

答案 0 :(得分:6)

了解require机器的工作原理以及代码没有的原因非常重要。

require旨在查找文件系统中的Lua脚本和 DLL 。静态库不是DLL;实际上,就C / C ++而言,一旦完成链接,静态库与直接将.c / .cpp文件编译到应用程序中没有什么不同。

require找到具有适当名称的DLL时,它会加载它并尝试查找名为luaopen_<modname>的函数,其中<modname>是模块的名称。如果是这样,它将执行此函数并将其返回的值存储在已加载模块的内部数据库中。

为模块调用require将返回此函数返回的内容;如果模块已经加载,则从数据库中提取返回值并直接返回。

只需致电luaopen_foo就不会执行任何此类。实际上,简单地调用这个函数是个坏主意;它是一个Lua函数,需要被称为作为Lua函数(即:你需要用lua_pushcfunction将它推到Lua堆栈上并用lua_call调用它等等等)。

如果要创建本地模块(一个不在Lua脚本或DLL中,但从代码中公开),那么您需要使用Lua工具来执行此操作。具体来说,请使用luaL_requiref

luaL_requiref(L, "foo", luaopen_foo, 0);

请拨打此电话,而不是直接拨打luaopen_foo。这将自动注册luaopen_foo的返回值和require的已加载模块的内部数据库。因此,对require "foo"的后续调用将返回此表。

还有一件事:do是Lua中的关键字;您不应该为Lua表键名使用关键字。你可以,但你总是要引用它们(即:你的脚本必须foo["do"](...)来调用它。)

答案 1 :(得分:2)

  • luaopen_foo创建一个包含一个函数的表,但它不会以任何方式将它暴露给Lua。如果要访问脚本,则需要将其分配给脚本可以访问的内容。您可以使用包机制执行此操作,或者只将其分配给全局(这是Lua的内置库所执行的操作)。
  • 您有一个名为do的字段,如果您想使用foo.do语法,则会出现问题,因为do是关键字。
  • Lua函数的返回值告诉Lua你在堆栈上剩下多少个值。您的l_do函数位于其返回值。
  • luaopen_foo的情况下,由于您直接调用它并忽略它的返回值,因此根本不需要它返回任何内容。

将您的代码更改为:

static int l_bar( lua_State *L )
{
   puts("l_bar called.");
   return 0;
}

void luaopen_foo( lua_State *L ) 
{
   static const struct luaL_Reg foo[] = {
      { "bar", l_bar },
      { NULL, NULL }
   };
   luaL_newlib( L, foo );   // create table containing `bar`
   lua_setglobal(L, "foo"); // assign that table to global `foo`
}

将脚本更改为:

foo.bar()