2012-05-03 17 views
9

Mam API (konkretną bibliotekę GUI), która polega na wartości std::shared_ptr, tj. Często są one używane jako parametry funkcji i przechowywane w innych obiektach. Na przykład widżety kontenerów, takie jak splittery i pola, będą przechowywać ich widżety podrzędne w shared_ptr s. Teraz chciałbym zmapować ten interfejs API do Lua przez luabind. W idealnym świecie Luabind tworzyłby nowe obiekty w shared_ptrs i pozwalał mi przekazywać je bezpośrednio do funkcji pobierających parametry shared_ptr. To wydaje się działać dla pojedynczych klas, np .:Używanie luabind i std :: shared_ptr z dziedziczeniem

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

Podczas Oświadczam, to tak, mogę wystawiać i funkcje wykorzystania takich jak void foo(std::shared_ptr<Button> const&).

Teraz podręcznik luabind wspomina o tym, że aby użyć hierarchii klas, musiałbym użyć tej samej instancji szablonu shared_ptr dla wszystkich klas w hierarchii, np.

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

Teraz nie można już nazwać foo - nie będą mogły znaleźć funkcję z Lua. Czy w jakiś sposób mogę sprawić, że luabind nadal obsługuje przyciski przechodzące w shared_ptr s? Chciałbym również wiedzieć, dlaczego luabind upoważnia do używania tego samego inteligentnego wskaźnika dla wszystkich klas w hierarchii, zamiast zamieniać je na wskaźniki klasy bazowej.

+0

nie powinien, że druga linia 'użycie std :: shared_ptr ' 'nie std :: shared_ptr

+0

Tak - dziękuję! Tylko typ! – ltjax

Odpowiedz

7

Myślę za to do pracy trzeba związać swoją klasę pochodną tak:

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

Na przykład:

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

Teraz można go używać z 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) 
     ]; 
    } 
}; 

In lua:

b = Button() 

a = Action() 

a:DoClick(b) 

Powodem jest to, że luabind używa systemu typu id z liczbami całkowitymi (dokładniej std :: size_t jak zdefiniowano w dziedziczeniu.hpp).
można uzyskać typu id każdej zarejestrowanej z funkcją typu:

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

Jeżeli T jest zarejestrowanym klasa.
W moim programie demonstracyjnym są:

  • BaseWidget = 3
  • std :: shared_ptr < BaseWidget> = 6
  • Przycisk = 4
  • std :: shared_ptr < Przycisk> = 7
  • Działanie = 5

Tak więc, gdy wywołasz DoClick z lua, zadzwoni do członka pobierania t on szablon klasy pointer_holder w 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 
    ); 
} 

Jak widać, jeśli klasa docelowa nie jest taki sam jak ten zarejestrowany, to postaram się zrobić odlew.
Tutaj sprawy stają się interesujące. Jeśli zadeklarowana klasa Przycisk jak

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

następnie wystąpienie odbędzie się jako shared_ptr do BaseWidget, zatem funkcja obsada będzie próbował rzucić z BaseWidget (3) do std :: shared_ptr < przycisk> (7) i to się nie udaje. Mogłoby to działać, gdyby luabind obsługiwał konwersję z bazy do pochodnej, której nie wydaje się.

Jeśli jednak ogłoszony klasę przycisk jako

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

następnie wystąpienie odbędzie się jako jako shared_ptr na przycisk, a następnie id docelowym będzie pasował zarejestrowanego typu. Funkcja get będzie rozgałęziała się przy pierwszym powrocie, nigdy nie przejmując się obsadą.

Można również znaleźć samodzielny program, którego użyłem: here at pastebin.

A oto lista interesujących punktów przerwy można ustawić, aby zobaczyć, co się dzieje (luabind wersja 900):

  • linia 94 w instance_holder.hpp (pierwsza linia pointer_holder :: dostać)
  • linia 143 (w instance.cpp pierwszej linii cast_graph :: iMPL :: cast)
+0

Właściwie dodałem te przeciążenia dla get_pointer. Zdecydowanie mija on smart_pointer (mogę powiedzieć, ponieważ liczniki odwołań są poprawne, jeśli wielokrotnie wywołuję te funkcje). – ltjax

+0

Przekazywanie surowego wskaźnika jest jeszcze bardziej niezgrabne, ponieważ chcę przechowywać wskaźniki, a nie tylko wykonywać na nich funkcje. W przypadku tej pracy musiałbym zmodyfikować hierarchię obiektów GUI, aby wyprowadzić z enable_shared_from_this. – ltjax

+0

Jednak przy obecnym rozwiązaniu ignorujesz to, że instrukcja obsługi luabind używa typu shared_ptr klasy podstawowej: zobacz http://www.rasterbar.com/products/luabind/docs.html#smart-pointers, ostatni akapit. Jeśli to "po prostu działa", chciałbym wiedzieć, dlaczego ... – ltjax

Powiązane problemy