我已经在shell世界中被宠坏了,我可以这样做:
./lua <<EOF
> x="hello world"
> print (x)
> EOF
hello world
现在我正在尝试在C应用程序中包含一个Lua脚本,我期望它会随着时间的推移而增长。我从一个简单的开始:
const char *lua_script="x=\"hello world\"\n"
"print(x)\n";
luaL_loadstring(L, lua_script);
lua_pcall(L, 0, 0, 0);
但这有几个缺点。首先,我必须逃避换行和引用。但是现在我在使用gcc进行编译时遇到了string length ‘1234’ is greater than the length ‘509’ ISO C90 compilers are required to support
警告,我希望这个程序不仅可以自包含,而且可以移植到其他编译器中。
在C程序中包含大型Lua脚本的最佳方法是什么,而不是作为单独的文件提供给最终用户?理想情况下,我想将脚本移动到单独的* .lua文件中以简化测试和更改控制,并将该文件以某种方式编译到可执行文件中。
答案 0 :(得分:11)
在支持binutils的系统上,您还可以使用'ld -r'将Lua文件“编译”为.o,将.o链接到共享对象,然后将您的应用程序链接到共享库。在运行时,您可以在lua文本中使用dlym(RTLD_DEFAULT,...),然后可以根据需要对其进行评估。
从some_stuff.lua创建some_stuff.o:
ld -s -r -o some_stuff.o -b binary some_stuff.lua
objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents some_stuff.o some_stuff.o
这将为您提供一个带有符号的目标文件,这些符号用于界定您的lua数据的开头,结尾和大小。据我所知,这些符号由文件名中的ld确定。您无法控制名称,但它们始终是派生的。你会得到类似的东西:
$ nm some_stuff.o
000000000000891d R _binary_some_stuff_lua_end
000000000000891d A _binary_some_stuff_lua_size
0000000000000000 R _binary_some_stuff_lua_start
现在将some_stuff.o链接到与任何其他目标文件一样的共享对象。然后,在你的应用程序中,编写一个名为“some_stuff_lua”的函数,并执行相应的dlsym魔术。类似下面的C ++,它假设你有一个名为SomeLuaStateWrapper的lua_State包装器:
void SomeLuaStateWrapper::loadEmbedded(const std::string& embeddingName)
{
const std::string prefix = "_binary_";
const std::string data_start = prefix + embeddingName + "_start";
const std::string data_end = prefix + embeddingName + "_end";
const char* const data_start_addr = reinterpret_cast<const char*>(
dlsym(RTLD_DEFAULT, data_start.c_str()));
const char* const data_end_addr = reinterpret_cast<const char*>(
dlsym(RTLD_DEFAULT, data_end.c_str()));
THROW_ASSERT(
data_start_addr && data_end_addr,
"Couldn't obtain addresses for start/end symbols " <<
data_start << " and " << data_end << " for embedding " << embeddingName);
const ptrdiff_t delta = data_end_addr - data_start_addr;
THROW_ASSERT(
delta > 0,
"Non-positive offset between lua start/end symbols " <<
data_start << " and " << data_end << " for embedding " << embeddingName);
// NOTE: You should also load the size and verify it matches.
static const ssize_t kMaxLuaEmbeddingSize = 16 * 1024 * 1024;
THROW_ASSERT(
delta <= kMaxLuaEmbeddingSize,
"Embedded lua chunk exceeds upper bound of " << kMaxLuaEmbeddingSize << " bytes");
namespace io = boost::iostreams;
io::stream_buffer<io::array_source> buf(data_start_addr, data_end_addr);
std::istream stream(&buf);
// Call the code that knows how to feed a
// std::istream to lua_load with the current lua_State.
// If you need details on how to do that, leave a comment
// and I'll post additional details.
load(stream, embeddingName.c_str());
}
所以,现在在您的应用程序中,假设您已链接或包含some_stuff.o的库,您可以说:
SomeLuaStateWrapper wrapper;
wrapper.loadEmbedded("some_stuff_lua");
并且some_stuff.lua的原始内容将在'wrapper'的上下文中被lua_load'。
此外,如果您希望能够使用'require'从Lua加载包含some_stuff.lua的共享库,只需在其他一些C / C ++文件中提供包含some_stuff.oa luaopen入口点的相同库:
extern "C" {
int luaopen_some_stuff(lua_State* L)
{
SomeLuaStateWrapper wrapper(L);
wrapper.loadEmbedded("some_stuff_lua");
return 1;
}
} // extern "C"
您的嵌入式Lua现在也可以通过require获得。这对luabind特别有效。
使用SCons,教会构建系统相当容易,当它在SharedLibrary的sources部分中看到一个.lua文件时,它应该使用上面的ld / objcopy步骤“编译”该文件:
# NOTE: The 'cd'ing is annoying, but unavoidable, since
# ld in '-b binary' mode uses the name of the input file to
# set the symbol names, and if there is path info on the
# filename that ends up as part of the symbol name, which is
# no good. So we have to cd into the source directory so we
# can use the unqualified name of the source file. We need to
# abspath $TARGET since it might be a relative path, which
# would be invalid after the cd.
env['SHDATAOBJCOM'] = 'cd $$(dirname $SOURCE) && ld -s -r -o $TARGET.abspath -b binary $$(basename
$SOURCE)'
env['SHDATAOBJROCOM'] = 'objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents $
TARGET $TARGET'
env['BUILDERS']['SharedLibrary'].add_src_builder(
SCons.Script.Builder(
action = [
SCons.Action.Action(
"$SHDATAOBJCOM",
"$SHDATAOBJCOMSTR"
),
SCons.Action.Action(
"$SHDATAOBJROCOM",
"$SHDATAOBJROCOMSTR"
),
],
suffix = '$SHOBJSUFFIX',
src_suffix='.lua',
emitter = SCons.Defaults.SharedObjectEmitter))
我确信可以使用像CMake这样的其他现代构建系统来做这样的事情。
这种技术当然不仅限于Lua,而是可以用于嵌入二进制文件中的任何资源。
答案 1 :(得分:4)
一个非常便宜,但不那么容易改变的方法是使用像bin2c这样的东西从选定的lua文件(或其编译的字节码,它更快更小)生成一个标题,然后你可以将它传递给lua执行。
你也可以尝试将它作为资源嵌入,但我不知道它是如何在visual studio / windows之外工作的。
根据您的要求,您甚至可以找到exeLua使用。