Автоматическое связывание Lua с использованием C ++ - PullRequest
0 голосов
/ 29 сентября 2018

Я создаю простой 2D игровой движок, и он будет становиться все больше и больше, раскрывая все функции в Lua, будет невозможно: поэтому я пытаюсь немного автоматизировать процесс, Есть ли в любом случае, чтобы получить всеn аргументов (с разными типами) из стека сразу и вставьте их непосредственно в функцию C ++.Я уже автоматизировал функции проверки аргументов.тем не менее привязка функции немного сложнее

Например, у меня есть нормальный код для изменения позиции спрайта:

int LuaSprite::SetSpritePosition(lua_State* L)
{
    Drawable* sprt = (Drawable*)lua_touserdata(L, 1);
    int x = (int)lua_tonumber(L, 2);
    int y = (int)lua_tonumber(L, 3);
    sprt->setPosition(Vec2i(x, y));
    return 0;
}

Концепция, которую я хочу достичь, более менее похожа на это:

int LuaSprite::SetSpritePosition(lua_State* L)
{   
    LOAD_ARGS(Sprite*, int, int)
    GET_ARG(1)->setPosition(Vec2i(GET_ARGS_FROM(1)));
    LOAD_RETURN(true, x, y, 3);
    return 3;
}

Так

  • LOAD_ARGS получит из стека данного типа соответственно.
  • GET_ARG (i) получит аргумент по индексу i.
  • GET_ARGS_FROM (i) будет делать что-то вроде этого x, y, z, type

Я не запрашиваю такое же поведение, как это невозможно сделать, но что-то похожее, по крайней мереИ я уверен, что это не достижимо только при использовании «простого» C ++ и что мне нужны также некоторые «волшебные» макросы.Я не использую готовые «библиотеки» привязки Lua, потому что есть пользовательские структуры, а некоторые функции используют сложные структуры и классы. Я много искал и чувствую себя действительно потерянным.

Ответы [ 3 ]

0 голосов
/ 29 сентября 2018

В моей практике мне очень удобно следить за гибкостью Lua в отношении типов аргументов или другого расположения аргументов.Вы не ограничены строгими правилами C / C ++, у вас может быть один и тот же метод, выполняющий различную работу в зависимости от точных аргументов.Т.е. семантика вызова выражает некоторую концепцию более высокого уровня.

При выполнении таких гибких методов полностью автоматическое связывание в большинстве случаев бесполезно.Скажем, вы можете принимать различные объекты в качестве аргументов.Либо одно число, представляющее, например, яркость, или триплет rgb для указания точного цвета, либо даже строку, указывающую путь к текстурному изображению.Плюс может быть необязательный следующий аргумент, указывающий режим смешивания не по умолчанию.Вы не можете добавить к этому средство чтения фиксированных общих аргументов.

Так что вместо того, чтобы пытаться анализировать некоторые аргументы заранее с каким-либо шаблоном voodoo, проще иметь служебные функции, читая, что Vec2 / Vec2i / Vec3 / ..для вас, взяв только указатель на состояние Lua, индекс для стека, с которого нужно начать, и возвращая флаг результата, или число фактически прочитанных значений, чтобы отследить синтаксический анализ аргументов. Конструкция

GET_ARG(1) настолько универсальна,что в моем случае это просто метод в базовом классе для всех объектов, которые должны быть доступны из Lua.Эта база является шаблоном, поэтому она знает тип объекта, автоматически приводя пользовательские данные Lua по индексу 1 (это всегда 1) к точному собственному типу, ничего не записывая.

Возвращать значения также слишком просто, чтобы жаловатьсяabout (один логический или пара целых) или слишком сложный, чтобы обрабатываться автоматическими привязками.Скажем, вы возвращаете какой-то большой объект, требующий некоторой конфигурации / обновления / регистрации после его создания, а затем дополнительные значения, некоторые из которых являются необязательными или зависят от типа ранее возвращенных данных.

Итак, ваш пример вмой код будет выглядеть примерно так:

int LuaSprite::SetSpritePosition(lua_State* L)
{   
    get_object(L)->setPosition(read_lua_vec2i(L, 2));
    return 0;
}
0 голосов
/ 03 января 2019

попробуйте Luaaa (https://github.com/gengyong/luaaa),, что более легко, внедрить только в один заголовочный файл.

Он полностью соответствует вашим требованиям.

примеры кодов:

    // include luaaa file
    #include "luaaa.hpp"
    using namespace luaaa;  

    // Your exists class
    class Cat
    {
    public:
        Cat();
        virtual ~Cat();
    public:
        void setName(const std::string&);
        const std::string& getName() const;
        void eat(const std::list<std::string>& foods);
        //...
    private:
        //...
    };

    lua_State * state; // create and init lua

    // To export it:
    LuaClass<Cat>(state, "AwesomeCat")
    .ctor<std::string>();
    .fun("setName", &Cat::setName);
    .fun("getName", &Cat::getName);
    .fun("eat", &Cat::eat);
    .def("tag", "Cat");

Вы можете найти более подробную информацию здесь (https://github.com/gengyong/luaaa).

Я автор этой библиотеки, я настоятельно рекомендую вам попробовать ее, если у вас есть какие-либо вопросы, пожалуйста,свободно спрашивать.

0 голосов
/ 29 сентября 2018

Я обнаружил, Luabridge очень удобно при выполнении этих задач.Это библиотека только для заголовков, которая работает со старым кодом C ++ (до c ++ 11 и после).

Вы не программируете непосредственно для стека Lua, вместо этого вы включаете обычный класс / структуру в определение связывания.

Код привязки выглядит примерно так:

 getGlobalNamespace(L).beginNamespace("GameFrameworkName")

.beginClass<RectF>("Rect")
.addStaticFunction("__call", &RectF_Ctor) // Constructor from Table!
.addData<float>("x", &RectF::x)
.addData<float>("y", &RectF::y)
.addData<float>("width", &RectF::width)
.addData<float>("height", &RectF::height)
.addFunction("Union", &RectF::Union)
.addFunction("Intersects", (bool (RectF::*)(const RectF&)) &RectF::Intersects)
.addFunction("Intersection", &RectF::Intersection)
.addFunction("Contains", (bool (RectF::*)(const PointF&)) &RectF::Contains)
.addFunction("Offset", (void (RectF::*)(const PointF&)) &RectF::Offset)
.addFunction("Inflate", (void (RectF::*)(float, float)) &RectF::Inflate)
.addFunction("__tostring", &RectF_ToString)
.addFunction("__eq",  (bool   (RectF::*)(const RectF&)) &RectF::operator==)
.addFunction("Copy", &RectF_Copy)
.endClass()


.beginClass<PointF>("Point")
.addStaticFunction("__call", &PointF_Ctor) // Constructor from Table!
.addData<float>("x", &PointF::x)
.addData<float>("y", &PointF::y)
.addFunction("__tostring", &PointF_ToString)
.addFunction("__add", (PointF (PointF::*)(const PointF&)) &PointF::operator+ )
.addFunction("__sub", &PointF_SubtractOperator )
.addFunction("__mul", &PointF_MultiplyOperator )
.addFunction("__div", &PointF_DivideOperator )
.addFunction("__eq",  (bool   (PointF::*)(const PointF&)) &PointF::operator==)
.addFunction("__unm", &PointF_Unm)
.addFunction("Copy",  &PointF_Copy)
.endClass()

.endNamespace();

Для игровых объектов я обычно управляю временем жизни в C ++ и просто передаю указатели Lua.Luabind позволяет вам переопределить Lua CTor (__call) и ~ Tor (__gc).Вот мои копии:

template<typename T>
std::string DoNotCreate()
{
    DBG_ASSERT(false); // Do not try to create a new instance of this type
    return StrFormat("ERROR: Lua cannot create an instance of %s.", typeid(T).name());
}


///////////////////////////////////////
// \brief       Do Not Allow Lua or Luabridge to delete us
void DoNotGarbageCollect(void*) {}

И их использование:

.deriveClass<Sprite, DisplayObject>("Sprite")
  .addStaticFunction("__call", &DoNotCreate<Sprite>)
  .addFunction("__gc", (void (*) (Sprite*)) &DoNotGarbageCollect)

Для вызова Lua Code из C ++ вы используете LuaRefs, которые (IIRC) в основном являются вариантами, которые могут быть любым типом Lua.


Если вам интересно, я нашел Zerobrane хорошим отладчиком, вам просто нужно добавить библиотеки lua для сокетов

...