The PAL abstract pluggable factory is responsible for constructing the correct objects when the application developer issues a create object call. The factory maintains a registry (MAP <STRING, FactoryObject<FactoryBase>*> mRegistry
) that connects a descriptive string with a basic FactoryObject that will allow construction of a requested object.
Each factory object has a cloning function virtual FactoryObject* Create() = 0;
that allows a new copy of the object to be created. Registration information is also provided by each factory object providing a unique name for the object, the name of the type of object that it provides the functionality for, the group of objects to which it belongs, and finally a version number.
class RegistrationInfo {
public:
STRING mClassName;
STRING mGroupName;
STRING mUniqueName;
unsigned long mVersion;
...
};
For example, an implementation of the Box object for the ODE engine could have its mUniqueName as palODEBox, its mClassName as palBox, and its group name and version as ODE, 1. A Tokamak implementation of the box could then be defined by a mUniqueName as palTokamakBox, its mClassName as palBox, and its group name and version as Tokamak, 1.
This registration information is stored by the factory in an array (VECTOR<RegistrationInfo<FactoryBase> >
). When a certain group of objects is selected (eg: ODE) then the registry is populated with the correct set of objects from the registration information.
The PAL factory can load objects either from a static registry or from dynamic objects (ie: DLLs). For static inclusion a default constructor is provided that will construct a static object that registers its information. For dynamic inclusion the factory scans the dynamic objects (DLL's) in the current directory to see if they support factory objects. This behaviour is defined by a number of different defines with some default macros.
^ topWhen creating an object that should be available to the factory two macros must be defined. In the definition of the factory object the objects registration information must be include. For example, if we are implementing a Box for the Newton engine, then within the interface (.h) for the Newton implementation of the box we provide:
FACTORY_CLASS(palNewtonBox,palBox,Newton,1)
And within the implementation (.cpp) we define:
FACTORY_CLASS_IMPLEMENTATION(palNewtonBox);
The first macro expands to provide a custom constructor that includes the object registration information and registers this information with a factory. This is why each factory object requires a default constructor to be implemented. The second macro provides the implemented behaviour for the factory object.
For static objects, the FACTORY_CLASS_IMPLEMENTATION simply creates a uniquely named static instance of the object that will register itself with the current factory. For dynamic objects, this expands to provide the functionality required to enable an external program to access the objects.
For dynamic functionality a group of objects can be included in one implementation file. This behaviour is selected by defining DLL_GROUP_IMPLEMENTATION. Additionally two macros need to be placed around implemented objects:
FACTORY_CLASS_IMPLEMENTATION_BEGIN_GROUP;
....
FACTORY_CLASS_IMPLEMENTATION_END_GROUP;
Alternatively if only one object is implemented then a simple dynamic implementation can be enabled through the use of the DLL_IMPLEMENTATION definition. This functionality is available on Windows and Linux.