member function mit exakter Signatur binden
-
Hallo,
mir ist kein besserer Name für den Titel eingefallen
Ich habe dieses Problem:#include <vector> #include <iostream> #include <functional> template<typename Signature> struct Publisher { using FunctionType = std::function<Signature>; std::vector<FunctionType> Subscribers_; void subscribe( Signature FreeFunc ) { Subscribers_.emplace_back( FreeFunc ); } template<typename ObjType, typename P1> void subscribe( ObjType* Obj, void (ObjType::*MemFun)( P1 ) ) { Subscribers_.push_back( std::bind( MemFun, Obj, std::placeholders::_1 ) ); } template<typename ObjType, typename P1, typename P2> void subscribe( ObjType* Obj, void (ObjType::*MemFun)( P1, P1 ) ) { Subscribers_.push_back( std::bind( MemFun, Obj, std::placeholders::_1, std::placeholders::_2 ) ); } template<typename ...Params> void operator()( Params&&... p ) { for( const auto& Sub : Subscribers_ ) { Sub( std::forward<Params>( p )... ); } } }; void func1( unsigned int ) { } void func2( unsigned int& ) { } void func3( const unsigned int& ) { } struct Test { void func1( unsigned int ) { } void func2( unsigned int& ) { } void func3( const unsigned int& ) { } }; int main() { Publisher<void ( unsigned int& )> Pub; // Pub.subscribe( &func1 ); // falsche Signatur -> Compiler Error Pub.subscribe( &func2 ); // Pub.subscribe( &func3 ); // falsche Signatur -> Compiler Error Test t; Pub.subscribe( &t, &Test::func1 ); // falsche Signatur -> ok Pub.subscribe( &t, &Test::func2 ); Pub.subscribe( &t, &Test::func3 ); // falsche Signatur -> ok unsigned int Arg = 1; Pub( Arg ); }
Hier der Code auf Ideone
- die Parameter P1 in der
subscribe
-Methode für member Funktionen verlieren ihre CV-Qualifikation. Damit lassen sichTest::func1
,Test::func2
undTest::func3
binden, obwohl nur die Signatur vonTest::func2
passt. - ich muss für member Funktionen die Platzhalter für die Argumente angeben und benennen. Das erfordert ein subscribe-Methoden für member Funktionen mit unterschiedlich vielen Parametern.
Wie kann ich sicherstellen, dass die Member-Funktion in
subscribe
exakt der vorgegebenen Signatur entspricht?
Und gibt´s für 2) eine elegante Lösung?
- die Parameter P1 in der
-
So...
Habe das etwas umgestellt und eine halbwegs zufrieden stellende Lösung gefunden. Dazu musste ich den template Parameter des Publisher ändern, der sieht jetzt leider nicht mehr wie eine Funktionssignatur aus.#include <vector> #include <functional> namespace { // Catch-all für nicht unterstützte Anzahl von Aufrufparametern template<typename RetType, unsigned int ParamCount> struct SubscriberFactory; // Spezialisierung für 0 Parameter template<typename RetType> struct SubscriberFactory<RetType,0> { template<typename ObjType, typename MemFunType> static RetType create_subscriber( ObjType* Obj, MemFunType Fun ) { return std::bind( Fun, Obj ); } } // Spezialisierung für 1 Parameter template<typename RetType> struct SubscriberFactory<RetType,1> { template<typename ObjType, typename MemFunType> static RetType create_subscriber( ObjType* Obj, MemFunType Fun ) { return std::bind( Fun, Obj, std::placeholders::_1 ); } } // usw... } template<typename ...Params> class Publisher { using Signature = void( Params... ); using FunctionType = std::function<Signature>; std::vector<FunctionType> Subscribers_; public: void subscribe( Signature FreeFunc ) { Subscribers_.emplace_back( FreeFunc ); } template<typename ObjType> void subscribe( ObjType* Obj, void (ObjType::*MemFun)( Params... ) ) { SubscriberFactory<FuncType, sizeof...( Params )> Factory; Subscribers_.push_back( Factory.template create_subscriber( Obj, MemFun ) ); } void operator()( Params&&... p ) { for( const auto& Sub : Subscribers_ ) { Sub( std::forward<Params>( p )... ); } } };
-
So?
template <typename SigT> struct Publisher; template <typename SigT> struct PublisherBase; // am besten in einem detail-Namespace verstecken template <typename... Ts> struct PublisherBase<void(Ts...)> { private: using Sig = R(Ts...); public: void subscribe(void (*freeFunc)(Ts...)) { static_cast<Publisher<Sig>*>(this)->doSubscribe(freeFunc); } template <class C> void subscribe(C* inst, void (C::*memberFunc)(Ts...)) { static_cast<Publisher<Sig>*>(this)->doSubscribe([inst, memberFunc](Ts&&... args) { (inst->*memberFunc)(std::forward<Ts>(args)...); }); } template <class C> void subscribe(const C* inst, void (C::*memberFunc)(Ts...) const) { static_cast<Publisher<Sig>*>(this)->doSubscribe([inst, memberFunc](Ts&&... args) { (inst->*memberFunc)(std::forward<Ts>(args)...); }); } void operator ()(Ts... args) const { for (auto& func : static_cast<const Publisher<Sig>*>(this)->subscribers_) func(std::forward<Ts>(args)...); } }; template <typename SigT> struct Publisher : PublisherBase<SigT> { friend PublisherBase<SigT>; private: std::vector<std::function<SigT>> subscribers_; void doSubscribe(std::function<SigT> func) { subscribers_.push_back(std::move(func)); } };
-
Wenn ich es mir recht überlege, kannst du sogar auf die Basisklasse verzichten und gleich
Publisher<>
selbst spezialisieren.template <typename SigT> struct Publisher; template <typename... Ts> struct Publisher<void(Ts...)> { private: std::vector<std::function<void(Ts...)>> subscribers_; public: void subscribe(void (*freeFunc)(Ts...)) { subscribers_.push_back(freeFunc); } template <class C> void subscribe(C* inst, void (C::*memberFunc)(Ts...)) { subscribers_.push_back([inst, memberFunc](Ts&&... args) { (inst->*memberFunc)(std::forward<Ts>(args)...); }); } template <class C> void subscribe(const C* inst, void (C::*memberFunc)(Ts...) const) { subscribers_.push_back([inst, memberFunc](Ts&&... args) { (inst->*memberFunc)(std::forward<Ts>(args)...); }); } void operator ()(Ts... args) const { for (auto& func : subscribers_) func(std::forward<Ts>(args)...); } };
-
Warum kommst du ohne std::placeholder aus?
Edit:
Hab´s verstanden, glaube ich. Ist ne schicke Lösung. Gefällt mir
-
DocShoe schrieb:
Warum kommst du ohne std::placeholder aus?
Weil ich Lambdafunktionen statt
std::bind()
benutze.
-
Jau, hat nen Moment gedauert bis ich´s gesehen habe.