Можно ли вызвать loadstring для строки байт-кода lua, которая содержит ссылку на функцию C?

Мы используем игровой движок Love2d Lua, который предоставляет графический API для Lua. Мы пытаемся сериализовать гигантскую хеш-таблицу, содержащую все данные сохранения для игрового мира. Этот хэш включает в себя некоторые функции, и некоторые из этих функций вызывают функции Love2d C.

Чтобы сериализовать функции в хеше, мы используем string.dump и загружаем их обратно с помощью loadstring. Это хорошо работает для чистых функций Lua, но когда мы пытаемся сериализовать, а затем загрузить обратно в функцию, которая вызывает обернутую функцию C, например, в API Love2d, loadstring возвращает nil.

Рассмотрим следующую простую программу, которая рисует «привет, мир» на экране с помощью графического движка Love2d:

function love.load()
    draw = function()
        love.graphics.print('hello, world', 10, 10)
    end
end
function love.draw()
    draw()
end

Мы хотели бы иметь возможность сделать это:

function love.load()
    draw_before_serialize = function()
        love.graphics.print('hello, world', 10, 10)
    end

    out = io.open("serialized.lua", "wb")
    out:write('draw = load([[' .. string.dump(draw_before_serialize) .. ']])')
    out:close()

    require "serialized"
end
function love.draw()
    draw()
end

Делая это, мы записываем в Lua-файл на диске, который содержит смесь некомпилированного байт-кода Lua и Lua, которая выглядит примерно так:

draw = load([[^[LJ^A^@      
       @main.lua2^@^@^B^@^B^@^D^E^B^B4^@^@^@%^A^A^@>^@^B^AG^@^A^@^Qhello, world 
       print^A^A^A^B^@^@]])

Этот метод отлично работает с функциями Lua, которые не вызывают модули C. Мы думаем, что проблема именно в этом, потому что этот пример работает:

function love.load()
    draw_before_serialize = function()
        print('hello, world')
    end

    out = io.open("serialized.lua", "wb")
    out:write('draw = load([[' .. string.dump(draw_before_serialize) .. ']])')
    out:close()

    require "serialized"
end
function love.draw()
    draw()
end

Вместо вызова графического метода Love2d он выводит на консоль.

После дополнительного тестирования мы были сбиты с толку, обнаружив, что этот пример действительно работает:

function love.load()
    draw_before_serialize = function()
        love.graphics.print('hello, world', 10, 10)
    end

    draw = load(string.dump(draw_before_serialize))
end
function love.draw()
    draw()
end

Здесь мы на самом деле не записываем функцию на диск, а вместо этого просто выгружаем ее, а затем сразу же загружаем обратно. Мы подумали, что, возможно, виновата не запись данных с установленным флагом режима двоичной записи ( "wb"), но, поскольку мы работаем в Linux, этот флаг не действует.

Есть идеи?

7
задан davidscolgan 2 June 2012 в 21:53
поделиться