#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), но он должен работать и с обычной версией.