2012-05-03 32 views
9

Tôi có API (thư viện GUI cụ thể) dựa trên std::shared_ptr rất nhiều, tức là chúng thường được sử dụng làm tham số chức năng và được lưu trữ trong các đối tượng khác. Ví dụ: tiện ích vùng chứa, chẳng hạn như bộ tách và hộp sẽ lưu trữ tiện ích con của chúng trong số shared_ptr s. Bây giờ tôi muốn ánh xạ API này tới Lua thông qua luabind. Trong một thế giới lý tưởng, Luabind sẽ tạo ra các đối tượng mới trong shared_ptrs và cho phép tôi truyền các đối tượng đó trực tiếp đến các hàm tham số shared_ptr. Điều này dường như làm việc cho các lớp học duy nhất, ví dụ .:Sử dụng luabind và std :: shared_ptr với thừa kế

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

Trong khi tôi tuyên bố nó như thế, tôi có thể vạch trần và chức năng sử dụng như void foo(std::shared_ptr<Button> const&).

Bây giờ hướng dẫn luabind đề cập rằng để sử dụng phân cấp các lớp, tôi sẽ phải sử dụng cùng một ví dụ mẫu shared_ptr cho tất cả các lớp trong cấu trúc phân cấp, ví dụ:

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

Bây giờ tôi không còn có thể gọi foo - nó sẽ không tìm thấy hàm từ Lua. Tôi có thể bằng cách nào đó có được luabind để vẫn hỗ trợ các nút đi qua trong shared_ptr s? Ngoài ra, tôi muốn biết lý do tại sao luabind ủy nhiệm rằng bạn sử dụng cùng một con trỏ thông minh cho tất cả các lớp trong hệ thống phân cấp thay vì chúng chỉ được chuyển đổi thành con trỏ lớp cơ sở.

+0

nên không phải là dòng sử dụng thứ hai 'std :: shared_ptr 'không' std :: shared_ptr

+0

Yea - cảm ơn bạn! Chỉ cần một loại tho! – ltjax

Trả lời

7

Tôi nghĩ cho rằng để làm việc bạn phải ràng buộc lớp được thừa kế của bạn như thế này:

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

Ví dụ:

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

Bây giờ bạn có thể sử dụng nó với 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) 
     ]; 
    } 
}; 

Trong lua:

b = Button() 

a = Action() 

a:DoClick(b) 

Lý do là luabind sử dụng hệ thống loại id với số nguyên (chính xác hơn std :: size_t như được định nghĩa trong inheritance.hpp).
Bạn có thể lấy loại id của bất kỳ loại đăng ký nào có chức năng:

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

Trường hợp T là lớp đã đăng ký.
Trong chương trình demo của tôi đó là:

  • BaseWidget = 3
  • std :: shared_ptr < BaseWidget> = 6
  • Button = 4
  • std :: shared_ptr < Button> = 7
  • Hành động = 5

Vì vậy, khi bạn gọi DoClick từ lua, nó sẽ gọi là thành viên của t anh lớp mẫu pointer_holder trong 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 
    ); 
} 

Như bạn có thể thấy, nếu lớp mục tiêu không phải là giống như một đăng ký, nó sẽ cố gắng để làm một diễn viên.
Đây là nơi mọi thứ trở nên thú vị. Nếu bạn tuyên bố lớp Button như

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

thì dụ sẽ được tổ chức như một shared_ptr để BaseWidget, do đó các chức năng dàn diễn viên sẽ cố gắng đúc từ BaseWidget (3) để std :: shared_ptr < Button> (7) và điều đó không thành công. Nó có thể hoạt động nếu luabind hỗ trợ chuyển đổi dựa trên cơ sở, mà nó dường như không.

Tuy nhiên, nếu bạn đã khai báo lớp Button như

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

thì dụ sẽ được tổ chức như là một shared_ptr để nút và sau đó là id mục tiêu sẽ phù hợp với loại đã đăng ký. Hàm get sẽ phân nhánh trên lần trả về đầu tiên, không bao giờ bận tâm đến dàn diễn viên.

Bạn cũng có thể tìm thấy chương trình tự chứa mình đã sử dụng here at pastebin.

Và đây là danh sách các điểm break thú vị bạn có thể thiết lập để xem những gì đang xảy ra (luabind phiên bản 900):

  • dòng 94 trong instance_holder.hpp (dòng đầu tiên của pointer_holder :: get)
  • dòng 143 trong instance.cpp (dòng đầu tiên của cast_graph :: impl :: cast)
+0

Tôi thực sự đã thêm những quá tải đó cho get_pointer. Nó chắc chắn vượt qua smart_pointer (tôi có thể nói vì số lượng tham chiếu là đúng nếu tôi gọi các hàm đó nhiều lần) – ltjax

+0

Ngoài ra, việc chuyển con trỏ thô thậm chí còn vụng về hơn vì tôi muốn lưu trữ các con trỏ, không chỉ thực hiện các hàm trên chúng. Đối với công việc đó, tôi phải sửa đổi hệ thống phân cấp đối tượng GUI để lấy được từ enable_shared_from_this. – ltjax

+0

Nhưng với giải pháp hiện tại của bạn, bạn bỏ qua rằng hướng dẫn sử dụng luabind sử dụng loại shared_ptr cơ sở: xem http://www.rasterbar.com/products/luabind/docs.html#smart-pointers, đoạn cuối. Nếu điều này "chỉ hoạt động" tôi muốn biết tại sao ... – ltjax

Các vấn đề liên quan