2012-05-03 16 views
9

Tengo una API (una biblioteca de GUI específica) que se basa mucho en std::shared_ptr, es decir, a menudo se usan como parámetros de función y se almacenan en otros objetos. Por ejemplo, los widgets de contenedores, como los divisores y las cajas, almacenarán sus widgets secundarios en shared_ptr s. Ahora me gustaría asignar esta API a Lua a través de luabind. En un mundo ideal, Luabind crearía nuevos objetos en shared_ptrs y me permitiría pasarlos directamente a las funciones que toman los parámetros shared_ptr. Esto parece funcionar para las clases individuales, por ejemplo .:Uso de luabind y std :: shared_ptr con la herencia

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

Mientras lo declaro así, yo puedo exponer y utilizar las funciones como void foo(std::shared_ptr<Button> const&).

Ahora el manual de luabind menciona que para usar una jerarquía de clases, tendría que usar la misma instancia de plantilla shared_ptr para todas las clases en la jerarquía, p.

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

Ahora ya no puedo llamar foo - no será capaz de encontrar la función de Lua. ¿De alguna manera puedo conseguir que luabind siga apoyando los botones para pasar en shared_ptr s? Además, me gustaría saber por qué luabind exige que utilice el mismo puntero inteligente para todas las clases en la jerarquía en lugar de simplemente convertirlos en punteros de clase base.

+0

en caso de no uso esa segunda línea 'std :: shared_ptr ' 'no std :: shared_ptr

+0

Sí, ¡gracias! ¡Solo un tipo si! – ltjax

Respuesta

7

Creo que para que eso funcione hay que unir su clase derivada de esta manera:

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

Por ejemplo:

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; 
    } 
}; 

Ahora usted puede utilizarlo con 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) 
     ]; 
    } 
}; 

En lua:

b = Button() 

a = Action() 

a:DoClick(b) 

La razón es que luabind utiliza el sistema de identificación tipo con números enteros (más precisamente std :: size_t como se define en inheritance.hpp).
Puede obtener el Tipo-id de cualquier tipo certificada con la función:

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

Donde T es la clase registrada.
En mi programa de demostración son:

  • BaseWidget = 3
  • std :: shared_ptr < BaseWidget> = 6
  • botón = 4
  • std :: shared_ptr < Button> = 7
  • Acción = 5

Así que cuando llame a DoClick desde lua, llamará al miembro get de t que pointer_holder clase de plantilla en 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 
    ); 
} 

Como se puede ver, si la clase de destino no es el mismo que el registrado, se trata de hacer un reparto.
Aquí es donde las cosas se ponen interesantes. Si usted declaró la clase Button como

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

continuación, la instancia se llevará a cabo como un shared_ptr a BaseWidget, por tanto, la función de conversión tratará de echar de BaseWidget (3) std :: shared_ptr < Botón> (7) y eso falla. Podría funcionar si luabind admite la conversión de base a derivada, lo que no parece.

Sin embargo, si usted declaró la clase Button como

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

continuación, la instancia se llevará a cabo como una shared_ptr a Button y entonces el ID de destino coincidirá con el tipo registrado. La función get se ramificará en la primera vuelta, nunca se molestará con el reparto.

También puede encontrar el programa autocontenido que usé here at pastebin.

Y aquí es una lista de puntos de ruptura interesantes que se pueden configurar para ver lo que está pasando (luabind versión 900):

  • línea 94 en instance_holder.hpp (primera línea de pointer_holder :: get)
  • la línea 143 en instance.cpp (primera línea de cast_graph :: :: impl fundido)
+0

De hecho, agregué esas sobrecargas para get_pointer. Definitivamente pasa el smart_pointer (puedo decirlo porque los recuentos de referencia son correctos si llamo a esas funciones varias veces) – ltjax

+0

Además, pasar el puntero sin formato es aún más torpe ya que quiero almacenar los punteros, no solo ejecutar funciones en ellos. Para ese trabajo en absoluto, tendría que modificar la jerarquía de objetos de GUI para derivar de enable_shared_from_this. – ltjax

+0

Pero con su solución actual, ignora que el manual de luabind obliga a utilizar el tipo shared_ptr de clase base: consulte http://www.rasterbar.com/products/luabind/docs.html#smart-pointers, último párrafo. Si esto "solo funciona", me gustaría saber por qué ... – ltjax

Cuestiones relacionadas