2010-08-19 41 views
8

Có một số thư viện cho phép tôi dễ dàng và thuận tiện tạo ra các callback hướng đối tượng trong c + + không?Gọi lại hướng đối tượng cho C++?

ngôn ngữ Eiffel ví dụ có khái niệm "đại lý", mà ít nhiều công việc như thế này:

class Foo{ 
public: 
    Bar* bar; 

    Foo(){ 
     bar = new Bar(); 
     bar->publisher.extend(agent say(?,"Hi from Foo!", ?)); 
     bar->invokeCallback(); 
    } 

    say(string strA, string strB, int number){ 
     print(strA + " " + strB + " " + number.out); 
    } 

} 

class Bar{ 
public: 
    ActionSequence<string, int> publisher; 

    Bar(){} 

    invokeCallback(){ 
     publisher.call("Hi from Bar!", 3); 
    } 
} 

đầu ra sẽ là: Hi từ Bar! 3 Xin chào từ Foo! Vì vậy, đại lý cho phép để viên nang một thành viên chức năng vào một đối tượng, cung cấp cho nó cùng một số thông số gọi được xác định trước (Hi từ Foo), xác định các tham số mở (?), Và vượt qua nó cho một số đối tượng khác mà sau đó có thể gọi trễ rồi.

Vì C++ không cho phép tạo con trỏ hàm trên các hàm thành viên không tĩnh, có vẻ như không nhỏ để thực hiện điều gì đó dễ sử dụng trong C++. tôi tìm thấy một số bài viết với google trên callbacks đối tượng theo định hướng trong c + +, tuy nhiên, thực sự tôi đang tìm một số thư viện hoặc tập tin tiêu đề tôi chỉ đơn giản là có thể nhập khẩu cho phép tôi sử dụng một số cú pháp tương tự thanh lịch.

Bất kỳ ai cũng có một số mẹo cho tôi?

Cảm ơn!

+0

Tôi không chắc rằng tôi hoàn toàn hiểu cú pháp của bạn nhưng 'boost :: bind' có thể được sử dụng để đóng gói cả hàm và hàm thành viên với giao diện thích hợp vào đối tượng. http://www.boost.org/doc/libs/1_44_0/libs/bind/bind.html#with_member_pointers –

+0

hey! vâng tôi nghĩ đó là những gì tôi đang tìm kiếm. tiếc là tôi không thể biên dịch tăng cho iPhone (phát triển trên iPhone). tôi đọc rằng điều này cũng có thể làm với stl. ai đó có thể giải thích như thế nào? – Mat

Trả lời

2

C++ cho phép con trỏ hàm trên đối tượng thành viên.
Xem here để biết thêm chi tiết.
Bạn cũng có thể sử dụng boost.signals hoặc boost.signals2 (bỏ qua nếu chương trình của bạn có đa luồng hay không).

+0

có nhưng chỉ có các hàm thành viên tĩnh. nhưng tôi muốn các đối tượng chính nó được thông báo – Mat

+0

Không, bất kỳ chức năng thành viên có thể là một con trỏ đến một chức năng thành viên. –

+0

@Mat tôi nhấn mạnh bởi vì Mat doesnt dường như nắm bắt nó: C++ ALLOWS Thành viên con trỏ chức năng không tĩnh. Tuy nhiên cách thông minh nhất để làm những gì bạn muốn là một hàm functor (thực sự nó chỉ đơn giản là định nghĩa toán tử ngoặc đơn để các đối tượng của bạn có thể hoạt động như một hàm) như MeThinks đã trả lời. –

10

Cách OO nhất để sử dụng Callback trong C++ là để gọi một chức năng của một giao diện và sau đó vượt qua một thực hiện của giao diện đó.

#include <iostream> 

class Interface 
{ 
    public: 
    virtual void callback() = 0; 
}; 

class Impl : public Interface 
{ 
    public: 
    virtual void callback() { std::cout << "Hi from Impl\n"; } 
}; 

class User 
{ 
    public: 
    User(Interface& newCallback) : myCallback(newCallback) { } 

    void DoSomething() { myCallback.callback(); } 

    private: 
    Interface& myCallback; 
}; 

int main() 
{ 
    Impl cb; 
    User user(cb); 
    user.DoSomething(); 
} 
1

Có nhiều thư viện khác nhau cho phép bạn làm điều đó. Kiểm tra tăng :: chức năng.

Hoặc thử thực hiện đơn giản của riêng bạn:

template <typename ClassType, typename Result> 
class Functor 
{ 
typedef typename Result (ClassType::*FunctionType)(); 
ClassType* obj; 
FunctionType fn; 
public: 
Functor(ClassType& object, FunctionType method): obj(&object), fn(method) {} 

Result Invoke() 
{ 
    return (*obj.*fn)(); 
} 

Result operator()() 
{ 
    return Invoke(); 
} 
}; 

Cách sử dụng:

class A 
{ 
int value; 
public: 
A(int v): value(v) {} 

int getValue() { return value; } 
}; 


int main() 
{ 
A a(2); 

Functor<A, int> fn(a, &A::getValue); 

cout << fn(); 
} 
1

Tham gia ý tưởng về functors - sử dụng std :: tr1 :: chức năng và tăng cường :: ràng buộc để xây dựng các đối số vào nó trước khi đăng ký nó.

0

Có nhiều khả năng trong C++, vấn đề thường là một trong các cú pháp.

  • Bạn có thể sử dụng con trỏ để hoạt động khi bạn không yêu cầu trạng thái, nhưng cú pháp thực sự kinh khủng. Điều này có thể được kết hợp với boost::bind cho một điều thú vị hơn ...cú pháp (*)
  • Tôi sửa sai giả định của bạn, nó thực sự khả thi để có con trỏ đến hàm thành viên, cú pháp chỉ là quá khó xử, bạn sẽ bỏ chạy (*)
  • Bạn có thể sử dụng đối tượng Functor, về cơ bản là Functor là một đối tượng mà quá tải toán tử (), ví dụ void Functor::operator()(int a) const;, vì nó là đối tượng có trạng thái và có thể xuất phát từ giao diện chung
  • Bạn chỉ cần tạo phân cấp của riêng mình với tên đẹp hơn cho hàm gọi lại nếu bạn không Không muốn đi đường quá tải của nhà điều hành
  • Cuối cùng, bạn có thể tận dụng các tiện ích C++ 0x: std::function + các hàm lambda thực sự tuyệt vời khi nói đến để thể hiện.

tôi sẽ đánh giá cao một bài đánh giá về cú pháp lambda;)

Foo foo; 
std::function<void(std::string const&,int)> func = 
    [&foo](std::string const& s, int i) { 
    return foo.say(s,"Hi from Foo",i); 
    }; 

func("Hi from Bar", 2); 
func("Hi from FooBar", 3); 

Tất nhiên, func chỉ khả thi khi foo là khả thi (phạm vi vấn đề), bạn có thể sao chép foo sử dụng [=foo] để chỉ đi qua giá trị thay vì truyền bằng tham chiếu.

(*) Mandatory Tutorial on Function Pointers

5

dân thường sử dụng một trong những mô hình:

thừa kế. Nghĩa là, bạn định nghĩa một lớp trừu tượng có chứa hàm gọi lại. Sau đó, bạn lấy một con trỏ/tham chiếu đến nó. Điều đó có nghĩa là bất kỳ ai cũng có thể kế thừa và cung cấp gọi lại này.

class Foo { 
    virtual void MyCallback(...) = 0; 
    virtual ~Foo(); 
}; 
class Base { 
    std::auto_ptr<Foo> ptr; 
    void something(...) { 
     ptr->MyCallback(...); 
    } 
    Base& SetCallback(Foo* newfoo) { ptr = newfoo; return *this; } 
    Foo* GetCallback() { return ptr; } 
}; 

Thừa kế lại. Đó là, lớp gốc của bạn là trừu tượng, và người dùng thừa hưởng từ nó và định nghĩa các callback, thay vì có một lớp cụ thể và các đối tượng gọi lại chuyên dụng.

class Foo { 
    virtual void MyCallback(...) = 0; 
    ... 
}; 
class RealFoo : Foo { 
    virtual void MyCallback(...) { ... } 
}; 

Thậm chí thừa kế thừa hơn. Bằng cách này, bạn có thể sử dụng các mẫu để thay đổi hành vi của một đối tượng. Nó tương tự như tùy chọn thứ hai nhưng hoạt động tại thời gian biên dịch thay vì tại thời gian chạy, có thể mang lại nhiều lợi ích và nhược điểm khác nhau, tùy thuộc vào ngữ cảnh.

template<typename T> class Foo { 
    void MyCallback(...) { 
     T::MyCallback(...); 
    } 
}; 
class RealFoo : Foo<RealFoo> { 
    void MyCallback(...) { 
     ... 
    } 
}; 

Bạn có thể lấy và sử dụng con trỏ hàm thành viên hoặc chức năng con trỏ thường xuyên

class Foo { 
    void (*callback)(...); 
    void something(...) { callback(...); } 
    Foo& SetCallback(void(*newcallback)(...)) { callback = newcallback; return *this; } 
    void (*)(...) GetCallback() { return callback; } 
}; 

Có chức năng objects- họ quá tải operator(). Bạn sẽ muốn sử dụng hoặc viết một hàm wrapper- hiện đang được cung cấp trong hàm std ::/boost ::, nhưng tôi cũng sẽ trình bày một cách đơn giản ở đây. Nó tương tự như khái niệm đầu tiên, nhưng ẩn thực hiện và chấp nhận một loạt các giải pháp khác. Cá nhân tôi thường sử dụng phương thức này làm phương thức gọi lại của tôi.

class Foo { 
    virtual ... Call(...) = 0; 
    virtual ~Foo(); 
}; 
class Base { 
    std::auto_ptr<Foo> callback; 
    template<typename T> Base& SetCallback(T t) { 
     struct NewFoo : Foo { 
      T t; 
      NewFoo(T newt) : t(newt) {} 
      ... Call(...) { return t(...); } 
     }; 
     callback = new NewFoo<T>(t); 
     return this; 
    } 
    Foo* GetCallback() { return callback; } 
    void dosomething() { callback->Call(...); } 
}; 

Giải pháp đúng chủ yếu phụ thuộc vào ngữ cảnh. Nếu bạn cần để lộ một API kiểu C thì các con trỏ hàm là cách duy nhất để đi (nhớ void * đối với các đối số người dùng). Nếu bạn cần thay đổi theo thời gian chạy (ví dụ, để lộ mã trong một thư viện được biên dịch trước) thì không thể sử dụng phép kế thừa tĩnh tại đây.

Chỉ cần lưu ý nhanh: Tôi đã bỏ qua mã đó, vì vậy nó sẽ không hoàn hảo (như các công cụ sửa đổi truy cập cho các chức năng, v.v.) và có thể có một vài lỗi.

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