2010-08-19 12 views
8

Czy istnieje biblioteka, która pozwala mi łatwo i wygodnie tworzyć wywołania zwrotne zorientowane obiektowo w języku C++?Callbacks zorientowane na obiekt dla C++?

język Eiffla ma na przykład pojęcie „środki”, które bardziej lub mniej pracy tak:

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

wyjściowe będą: Hi z baru! 3 Cześć z Foo!

A więc - agent pozwala na kapsułkę funkcji członkowskiej w obiekcie, przekazując ją według pewnych zdefiniowanych parametrów wywołania (Hi z Foo), określa parametry otwarte (?) I przekazuje je do innego obiektu, który może następnie wywołać to później.

Ponieważ C++ nie pozwala na tworzenie wskaźników funkcyjnych na niestatycznych funkcjach składowych, nie wydaje się, aby tak proste było zaimplementowanie czegoś tak łatwego w użyciu w języku C++. Znalazłem kilka artykułów z google na temat wywołań zwrotnych zorientowanych obiektowo w języku C++, jednak w rzeczywistości szukam plików bibliotecznych lub nagłówkowych, które po prostu mogę zaimportować, co pozwala mi na użycie podobnej eleganckiej składni.

Ktoś ma dla mnie wskazówki?

Dzięki!

+0

Nie jestem pewien, czy całkowicie rozumiem twoją składnię, ale 'boost :: bind' może być użyty do spakowania zarówno funkcji, jak i funkcji składowych za pomocą odpowiedniego interfejsu do obiektu. http://www.boost.org/doc/libs/1_44_0/libs/bind/bind.html#with_member_pointers –

+0

hej! tak, myślę, że tego właśnie szukam. niestety nie mogę skompilować doładowania dla iPhone'a (rozwijającego się na iPhonie). Czytałem, że jest to również możliwe ze stl. Czy ktoś może wytłumaczyć jak? – Mat

Odpowiedz

2

C++ udostępnia wskaźniki funkcji na obiektach członkowskich.
Aby uzyskać więcej informacji, patrz here.
Można również użyć boost.signals lub boost.signals2 (depandowanie, jeśli program jest wielowątkowy lub nie).

+0

Tak, ale tylko statyczne funkcje członków. ale chcę, aby sam obiekt był powiadamiany – Mat

+0

Nie, każda funkcja elementu może być wskaźnikiem do funkcji składowej. –

+0

@Mat podkreślam, ponieważ Mat nie wydaje się tego rozumieć: C++ POZWALA Członkom niefunkcjonalne wskaźniki funkcji. Jednak najmądrzejszy sposób robienia tego, co chcesz, to funktor (w rzeczywistości to po prostu definiowanie operatora nawiasu, tak aby twoje obiekty mogły zachowywać się jak funkcje), na przykład MeThinks. –

10

Najbardziej OO sposobem korzystania oddzwaniania w C++ jest do wywołania funkcji interfejsu, a następnie przekazać implementację tego interfejsu.

1

Istnieje wiele bibliotek, które pozwalają to zrobić. Sprawdź funkcję boost :: function.

Albo próbować własną prostą realizację:

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

Zastosowanie:

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

Dołączenie ideę funktorów - używać std :: tr1 :: funkcję i boost :: wiążą się budować argumenty do niego przed zarejestrowaniem.

0

Istnieje wiele możliwości w C++, przy czym generalnie problem jest jednym ze składni.

  • Możesz użyć wskaźnika do funkcji, gdy nie potrzebujesz stanu, ale składnia jest naprawdę okropna. Można to połączyć z boost::bind, aby uzyskać jeszcze więcej ... ciekawych ...Składnia (*)
  • poprawiam swoje fałszywe założenie, że rzeczywiście jest to możliwe, aby wskaźnik do funkcji składowej, składnia jest tak niewygodne będziesz uciekać (*)
  • Można używać obiektów funktora, w zasadzie Funktor jest obiektem przeciążającym operatora (), na przykład void Functor::operator()(int a) const;, ponieważ jest to obiekt, który ma on i może pochodzić ze wspólnego interfejsu. Można po prostu utworzyć własną hierarchię z ładniejszą nazwą funkcji wywołania zwrotnego, jeśli nie chcesz iść do operatora przeciążającego drogi
  • Wreszcie, możesz skorzystać z funkcji C++ 0x: std::function + funkcje lambda są naprawdę niesamowite, gdy przychodzi do ekspresji.

Byłbym wdzięczny za opinię na temat składni 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); 

Oczywiście func jest jedyną realną podczas foo jest opłacalne (emisja zakres), można skopiować foo użyciu [=foo] wskazać podanie przez wartości zamiast przechodzić przez referencję.

(*) Mandatory Tutorial on Function Pointers

5

Ludzie zazwyczaj skorzystać z jednej z kilku wzorów:

dziedziczenia. Oznacza to, że definiujesz klasę abstrakcyjną, która zawiera wywołanie zwrotne. Następnie bierzesz do niego wskaźnik/odnośnik. Oznacza to, że każdy może dziedziczyć i zapewniać to wywołanie zwrotne.

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

Dziedziczenie ponownie. Oznacza to, że twoja klasa główna jest abstrakcyjna, a użytkownik dziedziczy z niej i definiuje wywołania zwrotne, zamiast mieć konkretną klasę i dedykowane obiekty wywołania zwrotnego.

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

Jeszcze więcej dziedziczenia - statyczne. W ten sposób możesz użyć szablonów do zmiany zachowania obiektu. Jest podobny do drugiej opcji, ale działa w czasie kompilacji zamiast w czasie wykonywania, co może przynieść różne korzyści i wady w zależności od kontekstu.

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

Można zabrać i użyć wskaźników funkcji członka lub regularnych wskaźników funkcji

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

Istnieje funkcja objects- one przeciążać operatora(). Będziesz chciał użyć lub napisać funkcjonalne opakowanie - aktualnie dostarczane w funkcji std ::/boost ::, ale tutaj też pokażę prostą. Jest podobny do pierwszej koncepcji, ale ukrywa implementację i akceptuje szeroką gamę innych rozwiązań. Osobiście normalnie używam tego jako mojej metody callbackowej.

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(...); } 
}; 

Właściwe rozwiązanie zależy głównie od kontekstu. Jeśli potrzebujesz odsłonić API w stylu C, to jedynym sposobem na to są wskaźniki funkcyjne (pamiętaj o void * dla argumentów użytkownika). Jeśli chcesz się różnić w czasie wykonywania (na przykład, ujawniając kod w prekompilowanej bibliotece), wówczas nie można tutaj zastosować dziedziczenia statycznego.

Krótka notatka: Podałem ci ten kod, więc nie będzie idealnie (jak modyfikatory dostępu do funkcji itp.) I może mieć kilka błędów. To przykład.

Powiązane problemy