2011-02-07 9 views
6

Posiadamy prostą bibliotekę komunikacyjną dla naszych klientów.Oddzwonienie w C++ do członka klasy

Mój problem: Jak zapisać wskaźnik do metody z klasy naszego klienta?

Library.h to plik nagłówkowy ze wszystkimi metodami, jakich potrzebuje nasz klient do nawiązania komunikacji.

library.cpp to nasz kod. Gdzieś tutaj muszę zapisać wskaźniki do metody funkcji oddzwaniania od naszego klienta.

customer.cpp to przykład, jak klient korzysta z naszej biblioteki.

library.h:

// This is the header file what our customer gets 
class Library { 
    public: 
    template <class Object, class Function> 
    void SetCallback(Object &obj, Function f); 
}; 

library.cpp:

struct T_CUSTOMER { 
    Object o; // <- ??? 
    Function f; // <- ??? 
} customer; 

void Library::SetCallback(Object &obj, Function f) { 
    //Saving the method from our costumer 
    customer.o = obj; // <- ??? 
    customer.f = f;  // <- ??? 
} 

void someFunction(void) { 
    // here i want to call the method from the customer 
    customer.o->customer.f(); //<- ??? 
} 

customer.cpp:

class AnyCustomerClass { 
    private: 
     Library lib; 

    public: 
     AnyCustomerClass() { 
      //< here the customer sets his method which I should call 
      lib.SetCallback(this, &AnyCustomerClass::callback()); 
     } 

     callback() { 
      // do something 
     } 
} 

Dzięki za jakaś pomoc!

Odpowiedz

7

Podstawową ideą jest zdefiniowanie abstrakcyjnej klasy Callback, która jest faktycznie przekazywana do interfejsu. Ten oddzwania do funkcji przechodzącej pojedynczy parametr int:

struct Callback { 
    virtual ~Callback(){} 
    virtual void operator()(int param)=0; 
}; 

Klasa ta umożliwia implementację będzie wolny od znajomości kodu trzeba oddzwonić do. Oczywiście, aby połączyć się z klasą, potrzebujesz instancji wywołania zwrotnego, która posiada wiedzę na temat jej celu. Więc wtedy również zapewnić szablonie klasy dziecięcej, która ułatwia dla użytkowników biblioteki do oprawienia metody w jednym z ich klas do instancji generycznego callback: -

template<class T> 
class ClassCallback : public Callback { 
    T* _classPtr; 
    typedef void(T::*fncb)(int param); 
    fncb _cbProc; 
public: 
    ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){} 
    virtual void operator()(int param){ 
    (_classPtr->*_cbProc)(param); 
    } 
}; 

Aby utworzyć instancję oddzwanianie ze swojej klasy, kod wyglądałby tak. Inwokacja jest prosta:

struct CMyClass { 
    Library* _theLibrary; 
    CMyClass(Library* init):_theLibrary(init){ 
    Callback* pCB = new ClassCallback<CMyClass>(&myClass,&CMyClass::OnCb); 
    _theLibrary->SetCallback(pCB); 
    } 
    void OnCb(int){ 
    // callback triggered 
    } 
    void Run(){ 
    _theLibrary->DoWork(); 
    } 
}; 

Podsumowanie: Library.h wtedy wyglądałoby to tak. Zdefiniuj klasę abstrakcyjną oddzwonienia, swoją klasę biblioteki i na matrycy klasę użytkową, że klient używa do zawijania ich swoją klasę i jej metody wywołania zwrotnego z:

// This is the header file what our customer gets 
struct Callback {... }; 
class Library { 
    Callback* _pcb; 
    public: 
    void SetCallback(Callback* pcb){_pcb=pcb;} 
    void DoWork(){ 
     int status=0; 
     (*pcb)(status); 
    } 
    ~Library(){delete _pcb;} 

}; 
template<class T> struct ClassCallback{ ... }; 
+0

Czy możesz jeszcze raz rzucić okiem na kod, który napisałem wczoraj? [Link] (http://stackoverflow.com/questions/4919308/callback-in-c-to-a-class-member/4920803#4920803) I nigdy nie myślałem, że napisanie takiej funkcji oddzwaniania byłoby tak skomplikowane. Dziękuję bardzo! – Sascha

+0

-1 za ignorowanie zarządzania pamięcią. – ltjax

5

Podstawową ideą jest ukrycie w kodzie dokładnego rodzaju obiektu i funkcji (Object i Function) za wirtualnym wywołaniem funkcji i zawijanie ich w abstrakcyjny interfejs (jest to idiom typu "usuwanie typu").

Następnie klienci mogą czerpać korzyści z podstawowego typu wywołania zwrotnego za pośrednictwem interfejsu szablonu.

Aby zapoznać się z samouczkiem, patrz część 4. na temat this website. Albo przyjrzyj się, jak działają Boost.Function i Boost.Bind (robią dokładnie to, choć z nieco mocniejszym interfejsem).

+1

+1 do boost.function – fouronnes

+0

-1 dla użycia boost :: do uzyskania czegoś w std :: –

+0

@Chris gdzie robi std :: narzędzie typu wymazuje obiekty funkcji (pre-tr1)? Ponadto nie używam nawet "boostu", wymieniając go jako odniesienie do tego, jak te rzeczy mogą być ładnie zaimplementowane. – ltjax

0

Poniższy kod wydaje się działać w jakiś sposób, ale imho całego projektu jest bardzo podejrzane. http://codepad.org/9QDcMJAg

#include <stdio.h> 

struct Library { 
    template <class Object,class Function> 
    void SetCallback(Object &obj, Function f); 
}; 


struct Object { 
    void Method(void); 
}; 

typedef void (Object::*Function)(void); 

struct T_CUSTOMER { 
    Object o; 
    Function f; 
}; 

T_CUSTOMER customer; 

template <class Object,class Function> 
void Library::SetCallback(Object &obj, Function f) { 
    customer.o = obj; 
    customer.f = f; 
} 

void someFunction(void) { 
    (customer.o.*customer.f)(); 
} 

struct AnyCustomerClass : Object { 
    Library lib; 

    AnyCustomerClass() { 
    lib.SetCallback(*this, (Function)&AnyCustomerClass::callback); 
    } 

    void callback(void) { 
    printf("callback!\n"); 
    } 
}; 

int main(void) { 

    AnyCustomerClass a; 

    someFunction(); 

} 
3

Najprostszym i najbardziej elastycznym sposobem jest użycie std :: funkcję.

Załóżmy, że mam funkcję (ale może to być również klasa), która musi wywołać inną funkcję przekazaną do niej. I zdefiniować tę funkcję tak:

#include <functional>   // defines std::function 
#include <iostream> 

typedef std::function<bool(int)> FunctionPtr; 

void myFunction (const FunctionPtr &functionPtr) 
{ 
std::cout << "Before" << std::endl; 
functionPtr(123); 
std::cout << "After" << std::endl; 
} 

Pierwszym przykładem, w jaki sposób wykorzystać ten wykorzystuje funkcję globalnego (lub metody statycznej), podobnie jak to:

bool myFunPtr(int i) 
    { 
    std::cout << "FunPtr:" << i << std::endl; 
    return true; 
    } 

int main() 
{ 
myFunction (myFunPtr); 
} 

prostu przekazać wskaźnik funkcji do myFunction.

mogę też użyć wyrażenia lambda, na przykład:

int main() 
{ 
myFunction ([](int i)->bool{std::cout << "Lambda:" << i << std::endl;return true;}); 
} 

Trzeci przykład (i która jest chyba to, co chcesz), jest przekazać instancję klasy, która ma zdefiniowaną funkcję operatorów, jak to:

class X 
    { 
    public: 
     X(std::string s) : m_s(s) {} 
     bool operator()(int i) 
     { 
     std::cout << m_s << i << std::endl; 
     return true; 
     } 
    private: 
     std::string m_s; 
    }; 

int main() 
{ 
myFunction ([](int i)->bool{std::cout << "Lambda:" << i << std::endl;return true;}); 

myFunction (myFunPtr); 

X x1("x1:"); 
myFunction (x1); 

X x2("x2:"); 
myFunction (x2); 
} 

Jak widać, przy użyciu funkcji std ::, mogę przejść 3 różne rodzaje 'odniesienia do funkcji' (wskaźników funkcji, lambda i funktory).

+0

Nie każdy ma jeszcze kompilator zgodny z TR1 lub "Pre" w wersji C++ 0x. –

0

Dzięki za odpowiedzi, ale nadal mają problemy z realizacją (I normalnie programów w języku Java lub C (microcontoller). Nigdy nie myślałem, że będzie tak skomplikowany dla mnie, aby zrealizować to w C++))

Lubię korzystać z sugestii Chrisa Becke.

library.h

// This is the header file what our customer gets 
struct Callback { 
    virtual void operator()(int param)=0; 
}; 
class Library { 
    Callback *cb; 
    public: 
    void SetCallback(Callback*); 
}; 
template<class T> 
class ClassCallback : public Callback { 
    T* _classPtr; 
    typedef void(T::*fncb)(int dwTime); 
    fncb _cbProc; 
public: 
    ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){} 
    virtual void operator()(int param){ 
    (_classPtr->*_cbProc)(param); 
    } 
}; 

customer.cpp

class T_Customer{ 
private: 
    Library lib; 
    void OnCallback(int param){ 
      printf("Parameter: %i\n",param); 
     } 
    }; 
public: 
    T_Customer(){ 
     lib.SetCallback(new ClassCallback<T_Customer>(this,&T_Customer::OnCallback)); 
    } 
}; 

Library.cpp

void Library::SetCallback(Callback *callback){ 
    cb = callback; 
} 


void executeCallback(){ 
    if(cb) 
    (*cb)(); 
} 

int main(void){ 
    T_Customer customer; 
    executeCallback(); 
    return 0; 
} 
+0

Jak napisałeś to jako odpowiedź, naprawiłem ją na miejscu (mam nadzieję) –

Powiązane problemy