5

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

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

这些被编译到一个静态库“lua_foo.lib”中,该库静态链接到我的主要 Lua 可执行文件。

任何人都可以帮助我了解我要去哪里错了吗?谢谢。我宁愿避免使用 c++ 包装器(目前),并且我不想将此库作为与主 Lua 引擎分开的 DLL 打包。


编辑

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

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

2 回答 2

12

了解require机器是如何工作的以及为什么你的代码不工作是很重要的。

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

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

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

简单地调用luaopen_foo不会做任何这些。事实上,简单地调用这个函数是个坏主意。它是一个 Lua 函数,需要作为 Lua 函数调用(即:您需要将其推入 Lua 堆栈lua_pushcfunction并调用它lua_call等等)。

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

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

调用它而不是luaopen_foo直接调用。这将自动注册来自加载模块的内部数据库的返回luaopen_foorequire。因此,后续调用require "foo"将返回此表。

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

于 2012-08-21T17:16:09.350 回答
1
  • 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()
于 2012-08-21T16:43:46.323 回答