/* Author: Marco Costalba (C) 2007 This is a test program to showcase a simple but powerful object factory. There are no external library requirements but a string class and a multi-valued associative container. All remaining stuff is pure C++. No RTTI or other meta-info feature is required. There is only one (small) 'simple_factory.hpp' file to be included in implementation files where factory is actually used. To compile: g++ -O2 -o factory main.cpp */ #include "simple_factory.hpp" #include <iostream> #include <string> using std::cout; 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(ParentClass*) : customCreatorWasHere(false) {} bool customCreatorWasHere; }; class OtherClass { public: OtherClass() {} OtherClass(int& cnt) { cnt++; } }; /* 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>(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. The third 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 // we can (optionally) provide our custom 'create' function, see later DerivedClass* customCreator(ParentClass* p) { DerivedClass* d = new DerivedClass(p); d->customCreatorWasHere = true; return d; } } 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 always redefine an object creator, as example to set // our custom create function instead of the default one. Note // how we pass a second template parameter that is the type of // the argument required by our customCreator() function. // Failing to do so results in a (safe) compile time error Factory<BaseClass>:: register_class<DerivedClass, ParentClass*>("Derived", &customCreator); // now any new DerivedClass 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<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 o = Factory<OtherClass>::object<int&>("Other", objCnt); cout << "Integer 'objCnt' incremented to " << objCnt << "\n"; // delete and remove some compiler warning noise delete b; delete d; 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 only default (no argument) and one argument c'tors are supported, tough is easily extendible to many args c'tors - 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 - Currently custom creator is a function with a given signature. It would be nice to extend to any callable entity. I still didn't figured out how to do it preserving the current user syntax - 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; }