Ähnliche Klassen mit einer unterschiedlichen Funktionssignatur generieren? (templates?)
-
ich würde mal vermuten, dass in deinem Fall das Folgende dein bester Freund ist:
http://www.boost.org/doc/libs/1_46_1/libs/preprocessor/doc/index.html
du musst die Klassen also als Preprozessormakro schreiben welches dann automatisch die richtigen Signaturen erzeugt.
-
Hört sich mir eher nach einem Fall für boost::factory an.
-
nurf schrieb:
Hört sich mir eher nach einem Fall für boost::factory an.
Mit boost.factory komme ich da leider nicht weiter. Zumindest sehe ich nicht, wie.
-
pumuckl schrieb:
Mit boost.factory komme ich da leider nicht weiter. Zumindest sehe ich nicht, wie.
Nach längerem Tüfteln bin ich auf sowas gekommen (noch nicht ausgereift):
template <class AbstractFactory, class ManagementPolicy> class FactoryProvider : public ManagementPolicy { std::unique_ptr<AbstractFactory*> myFactory; public: typedef typename std:tr1::result_of<AbstractFactory::create>::type result_t; //oder so ähnlich public: FactoryProvider& instance(); //singleton-instanz template <class... A> result_t create(A... args) { ManagementPolicy::pre_create(args); result_t newResult = myFactory->create(args); ManagementPolicy::post_create(newResult); return newResult; } void destroy(result_t r) { ManagementPolicy::pre_destroy(result_t); myFactory->destroy(result_t); ManagementPolicy::post_destroy(); //? } void replace(AbstractFactory* newFactory) { myFactory.reset(newFactory); } };Die Policy kann alles mögliche übernehmen, z.B. nichtstun oder Sicherstellung, dass immer nur ein Objekt des Produkt-Typs erzeugt wird usw.
Über boost könnte man sich noch die Singatur der create-Methode der abstrakten Fabrik besorgen (das geht, turtle mocks nutzt die Möglichkeit) und damit das variadic template ersetzen.
Alternativ könnte ich vielleicht auch an Stelle der AbstractFactory nur die Signatur der create-Methode vorgeben und ein boost::function Objekt mit der passenden Signatur speichern sowie einen Deleter.Hm... mir kommen mehr Ideen. Ich meld mich, wenn ich was ausgereifteres hab

-
Haesslicher Code ist haesslich.
-
Und wieder ein Problem, das es so nicht gegeben hätte, wenn man auf Singletons verzichtet hätte

-
hustbaer schrieb:
Und wieder ein Problem, das es so nicht gegeben hätte, wenn man auf Singletons verzichtet hätte

Hm. Soll ich die abstrakte Factory global machen? Welche Alternative gibts?
-
Wie wäre es mit eine Template-Fabrik mit definierbare Funktionssignatur:
template < typename CreatorSignature , typename IdentifierType> class GenericFactory { public: //CreatorSignature darf Signatur oder boost::function-Typ sein //defniert boost::function-Typ passend für die gewünschte Signatur typedef typename boost::mpl::if_<boost::is_function<CreatorSignature>, boost::function<CreatorSignature>, CreatorSignature>::type CreationFunctor; //Create-Funktor-Parametertyp typedef typename boost::call_traits<CreationFunctor>::param_type FunctorParam; //ID-Parametertyp typedef typename boost::call_traits<IdentifierType>::param_type IdentifierParam; //Ergebnistyp typedef typename CreationFunctor::result_type ProductType; bool registerCreator(IdentifierParam identifier, FunctorParam creator) { return creators.insert(std::make_pair(identifier, creator)).second != 0; } CreationFunctor const & create(IdentifierParam id) const { typename CreatorMap::const_iterator i = creators.find(id); if(i != creators.end()) { return i->second; } else { throw //bad id } } private: typedef std::map<IdentifierType, CreationFunctor> CreatorMap; CreatorMap creators; }; //... GenericFactory<BaseType * (int, SomeType const &), std::string> factory; factory.registerCreator("product_a", DerivedTypeA::create); factory.registerCreator("product_b", DerivedTypeB::create); // BaseType * obj = factory.create("product_a")(42, SomeType("You drive me crazy"));So sinngemäß?
-
Tachyon schrieb:
So sinngemäß?
Geht schon in die Richtung, danke! Ich bau mir heute abend mal was

-
pumuckl schrieb:
hustbaer schrieb:
Und wieder ein Problem, das es so nicht gegeben hätte, wenn man auf Singletons verzichtet hätte

Hm. Soll ich die abstrakte Factory global machen? Welche Alternative gibts?
Ob global oder als Singleton ist für mich in diesem Fall das selbe.
Fass die nötigen Factories (bzw. (Smart-)Pointer darauf) in einer Struktur zusammen, und reich diese als Parameter rum.In vielen Fällen kann man auch Dependency-Injection verwenden - weiss nicht ob das bei dir geht.
-
hustbaer schrieb:
reich diese als Parameter rum.
In vielen Fällen kann man auch Dependency-Injection verwenden - weiss nicht ob das bei dir geht.
Wobe Dependency Injection und Rumreichen von Fabriken (oder irgendwie gearteten Referenzen/Pointern darauf) in meinen Augen das gleiche ist. Und nein, geht auf der Ebene nicht. Ich versuche das schon weitgehend zu machen, allerdings muss ich an irgendeiner Stelle den Startpunkt haben, wo die Fabriken erzeugt werden, die ggf. rumgereicht werden. Und ich kann weder dem Client meines Frameworks zumuten, alle Fabriken reinzustopfen (zumal er die nicht kennen sollte), noch kann ich Schicht 1 zumuten, dass sie die Fabrik für Schicht N erzeugt/verändert und dann durch alle anderen Schichten durchschubst, bis sie dort ankommt, wo sie zu landen hat. Zumal sie ja auch nichts außer der Fassade für Schicht 2 kennen sollte - und eben einer Möglichkeit, wie sie die Fassade bekommen kann.
Das ist (leider) ein inhärentes Merkmal meiner Architektur: Die Schichten werden on demand von oben nach unten/außen nach innen aufgebaut, da die utneren/inneren Schichten nicht immer gebraucht werden und ggf. durch andere Implementierungen ausgetauscht werden müssen.
-
Ein erster Wurf, natürlich fehlt noch einiges:
#include <boost/function.hpp> #include <boost/function_types/result_type.hpp> namespace util { template <class Signature, class DefaultCreator> class GenericAbstractFactory { typedef typename boost::function_types::result_type< Signature >::type ResultType; typedef typename boost::function<Signature> Creator; Creator theCreator; public: GenericAbstractFactory() : theCreator(DefaultCreator()) , create(theCreator) {} public: void replace(Creator const& newCreator) { theCreator = newCreator; } Creator const& create; }; } //ns util struct fn { int operator()(bool b) { return b? 5: 10; } }; #include <iostream> using std::cout; int main() { util::GenericAbstractFactory<int(bool), fn> factory; cout << factory.create(false) << '\n'; //10 cout << factory.create(true) << '\n'; //5 factory.replace([&](bool b) -> int { return b ? 20 : 50; }); cout << factory.create(false) <<'\n'; // 50 cout << factory.create(true) <<'\n'; // 20 }
-
So, nachdem ich endlich mal wieder dazu gekommen bin...
so siehts jetzt aus:#include <boost/type_traits.hpp> #include <boost/static_assert.hpp> #include <boost/function.hpp> #include <boost/function_types/result_type.hpp> #include <boost/mpl/if.hpp> #include <boost/preprocessor/repetition.hpp> #ifndef UTIL_GENERICFACTORY_MAX_ARITY #define UTIL_GENERICFACTORY_MAX_ARITY 10 #endif #define EMPTY(n) BOOST_PP_EMPTY() #define TEMPLATE_HEADER(n) \ template <BOOST_PP_ENUM_PARAMS(n, class T)> #define DECLARE_PARAM(z, n, unused) \ T##n&& t##n #define MULTIPLY(n) \ BOOST_PP_REPEAT(n, FACTOR, ~) #define FACTOR(z, n, unused) \ BOOST_PP_IF(n, *, ) t##n #define FORWARD_PARAM(z, n, unused) \ std::forward<T##n>(t##n) //1) #define DECLARE_CREATE(z, n, unused) \ BOOST_PP_IF(n, TEMPLATE_HEADER, EMPTY)(n) \ ResultType create(BOOST_PP_ENUM(n, DECLARE_PARAM, ~)) \ { \ static_assert(n <= arity, "Too many arguments!"); \ static_assert(n>= arity, "Not enough arguments!"); \ ManagementPolicy::preCreation(); \ ResultType r = theCreator(BOOST_PP_ENUM(n, FORWARD_PARAM, ~)); \ ManagementPolicy::postCreation(r); \ return r; \ } \ namespace detail { struct NoManagemetPolicy { void preCreation() {} template <class T> void postCreation(T const&) {} template <class T> void preDestruction(T const&) {} void postDestruction() {} }; } template <class Signature, class DefaultCreator, class DefaultDeleter, class ManagementPolicy = detail::NoManagemetPolicy> class GenericAbstractFactory : public ManagementPolicy { typedef typename boost::function_types::result_type< Signature >::type ResultType; typedef typename boost::mpl::if_< typename boost::is_pointer<ResultType>::type, ResultType, typename boost::add_reference<ResultType>::type >::type DeleterArg; public: typedef Signature CreatorSignature; typedef void DeleterSignature(DeleterArg); typedef boost::function<CreatorSignature> Creator; typedef boost::function<DeleterSignature> Deleter; private: const static unsigned arity = Creator::arity; private: Creator theCreator; Deleter theDeleter; GenericAbstractFactory() : theCreator(DefaultCreator()) , theDeleter(DefaultDeleter()) {} public: static GenericAbstractFactory& instance() { static GenericAbstractFactory theFactory; return theFactory; } void replace(Creator const& newCreator) { theCreator = newCreator; } void replace(Creator const& newCreator, Deleter const& newDeleter) { theCreator = newCreator; theDeleter = newDeleter; } //2) BOOST_PP_REPEAT(BOOST_PP_INC(UTIL_GENERICFACTORY_MAX_ARITY), DECLARE_CREATE, ~) void destroy(DeleterArg r) { ManagementPolicy::preDestruction(r); theDeleter(r); ManagementPolicy::postDestruction(); } }; #undef FORWARD_PARAM #undef DECLARE_PARAM #undef TEMPLATE_HEADER #undef EMPTY #undef MULTIPLY #undef FACTOR #undef DECLARE_CREATEDas Makro 2) sorgt dafür, dass das Makro 1) Mit Argumenten 0..N aufgerufen wird, was wiederum jeweils ein Methodentemplate definiert. Beispiel: (Für n=2):
template <class T0, class T1> ResultType create(T0&& t0, T1&& t1) { static_assert(2 <= arity, "Too many arguments!"); static_assert(2 >= arity, "Not enough arguments!"); ManagementPolicy::preCreation(); ResultType r = theCreator(std::forward<T0>(t0), std::forward<T1>(t1)); ManagementPolicy::postCreation(r); return r; }Dabei sorgt das static_assert dafür, dass tatsächlich nur die create-Methode instantiiert werden kann, die genau so viele Argumente hat wie die Signatur.
Ein kleineres Problem ahbe ich jetzt noch:
Ich habe eine Factory, deren Signatur ein std::function Objekt enthält:typedef std::function<void(ServerEvent const&)> ServerEventCallback; enum ConnectionType; typedef util::GenericAbstractFactory< ClientCommunicator*(ServerEventCallback const&, ConnectionType), DefaultCommunicatorCreator, DefaultCommunicatorDeleter, SingleProductPolicy > CommunicatorFactory;Natürlich kann ich jetzt an Stelle eines ServerEventCallback Objektes alles übergeben, was konvertierbar ist, z.B.
struct MyCallback { void operator()(ServerEvent const&) {}; } cb; /* ... */ CommunicatorFactory::instance().create(cb, SOME_CONN_TYPE);Ein Problem bekomme ich allerdings, wenn ich einen einfachen Funktionspointer übergebe:
void dummyCallback(ServerEvent const&) {} /* ... */ CommunicatorFactory::instance().create(dummyCallback, SOME_CONN_TYPE);3>f:\cardfw\src\cpp\test\communication_tests\test_communicatorfactory.cpp(38): error C2664: 'ClientCommunicator *util::GenericAbstractFactory<Signature,DefaultCreator,DefaultDeleter,ManagementPolicy>::create<void(const ServerEvent&), ConnectionType_t::type>(T0 (__cdecl &&),T1 &&)' : cannot convert parameter 1 from 'void (__cdecl *)(const cardfw::communication::ServerEvent &)' to 'void (__cdecl &&)(const cardfw::communication::ServerEvent &)' 3> with 3> [ 3> Signature=ClientCommunicator *(const ServerEventCallback &, ConnectionType), 3> DefaultCreator=DefaultCommunicatorCreator, 3> DefaultDeleter=DefaultCommunicatorDeleter, 3> ManagementPolicy=SingleProductPolicy, 3> T0=void (const ServerEvent &), 3> T1=ConnectionType_t::type 3> ] 3> None of the functions with this name in scope match the target type 3>Ich kann jetzt natürlich den funktionszeiger explizit auf ServerEventCallback casten (was ich auch mache), allerdings ist das lästig. Kann man das irgendwie umgehen?
-
pumuckl schrieb:
#define FORWARD_PARAM(z, n, unused) \ std::forward<T##n>(t##n) [...] #undef FROWARD_PARAMTippfehler.
-
Michael E. schrieb:
Tippfehler.
Danke, habs korrigiert.