2012-05-03 5 views
9

У меня есть API (определенная библиотека GUI), которая много зависит от std::shared_ptr, т. Е. Они часто используются в качестве параметров функции и хранятся в других объектах. Например, виджеты контейнеров, такие как сплиттеры и коробки, будут хранить дочерние виджеты в shared_ptr. Теперь я хотел бы сопоставить этот API с Lua через luabind. В идеальном мире Luabind создаст новые объекты в shared_ptrs и позволит мне передать их непосредственно функциям, использующим параметры shared_ptr. Это похоже на работу для отдельных классов, например .:Использование luabind и std :: shared_ptr с наследованием

luabind::class_<Button, std::shared_ptr<Button>>("Button") 

В то время как я объявляю это так, я могу выставить и функцию использовать как void foo(std::shared_ptr<Button> const&).

Теперь в руководстве luabind упоминается, что для использования иерархии классов мне придется использовать один и тот же шаблон-экземпляр shared_ptr для всех классов в иерархии, например.

luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>>("BaseWidget"), 
luabind::class_<Button, BaseWidget, std::shared_ptr<BaseWidget>>("Button") 

Теперь я больше не могу называть foo - она ​​не сможет найти функцию из Lua. Могу ли я каким-то образом получить luabind, чтобы все еще поддерживать пропущенные кнопки в shared_ptr? Кроме того, я хотел бы знать, почему luabind требует, чтобы вы использовали один и тот же умный указатель для всех классов в иерархии, а не просто конвертировались в указатели базового класса.

+0

не следует ли, что вторая линия использование 'станд :: shared_ptr ' 'не станд :: shared_ptr

+0

Да - спасибо! Просто тип tho! – ltjax

ответ

7

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

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button") 

Например:

class BaseWidget 
{ 
public: 
    static void Bind2Lua(lua_State* l) 
    { 
     luabind::module(l) 
     [ 
      luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>> ("BaseWidget") 
      .def(luabind::constructor<>()) 
     ]; 
    } 

    virtual ~BaseWidget() 
    { 

    } 
}; 

class Button : public BaseWidget 
{ 
public: 
    static void Bind2Lua(lua_State* l) 
    { 
     luabind::module(l) 
     [ 
      luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button") 
      .def(luabind::constructor<>()) 
      .def("Click", &Button::Click) 
     ]; 
    } 

    void Click() 
    { 
     std::cout << "Button::Click" << std::endl; 
    } 
}; 

Теперь вы можете использовать его с shared_ptr:

class Action 
{ 
public: 
    void DoClick(const std::shared_ptr<Button>& b) 
    { 
     // perform click action 
     b->Click(); 
    } 

    static void Bind2Lua(lua_State* l) 
    { 
     luabind::module(l) 
     [ 
      luabind::class_<Action> ("Action") 
      .def(luabind::constructor<>()) 
      .def("DoClick", &Action::DoClick) 
     ]; 
    } 
}; 

В луа:

b = Button() 

a = Action() 

a:DoClick(b) 

Причина в том, что luabind использует систему идентификаторов типов с целыми числами (точнее std :: size_t, как определено в inheritance.hpp).
Вы можете получить тип-идентификатор любого зарегистрированного типа с функцией:

luabind::detail::static_class_id<T>(nullptr); 

где T является зарегистрированным классом.
В моей демонстрационной программе они:

  • BaseWidget = 3
  • станд :: shared_ptr < BaseWidget> = 6
  • Кнопка = 4
  • станд :: shared_ptr < Button> = 7
  • Действие = 5

Поэтому, когда вы вызываете DoClick из lua, он будет называть элемент get t он шаблонный класс pointer_holder в instance_holder.hpp:

std::pair<void*, int> get(class_id target) const 
{ 
    if (target == registered_class<P>::id) 
     return std::pair<void*, int>(&this->p, 0); 

    void* naked_ptr = const_cast<void*>(static_cast<void const*>(
     weak ? weak : get_pointer(p))); 

    if (!naked_ptr) 
     return std::pair<void*, int>((void*)0, 0); 

    return get_class()->casts().cast(
     naked_ptr 
     , static_class_id(false ? get_pointer(p) : 0) 
     , target 
     , dynamic_id 
     , dynamic_ptr 
    ); 
} 

Как вы можете видеть, если целевой класс не то же самое, как один зарегистрированный, он будет пытаться сделать бросок.
Здесь все становится интересным. Если вы объявили класс Button, как

luabind::class_<Button, BaseWidget,std::shared_ptr<BaseWidget>>("Button") 

то экземпляр будет проходить как shared_ptr к BaseWidget, таким образом, функция преобразования будет пытаться отлиты из BaseWidget (3) к StD :: shared_ptr < Button> (7), и это терпит неудачу. Он может работать, если luabind поддерживает преобразование базы в производную, чего, по-видимому, нет.

Если же вы объявили класс Button как

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button") 

то экземпляр будет проходить в качестве shared_ptr к кнопке, а затем целевой идентификатор будет соответствовать зарегистрированному типу. Функция get будет входить в первую очередь, никогда не беспокоясь о приведении.

Вы также можете найти автономную программу, которую я использовал here at pastebin.

А вот список интересных точек останова вы можете установить, чтобы увидеть, что происходит (Luabind версия 900):

  • линии 94 в instance_holder.hpp (первая линия pointer_holder :: получить)
  • строка 143 в instance.cpp (первой линии cast_graph :: осущ :: гипсе)
+0

Я действительно добавил эти перегрузки для get_pointer. Он определенно передает smart_pointer (я могу сказать, потому что подсчет ссылок прав, если я вызываю эти функции несколько раз) – ltjax

+0

Кроме того, передача необработанного указателя еще более неуклюжаема, так как я хочу хранить указатели, а не просто выполнять на них функции. Для этой работы я должен был бы изменить иерархию объектов GUI, чтобы получить от enable_shared_from_this. – ltjax

+0

Но с вашим текущим решением вы игнорируете, что руководство luabind обязуется использовать тип shared_ptr базового класса: см. Http://www.rasterbar.com/products/luabind/docs.html#smart-pointers, последний абзац. Если это «просто работает», я хотел бы знать, почему ... – ltjax

Смежные вопросы