+1 за очень информативное решение. Я оставлю это в своем заднем кармане на случай, если мне понадобится что-то, не относящееся к Lua, и где переносимость менее важна.

избалован в мире оболочки, где я могу сделать:

./lua <<EOF
> x="hello world"
> print (x)
> EOF
hello world

Теперь я пытаюсь включить скрипт Lua в приложение на C, которое, как я ожидаю, будет расти со временем. Я начал с простого:

const char *lua_script="x=\"hello world\"\n"
  "print(x)\n";
luaL_loadstring(L, lua_script);
lua_pcall(L, 0, 0, 0);

Но это имеет несколько недостатков. Прежде всего, я должен избежать перевода строки и цитат. Но сейчас я бьюstring length ‘1234’ is greater than the length ‘509’ ISO C90 compilers are required to support предупреждение при компиляции с gcc, и я хотел бы сохранить эту программу не только автономной, но и переносимой на другие компиляторы.

Каков наилучший способ включить большой скрипт Lua в программу на C, а не отправлять конечному пользователю в виде отдельного файла? В идеале я хотел бы переместить сценарий в отдельный файл * .lua, чтобы упростить тестирование и контроль изменений, и чтобы этот файл каким-то образом был скомпилирован в исполняемый файл.

 Jonathan Leffler30 мая 2011 г., 16:36
@B Митч: unassume - они разные, очень разные (хотя во многих случаях общий результат ссылки на них одинаков).char * выделяет место для указателя и для инициализатора строки (и этот указатель может быть изменен);char [] выделяет место для инициализатора строки, и есть адрес, который можно передать функциям, но который нельзя изменить и который не занимает места.
 BMitch30 мая 2011 г., 13:26
@ Джонатан Леффлер: я всегда предполагал, чтоchar *x а такжеchar x[] мы одинаковы?
 Jonathan Leffler30 мая 2011 г., 09:07
Измените определение сценария на:const char lua_script[] = "..."; сохранить ненужный указатель.
 R..30 мая 2011 г., 05:26
Я бы отключил это предупреждение или явно сообщил бы gcc, что вы хотите написать на современном (C99) C-std=c99, C99 требует, чтобы компиляторы поддерживали строковые литералы длиной до 4095 символов. На практике, однако, любой полезный компилятор для не встроенной среды не будет иметь никаких ограничений на длину строки, поэтому предупреждение следует просто отключить или проигнорировать.
 R..30 мая 2011 г., 05:28
Что касается экранирования, перевода строки и т. Д. Я бы написал вспомогательную программу / скрипт для преобразования текстового файла в допустимую строку C и включения его в процесс сборки.

Ответы на вопрос(2)

которые поддерживают binutils, вы также можете «скомпилировать» файл Lua в .o с помощью «ld -r», связать .o с общим объектом, а затем связать свое приложение с общей библиотекой. Во время выполнения вы dlsym (RTLD_DEFAULT, ...) в тексте lua и можете затем оценить его, как вам нравится.

Чтобы создать some_stuff.o из some_stuff.lua:

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 ++, который предполагает, что у вас есть оболочка вокруг lua_State с именем SomeLuaStateWrapper:

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 будет lua_load'ed в контексте 'wrapper'.

Если, кроме того, вы хотите, чтобы совместно используемая библиотека, содержащая some_stuff.lua, могла быть загружена из Lua с помощью 'require', просто предоставьте той же библиотеке, которая содержит some_stuff.o, точку входа luaopen в некотором другом файле C / C ++:

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 довольно легко объяснить системе сборки, что, когда она видит файл .lua в разделе источников SharedLibrary, она должна «скомпилировать» файл с помощью шагов 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, но может использоваться для встраивания практически любого ресурса в двоичный файл.

 BMitch31 мая 2011 г., 04:29
+1 за очень информативное решение. Я оставлю это в своем заднем кармане на случай, если мне понадобится что-то, не относящееся к Lua, и где переносимость менее важна.
Решение Вопроса

но не такой простой способ изменить это использовать что-то вроде bin2c для генерации заголовка из выбранного файла lua (или его скомпилированного байт-кода, который быстрее и меньше), затем вы можете передать это lua для выполнения.

Вы также можете попробовать встроить его в качестве ресурса, но я понятия не имею, как это работает за пределами Visual Studio / Windows.

в зависимости от того, что вы хотите сделать, вы можете даже найтиexeLua использования.

 BMitch30 мая 2011 г., 13:24
Начиная с 5.1, я не видел bin2c в дистрибутиве, но я искал немного сложнее и нашелlua-users.org/wiki/BinToCee Похоже, я могу изменить это для моих целей. Спасибо Некролис!

Ваш ответ на вопрос