/* Author: Marco Costalba (C) 2007 This is a test program to showcase a simple but powerful object factory. This version requires boost::fusion library */ #include "simple_factory.hpp" #include <iostream> #include <string> using std::cout; using std::endl; using namespace simple_factory; /* Some stuff we will use in this demonstration */ class ParentClass {}; class BaseClass { public: BaseClass() {} BaseClass(ParentClass*) {} virtual ~BaseClass() {} }; class DerivedClass : public BaseClass { public: DerivedClass() {} DerivedClass(int) {} DerivedClass(int, double, std::string) {} DerivedClass(ParentClass*) : customCreatorWasHere(false) {} bool customCreatorWasHere; }; DerivedClass* customCreator(ParentClass* p) { DerivedClass* d = new DerivedClass(p); d->customCreatorWasHere = true; return d; } class OtherClass { public: OtherClass() {} OtherClass(int& cnt) { cnt++; } DerivedClass* operator()(ParentClass* p) { return customCreator(p); } }; /* First (and only) thing to do is to 'register' a class with a * factory using the function (actually a static method) * * bool Factory<Base>::register_class<Derived*(Arg1, ..., ArgN)>(name) * * where 'Derived' is the class to register, and 'Base' is the base * class of our hierarchy. We can have more hierarchies at the * same time. More then one class constructor can be registered, use * types arg1, arg2,..argN to pass the signature of the requested ctor. * If not given default ctor will be assumed. * * Finally parameter 'name' is a class identifier, i.e. a string with the * name we will use to query the factory for an object of 'Derived' type. * Normally it's the class name. * * We can register a class with a factory anywhere in a program, * both in file scope (will be registered at startup with static * data), as we see here, or in a function body (will be registered * when the function is called). * * the following two definitions will teach 'BaseClass' factory * how to create a 'BaseClass' and a 'DerivedClass' */ static bool s1 = Factory<BaseClass>::register_class<BaseClass*()>("Base"); static bool s2 = Factory<BaseClass>::register_class<DerivedClass*()>("Derived"); /* Of course you can (better) use an anonymous namespace in * implemantation files and avoid the 'static' qualifier */ namespace { // re-registering the same class with the same name (correctly) fails! bool s3 = Factory<BaseClass>::register_class<BaseClass* ()>("Base"); // s3 == false } int main(int argc, char* argv[]) { // just after a class has been registered we can start getting objects BaseClass* b = Factory<BaseClass>::object("Base"); // that's all, check if all was ok if (dynamic_cast<BaseClass*>(b)) cout << "\nIt's a BaseClass object\n"; // we can have objects from many hierarchies, // just need to registering with proper factory... Factory<OtherClass>::register_class<OtherClass* ()>("Other"); // ...and asking the object OtherClass* o = Factory<OtherClass>::object("Other"); if (dynamic_cast<OtherClass*>(o)) cout << "It's an OtherClass object\n"; // we can use any string expression to pass the class name, // the string value can be also unknown at compile time, well // that's the real meaning of using all this factory stuff. // As example we could load the class name from a file std::string myName("Der"); b = Factory<BaseClass>::object(myName + "ived"); if (dynamic_cast<DerivedClass*>(b)) cout << "It's a DerivedClass object\n"; // we can use any alias when registering a class with a // factory, in this case we call 'myClass' a BaseClass class Factory<BaseClass>::register_class<BaseClass*(ParentClass*)>("myClass"); // the second template argument 'ParentClass*' is the type of // the argument we will pass to our object constructor. It's // optional, if not given, as seen until now, the default // object constructor will be called // // after registration we can create a 'myClass' object // anywhere in the program. Here we also pass an argument // to our object constructor, in this case a pointer to a // ParentClass object called 'parent' ParentClass parent; b = Factory<BaseClass>::object("myClass", &parent); if (dynamic_cast<BaseClass*>(b)) cout << "It's a BaseClass object\n"; // we can use a custom create function instead of the default one Factory<BaseClass>:: register_class<DerivedClass*(ParentClass*)>("Derived", &customCreator); // now any new DerivedClass called with 'ParentClass*' argument will be // created with our customCreator() function. See how we pass the // mandatory argument 'parent' to feed customCreator(), failing to do // is safely detected at runtime b = Factory<BaseClass>::object("Derived", &parent); DerivedClass* d = dynamic_cast<DerivedClass*>(b); if (d->customCreatorWasHere) cout << "customCreator() was used\n"; // finally we can unregister a class from a factory Factory<OtherClass>::unregister("Other"); o = Factory<OtherClass>::object("Other"); if (!o) cout << "Class 'Other' unregistered\n"; // Ok, and now lets start with the fancy stuff! // as starter we try to pass the wrong type to 'DerivedClass' // constructor, we pass a 'OtherClass*' instead of a // 'ParentClass*' and see what's happens... OtherClass argumentWithWrongType; b = Factory<BaseClass>::object("Derived", &argumentWithWrongType); // ...it is detected at runtime! if (!b) cout << "Wrong argument type detected!\n"; // we want the factory to create a DerivedClass object // passing an integer as constructor argument Factory<BaseClass>::register_class<DerivedClass*(int)>("Derived"); b = Factory<BaseClass>::object("Derived", 7); if (dynamic_cast<DerivedClass*>(b)) cout << "c'tor DerivedClass(7) called\n"; // until now nothing new, but try to ask another 'Derived' object // always with one argument, this time with type 'ParentClass*' b = Factory<BaseClass>::object("Derived", &parent); if (dynamic_cast<DerivedClass*>(b)) cout << "c'tor DerivedClass(parent) called\n"; // also this works! so we have here a ctor overload for "Derived" // we can register as many overloads as we need, they all will be // nicely handled (and type checked) by our factory // // each overload can be also individually unregistered, // as example now we remove DerivedClass(int) ctor Factory<BaseClass>::unregister<BaseClass*(int)>("Derived"); b = Factory<BaseClass>::object("Derived", 7); if (!b) cout << "Derived(int) ctor unregistered but..."; // only ctor Derived(int) has been removed b = Factory<BaseClass>::object("Derived", &parent); if (dynamic_cast<DerivedClass*>(b)) cout << "DerivedClass(parent) still available\n"; // we can also pass an argument by reference as example to // be modified by object ctor. As usual first register... Factory<OtherClass>::register_class<OtherClass*(int)>("Other"); int objCnt = 6; // ...then ask for the object, in this case explicitly stating // argument is passed by reference with 'object<int&>' o = Factory<OtherClass>::object<int&>("Other", objCnt); cout << "Integer 'objCnt' incremented to " << objCnt << endl; // we can also use a functor object instead of a function // as our custom creator, as example an 'OtherClass' object, // that is also a functor. As always first unregister current one... Factory<BaseClass>::unregister<DerivedClass*(ParentClass*)>("Derived"); // ...then register our new creator and get an object Factory<BaseClass>::register_class<DerivedClass*(ParentClass*)>("Derived", o); d = dynamic_cast<DerivedClass*>(Factory<BaseClass>::object("Derived", &parent)); if (d->customCreatorWasHere) cout << "custom functor object was used\n"; // delete and remove some compiler warning noise delete b; delete o; s1 = s2 = s3 = false; /* So the bottom line is: - Support for many class hierarchies at the same time - Generic constructor argument type or default c'tor - Type safe constructor overloading - Support for user supplied 'object create' functions - Dynamic class registering/unregistering - And finally the most important: 100% type safe also at runtime! Known issues: - Currently key type it's a std::string. It could be easily converted to a generic type but for now is better to keep the code simple - If c'tor type is a pointer, Factory::object() fails to detect the correct type when a NULL value is passed: ParentClass parent, *p = NULL; b = Factory<BaseClass>::object("Derived", &parent); // OK b = Factory<BaseClass>::object("Derived", p); // OK b = Factory<BaseClass>::object<ParentClass*>("Derived", NULL); // OK b = Factory<BaseClass>::object("Derived", NULL); // Compile time warning! - Currently when calling Factory<>::object(name, ctor_arg); the argument 'ctor_arg' is passed by value so this leads to one extra copy in case argument is of a non-POD type. In case argument should be passed by reference this can also have the side effect that 'ctor_arg' is not updated by object constructor that instead updates a copy of 'ctor_arg'. workaround is to explicitly indicate we want to pass 'ctor_arg' by reference using the object<> template parameter: b = Factory<BaseClass>::object<Parent&>("Derived", p); // passing non-POD types b = Factory<BaseClass>::object<int&>("Derived", objCnt); // passing references */ return 0; }