2017-01-26 3 views
1

Ich schrieb das folgende Beispiel, das keine Standardbibliothek verwendet, um eine Klassenmemberfunktion aufzurufen. Dies funktioniert wie erwartet, aber ich möchte es generischer machen. Dies ist für Embedded-Systeme mit 8-Bit-Mikrocontrollern gedacht. Ich möchte keine Diskussion darüber provozieren, ob C++ für diese Art von Architektur verwendet werden soll.Member-Funktionsklasse ohne stl und dynamische Speicherzuweisung

Der Code funktioniert wie folgt: Das Verwenden eines void-Zeigers für eine Klassenmemberfunktion oder eine Funktion ist nicht möglich. Obwohl es möglich ist, Leerzeiger für einzelne Funktionen zu verwenden, ist dies kein gut definiertes Verhalten.

In diesem Fall verwende ich eine statische Funktion zum Aufrufen des Klassenmembers. Die Methodenaufruffunktion ist eine statische Vorlagenfunktion, die den Objekttyp und einen Mitgliedsfunktionszeiger als Vorlagenargumente akzeptiert.

Die Bindefunktion speichert den Objektzeiger als Void-Zeiger und setzt CallingFunction innerhalb der Klasse auf die spezifische statische Methode. Wenn der Aufruf mit dem Operator() erfolgt, wird der Aufrufer-Function-Zeiger mit dem spezifischen Objekt und allen Parametern aufgerufen.

All dies funktioniert wie erwartet und kann mit C++ 11 basierend auf variadischen Vorlagen kompiliert werden. Meine Absicht ist es, die api angenehmer zu machen. Mit f.bind (& t) scheint die ganze Zeit ein bisschen lang zu sein, aber ich kenne keinen besseren Weg, dies zu tun. Irgendwelche Ideen?

Ein weiterer Punkt ist das Speichern von Funktionen, die nicht Teil einer Klasse sind.

#include <iostream> 

class testclass { 
public: 
    virtual char test(void){ 
     return '.'; 
    } 
}; 

class testclass2 : public testclass{ 
public: 
    char test(void){ 
     return '@'; 
    } 
}; 

template<typename signature> 
class Function; 

template<typename TReturn, typename ...TParam> 
class Function<TReturn(TParam...)> { 
public: 
    template<typename TObject, TReturn (TObject::*TMethod)(TParam...)> 
    void bind(TObject *obj){ 
     this->obj = obj; 
     this->callingFunction = &methodCaller<TObject, TMethod>; 
    } 

    TReturn operator()(TParam... params){ 
     return callingFunction(this->obj, params...); 
    } 
private: 
    void *obj; 
    TReturn (*callingFunction)(void *obj, TParam...); 

    template<typename TObject, TReturn (TObject::*TMethod)(TParam...)> 
    static TReturn methodCaller(void *obj, TParam... params) { 
     TObject *c = static_cast<TObject*>(obj); 
     return (c->*TMethod)(params...); 
    } 
}; 

int main(){ 
    testclass t; 

    Function<char(void)> f; 
    f.bind<testclass, &testclass::test>(&t); 
    std::cout << f(); 

    testclass2 t1 = testclass2(); 
    f.bind<testclass, &testclass::test>(&t1); 
    std::cout << f(); 
} 
+0

Welche Schnittstelle erwarten Sie? Willst du etwas wie 'std :: bind (& testclass :: test, & t/*, _1, _2 * /);' – Jarod42

+1

Eine bessere Schnittstelle wäre f.bind (& t, & testclass :: test) – Gustavo

Antwort

0

Nachfolgend können Sie Ihre erwartete Schnittstelle

template<typename signature> class Function; 

template<typename TReturn, typename ...TParam> 
class Function<TReturn(TParam...)> { 
private: 
    template <typename Obj> 
    struct Helper 
    { 
     Obj* obj; 
     TReturn (Obj::*m)(TParam...); 

     Helper(Obj* obj, TReturn (Obj::*m)(TParam...)) : obj(obj), m(m) {} 

     template <typename ... Args> 
     TReturn call(Args&&... args) { return (obj->*m)(std::forward<Args>(args)...); } 
    }; 

public: 
    template<typename TObject> 
    void bind(TObject *obj, TReturn (TObject::*m)(TParam...)){ 
     this->obj = std::make_shared<Helper<TObject>>(obj, m); 
     this->callingFunction = &methodCaller<TObject>; 
    } 

    TReturn operator()(TParam&&... params){ 
     return callingFunction(this->obj.get(), std::forward<TParam>(params)...); 
    } 
private: 
    std::shared_ptr<void> obj; 
    TReturn (*callingFunction)(void* obj, TParam...); 

    template<typename TObject> 
    static TReturn methodCaller(void *obj, TParam... params) { 
     auto* c = static_cast<Helper<TObject>*>(obj); 
     return c->call(std::forward<TParam>(params)...); 
    } 
}; 

Demo

Ich lasse Sie std::shared_ptr ersetzen in Ihnen der Standard-Bibliothek mit Ihrem eigenen Smart-Pointer loswerden wollen.

+0

Es gibt keine dynamische Speicherzuweisung und keine Möglichkeit, std :: überhaupt zu verwenden. – Gustavo

+0

Ich verwendete Std :: Cout nur zum Testen der Ausgabe, aber der endgültige Code wird auf 8-Bit-Prozessor ausgeführt – Gustavo

Verwandte Themen