Пользовательский конструктор в Luabind

#lua #coroutine #luabind

#lua #сопрограмма #luabind

Вопрос:

Я использую Luabind для привязки C API к Lua. У меня есть некоторые объекты, которые не могут быть созданы напрямую, а скорее должны быть созданы в другом потоке. В настоящее время я занимаюсь этим, определяя вызываемый «статический» элемент create , который выдает результат до тех пор, пока объект не будет создан:

 luabind::class_<Foo>("Foo")
  .scope
  [
    luabind::def("create", amp;someCreateMethod, luabind::yield)
  ]
  

Это работает, но имеет недостаток в усложнении клиентского API. Для этих классов вы не можете создать их обычным способом (например, local f = Foo() ), но вместо этого вам нужно сделать local f = Foo.create() .

Возможно ли определить конструктор Luabind, который на самом деле не вызывает конструктор C , а вместо этого другую функцию, которая возвращает сконструированный объект (и может выдавать результат в то же время)? Я пытался определить привязки для __init и __call (последнее в a scope , чтобы определить его в классе, а не в его экземплярах), но ни один из подходов не увенчался успехом.

Ответ №1:

Конструкторы в Luabind должны быть реальными конструкторами класса C . Так что вам просто придется иметь дело с небольшой странностью API.

Если все, что вас интересует, это возможность использовать Foo в качестве метода конструктора, то вы можете сделать это. Зарегистрируйте свой класс C Foo как FooLua в Lua. Затем зарегистрируйте это someCreateMethod не как член FooLua , а просто как вызываемую функцию, свободную от Lua Foo . Таким образом, с точки зрения пользователя, Foo является конструктором для класса Lua Foo .

Теперь это ограничит вашу способность предоставлять Foo другие статические свойства, такие как члены и так далее. Но вы могли бы добиться этого, используя какое-нибудь прямое кодирование Lua API. Вы можете создать пустую таблицу Foo и создать для нее метатаблицу, которая пересылает __index и __newindex вызывает FooLua . Аналогично, вы можете переопределить эту метатаблицу, __call чтобы переслать конструкцию в Foo.create .

Комментарии:

1. И нет никакого способа, ни на стороне C , ни на стороне Lua, «подделать» его с помощью __call оператора?

2. @Xtapolapocetl: Luabind хранит типы C как пользовательские данные, поэтому код Lua не может повлиять на его метатаблицы. Вы могли бы использовать метатаблицы Luabind из C , но на самом деле, это такая незначительная проблема, что ею пренебрегают. Действительно, если синтаксис действительно вас беспокоит, вы могли бы просто вызвать объект FooInternal с помощью Luabind и просто создать функцию, Foo которая вызывает FooInternal.create функцию. Пользователь может думать, что он использует класс с именем Foo .

3. @Xtapolapocetl: В дополнение к комментарию Nicol, если вы используете только Foo для создания объектов (т. Е. никаких других методов области видимости), вы могли бы даже просто сделать Foo = Foo.create .

Ответ №2:

Хотя luabind не предоставляет простого способа определения пользовательских конструкторов, на самом деле это возможно с небольшим взломом:

 template<typename T,auto TCnstrct,typename ...TArgs>
static void custom_constructor(luabind::argument const amp;self_, TArgs... args)
{
    using holder_type = luabind::detail::value_holder<T>;
    luabind::detail::object_rep* self = luabind::touserdata<luabind::detail::object_rep>(self_);

    void* storage = self->allocate(sizeof(holder_type));
    self->set_instance(new (storage) holder_type(nullptr,TCnstrct(std::forward<TArgs>(args)...)));
}

template<typename T,auto TCnstrct,typename ...TArgs>
    static void define_custom_constructor(lua_State *l)
{
    auto *registry = luabind::detail::class_registry::get_registry(l);
    auto *crep = registry->find_class(typeid(T));
    assert(crep);
    auto fn = luabind::make_function(l,amp;custom_constructor<T,TCnstrct,TArgs...>);
    crep->get_table(l);
    auto o = luabind::object{luabind::from_stack(l,-1)};
    luabind::detail::add_overload(o,"__init",fn);
    lua_pop(l,1);
}
  

Это позволит вам использовать любую свободную функцию в качестве конструктора после определения класса:

 static void define_vector_class(lua_State *l)
{
    auto modMath = luabind::module_(l,"math");
    struct Vector
    {
        Vector()=defau<
        float x,y,z;
    };
    auto defVec = luabind::class_<Vector>("Vector");
    modMath[defVec];

    // Define custom constructor taking three float arguments
    define_custom_constructor<Vector,[](float x,float y,float z) -> Vector {
        Vector v;
        v.x = x;
        v.y = y;
        v.z = z;
        return v;
    },float,float,float>(l); // Constructor parameter types have to be specified in template parameter list as well
}
  

Тестировался с деблокированной версией luabind (https://github.com/decimad/luabind-deboostified), но он должен работать и с обычной версией.