Sign in to follow this  
Timkin

Pluggable factories and object initialisation

Recommended Posts

So, I've got a pluggable factory that can construct objects within a given heirarchy, but I have an issue with initialising derived objects within this heirarchy. Here's the source code for my factory class
// Factory.h
#ifndef FACTORY_H
#define FACTORY_H

#include <map>
#include "ObjectTypes.h"

template <typename BASE_TYPE>
class Factory
{
  typedef std::map<OBJ_TYPE,Factory*> FactoryRegistry;
  static FactoryRegistry& Registry();
  virtual BASE_TYPE* Create() const = 0;
public:
  Factory(OBJ_TYPE type);
  static BASE_TYPE* Construct(OBJ_TYPE type);
};

template <typename BASE_TYPE>
Factory<BASE_TYPE>::Factory(OBJ_TYPE type)
{
  Registry().insert(std::make_pair(type,this));
}

template <typename BASE_TYPE>
typename Factory<BASE_TYPE>::FactoryRegistry& Factory<BASE_TYPE>::Registry()
{
  static Factory<BASE_TYPE>::FactoryRegistry  m_registry;
  return m_registry;
}

template <typename BASE_TYPE>
BASE_TYPE* Factory<BASE_TYPE>::Construct(OBJ_TYPE type)
{
  Factory<BASE_TYPE>::FactoryRegistry::iterator iter = Registry().find(type);
  if (iter != Registry().end())
  {
    Factory<BASE_TYPE>* pDerivedFactory = iter->second;
    return pDerivedFactory->Create();
  }
  else
    return NULL;
}

#endif

and here's an object and its factory...
// AnObject.h

#ifndef ANOBJECT_H
#define ANOBJECT_H

#include "Factory.h"
#include "BaseTypes.h"

class AnObject : public BaseObject
{
  int x,y;
public:
	AnObject():BaseObject(BaseObject::NextValidID()){
		SetType(TYPE_1);
	}
  void Initialise(int a, int b){x=a;y=b;}
};

class AnObjectFactory : public Factory<BaseObject>
{
public:
  AnObjectFactory():Factory<BaseObject>(TYPE_1){}
private:
  BaseObject* Create() const;
  static const AnObjectFactory register_this;
};

#endif

// AnObject.cpp
#include "AnObject.h"

#include <iostream>

const AnObjectFactory AnObjectFactory::register_this;

BaseObject* AnObjectFactory::Create() const
{
  AnObject* l_pObj = NULL;
  try
  {
    l_pObj = new AnObject;
  }
  catch(std::bad_alloc& ba)
  {
	  std::cout << ba.what() << std::endl;
  }
  return l_pObj;
}

and another object and factory
// AnotherObject.h
#ifndef ANOTHEROBJECT_H
#define ANOTHEROBJECT_H

#include "Factory.h"
#include "BaseTypes.h"

class AnotherObject : public BaseObject
{
  float x,y,z;
public:
	AnotherObject():BaseObject(BaseObject::NextValidID()){
		SetType(TYPE_2);
	}
  void Initialise(float a, float b, float c){x=a;y=b;z=c;}
};

class AnotherObjectFactory : public Factory<BaseObject>
{
public:
  AnotherObjectFactory():Factory<BaseObject>(TYPE_2){}
private:
  BaseObject* Create() const;
  static const AnotherObjectFactory register_this;
};

#endif

#include "AnotherObject.h"

#include <iostream>

const AnotherObjectFactory AnotherObjectFactory::register_this;

BaseObject* AnotherObjectFactory::Create() const
{
  AnotherObject* l_pObj = NULL;
  try
  {
    l_pObj = new AnotherObject;
  }
  catch(std::bad_alloc& ba)
  {
	  std::cout << ba.what() << std::endl;
  }
  return l_pObj;
}

Here's the base type from which these types are derived...
// BaseTypes.h

#ifndef BASETYPES_H
#define BASETYPES_H

#include "ObjectTypes.h"

typedef unsigned int ID_TYPE;

class BaseObject
{
	ID_TYPE  m_uID;
	OBJ_TYPE m_eType;

	static ID_TYPE m_uNextValidID;

	void SetID(ID_TYPE l_uID);

protected:
	void SetType(OBJ_TYPE type);
public:
	BaseObject(ID_TYPE l_uID);

	static ID_TYPE NextValidID();

	ID_TYPE ID(){ return m_uID; }
	OBJ_TYPE TYPE(){ return m_eType; }
};

#endif

#include "BaseTypes.h"


ID_TYPE BaseObject::m_uNextValidID = 0;

BaseObject::BaseObject(ID_TYPE l_uID)
{
	SetType(TYPE_DEFAULT);
	SetID(l_uID);
}

void BaseObject::SetType(OBJ_TYPE type)
{
	m_eType = type;
}


void BaseObject::SetID(ID_TYPE l_uID)
{
	m_uID = l_uID;
	m_uNextValidID++;
}


ID_TYPE BaseObject::NextValidID(void){
	return m_uNextValidID;
}

and finally an enumeration for identifying types...
// ObjectTypes.h

#ifndef OBJECTTYPES_H
#define OBJECTTYPES_H

enum OBJ_TYPE {TYPE_DEFAULT=0,TYPE_1, TYPE_2};

#endif

So, here is some sample usage...
// main.cpp

#include <iostream>

#include "Factory.h"
#include "AnObject.h"
#include "AnotherObject.h"


int main(void)
{
	BaseObject* pObj1 = Factory<BaseObject>::Construct(TYPE_1);
	BaseObject* pObj2 = Factory<BaseObject>::Construct(TYPE_2);

	AnObject* pAnObj1 = reinterpret_cast<AnObject*>(pObj1);
	pAnObj1->Initialise(1,2);

	std::cout << pObj1->TYPE() << ": " << pObj1->ID() << std::endl;
	std::cout << pObj2->TYPE() << ": " << pObj2->ID() << std::endl;

	return 0;
}

Now, the issue I have with this framework is having to use reinterpret_cast<>() to gain access to the Initialise member function of each type. Obviously, if the function signature of the initialisation routine of each derived class was the same, I could provide an abstract interface through the base class... but given that derived classes are expected to have different initialisation requirements and different initialisation lists, how can I get around this? I don't want to have to know the class type beyong the enumeration list... and I'd like to avoid using the 'ellipsis' construct. Any ideas about how I could change this framework to solve this problem? Are there standard solutions out (I've looked but couldn't find any). Thanks, Timkin

Share this post


Link to post
Share on other sites
I've read that article previously and I purposefully wanted to avoid the need to call a 'register' method to register a contructor in the factory. Hence the use of the derived factory technique for self registering during program startup... I was hoping I could handle the initialisation without compiler macros... but perhaps I'll need to...

If anyone has any alternative suggestions, I'm all ears.

Thanks,

Timkin

Share this post


Link to post
Share on other sites
I'm not sure how general you need the solution to be, but why not create a class containing the arguments needed for any derived object's initialization? That would allow you to provide an abstract interface through the base class, since you would just be passing an object of common type across all of your derived classes. You might have to get tricky with the class if you want it to be general and perform well, but it's all I can think of.

Share this post


Link to post
Share on other sites
What are the advantages you see of using a factory here at all?

Share this post


Link to post
Share on other sites
An easy solution is to have a Data base class that gets passed into the Initialize function. The class could contain an identifier for its type. Then add the additional params in the derived class.

-= Dave

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
What are the advantages you see of using a factory here at all?


The source code I posted is ripped out of my game app and rebadged for clarity (I hope) so it's missing lots and the objects are greatly simplified (so it might not give a clear indication of my intent or usage)...

...but why am I looking at a factory pattern? Several reasons:

1) It's to be coupled with my warehouse (a set of repositories for each type in the game, accessed through a common interface), so since the warehouse hides the type behind a handle, it makes sense to do the same for object creation (at least from my perspective... ;) )

2) Because of the nature of the game, objects can be created by other objects and are destroyed through interaction with certain objects. It makes sense for object creation, storage and deletion to be managed.

3) The code is to be used and modified by someone else who knows almost no C++, so hiding some of the complexities of object management behind a simple class interface will make it easier for them to use this functionality without having to fully understand how it is achieved. My students will also be using the app and while at this stage they're not modifying this part of it, they may need to in the future... most of them don't know C++ before taking the course for which this game is used, so they're in the same boat.

If there is a better approach, I'm all ears. 8)

Cheers,

Timkin

Share this post


Link to post
Share on other sites
Quote:
Original post by Timkin
1) It's to be coupled with my warehouse (a set of repositories for each type in the game, accessed through a common interface), so since the warehouse hides the type behind a handle, it makes sense to do the same for object creation (at least from my perspective... ;) )
That seems reasonable.

Quote:
2) Because of the nature of the game, objects can be created by other objects and are destroyed through interaction with certain objects. It makes sense for object creation, storage and deletion to be managed.
Smart pointers (ideally, hidden behind a per-class static Create method, with constructors private) are a good way to handle this.

Okeydoke, from point 1 it seems a good idea to have a factory class. But that doesn't mean that the factory should only have one creation method. The fact that different objects will have different creation lists suggests to me that the user functions which call the factory have a pretty good idea of the sort of thing they're creating. So I'd suggest a factory with a different creation method for each type. If several types have the same initialization interface and are logically related it might be reasonable to group those particular ones behind the same creation method; mixing the two approaches is often the best solution.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
But that doesn't mean that the factory should only have one creation method. The fact that different objects will have different creation lists suggests to me that the user functions which call the factory have a pretty good idea of the sort of thing they're creating. So I'd suggest a factory with a different creation method for each type. If several types have the same initialization interface and are logically related it might be reasonable to group those particular ones behind the same creation method; mixing the two approaches is often the best solution.


I'd advocate one interface for creation and an abstraction for the arguments.

1) A factory is a lower level library then the type of objects it creates. That is, objects depend on the factory for creation and the factory should have no knowledge of the objects its creating. If it did it would create a circular dependency.

2) Going with a creation method per object type will prevent any data driven solution in the future. It will always involve adding code to the factory to create new object types, this prevents abstraction for other scripting languages or tools to use.

3) Also, a smaller note but important in larger projects, changing the header file requires recompiling all .cpp files that use that header. On our current project we 100's of object types that use factories and having to change the interface every time we add a new parameter for an object type would hurt productivity.

With our solution we have a clear abstraction from objects and factories. The factory worries about memory management such as memory space to allocate from, reference counting, allocation stratigies(pools etc.). It constructs the object correctly, with arguments, and destroys it correctly all in a data driven manner that allows designers to construct new types of objects with no programmer intervention

-= Dave

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
So I'd suggest a factory with a different creation method for each type. If several types have the same initialization interface and are logically related it might be reasonable to group those particular ones behind the same creation method; mixing the two approaches is often the best solution.


Wouldn't that be functionally equivalent to instantiating factories further down the object hierarchy (so each factory becomes specialised on a derived class subtree)? For example, if I had

BaseObject
^ ^
| |
-------- --------
| |
Dynamic Static
Object Object

and presuming all DynamicObjects have the same initialisation interface and all StaticObjects had the same initialisation interface, then I could instantiate Factory<DynamicObject> and Factory<StaticObject>.

While this is workable (and I'd indeed considered this), it still places a requirement on knowing the different types that are in existence in terms of the actual class hierarchy, rather than an abstract token, such as a handle. Furthermore, there is a distinct risk of having to instantiate a Factory on a single derived class because its interface is unique.

One requirement I have for this is extensibility of the code and app. Presently we're talking about the creation of a set of objects that I know about. However, these objects are created by joining certain sorts of attribute objects together in a plug-and-play context. I'd like to be able to provide extensibility so that later new objects can be defined by new combinations of attributes and that the interface to this should be rather script-like.

Perhaps I'm approaching this in the wrong way. My intuition and experience tells me that if you need to hack the tool to make it work on a given problem, then either the problem is poorly formed or the tool is wrong for the job. Perhaps, rather than looking at alternative tools, I should look at alternative ways to describe the problem?

Dave, you wrote:
Quote:

I'd advocate one interface for creation and an abstraction for the arguments.

If you can spare a few minutes, could you elaborate on this please? Perhaps with a code snippet or some rough outlining?

Thanks for the help guys... 'tis appreciated.

Cheers,

Timkin

Share this post


Link to post
Share on other sites
Quote:
Original post by Timkin
Dave, you wrote:
Quote:

I'd advocate one interface for creation and an abstraction for the arguments.

If you can spare a few minutes, could you elaborate on this please? Perhaps with a code snippet or some rough outlining?


Sure,

The goal of our system is to allow one interface for creation of objects. I define creation of an object to be its allocation strategy and construction of the object via its parameters.

For each object there needs to be a factory associated with it. The factory will handle the details of allocating and creating the object. There will also need to be one access point to a list of all factories. I'll refer to this registry as the pluggable factory. Think of it as a factory for factories.

Register a factory

To register the factory it will need some sort of identifier to determine the type of factory it is. We create a base class called CFactoryBase and this will simply contain the identifier and perhaps category.

class CFactoryBase {
public:
unsigned int identifier;
unsigned int category;
virtual CObject* CreateInstance(CMessage* data) = 0;
};

With this class we can register it into our pluggable factory and through out the program query a factory from it. The pluggable factory should have a sorted table (perhaps a key table for efficiency) to query the factory from.

class CPluggableFactory {
protected:
CKeyTable<CFactoryBase*> factories;
public:
// -- interface
void RegisterFactory(CFactoryBase* factory, unsigned int cat, unsigned int id);
};

Lastly, to make it easy to register the factory you can write a templated factory installer class that in its constructor makes a call to the pluggable factory to register itself. This lets you put in the .cpp file of a call like

CFactoryInstaller<CMyEnemy> enemy;

That line of code will do the registration of the object for you.

Creating an object

The derived factory class will extend the class by providing the implementation of a generation method to create the object. In the factory base class there should be an interface for setting the allocation strategy(pooled, mem space, etc.)

template <typename T> class CFactory : public CFactoryBase {
public:

// -- generation method
virtual CObject* CreateInstance(CMessage* data = NULL);
};

Based on the strategy you can call different allocation methods. Also, the CMessage class will handle the type of data you want to pass in. This will allow as many arguments as you want. It is up to the caller to generically set the arguments. You can extend this class to be as dynamic about its parameters as you want. With the simplest implementation you could have a couple fields for data. For example, int data0, data1, data2, data3 that would allow you to do client side code like this

CMessage msg;
SComponentInit init;
init.actor = this;
init.keyname = keyname;
init.prefix = prefix;
msg.SetData0(recast_<int32>(&init));

// -- create and install a new component
CComponent* component = stcast_<CComponent*>(CPluggableFactory::CreateInstance(
CComponent::eCategory, identity, &msg));

As you can see, you pass a pointer around in the first data field. If you want, you can also derive a message and provide more type safe information. If you want to extend this so a scripting language can support arguments you generate your data with a schema and data. It's all how far you want to take it but really it doesn't affect your Factory. All it knows is that its getting a base class pointer that it will pass to the Object's initialize function.

This is a basic outline to set it up. This is all pretty low level but it adds enough flexibility that you can write template wrapper classes for your students to use to pretty up the interface.

Hope this clarifies.

-= Dave

Share this post


Link to post
Share on other sites
Hehe... thanks for taking the time to write that Dave... it's actually very similar to what I posted in my original post (in the source boxes). The only significant difference being that I'm using a static constructor function and static registry in my pluggable factory, as well as self-registration of derived factories using a static const member of the derived factory (essentially I'm enforcing a singleton pattern for access to a set of factories).

Your solution of casting pointers is also what I presented in my original post for gaining access to the derived objects interface and this is what I was hoping to avoid... ;) It doesn't look like I'll be able to, so maybe I should just concentrate on cleaning up the construction and initialisation interface to minimise confusion.

The one benefit of your method of using the data message to initialise the object is, as I see it, that you need only expose a single initialisation interface for each object you want to create, which is fairly easily documented.

Cheers,

Timkin

Share this post


Link to post
Share on other sites
Quote:
Original post by Timkin
Your solution of casting pointers is also what I presented in my original post for gaining access to the derived objects interface and this is what I was hoping to avoid... ;) It doesn't look like I'll be able to, so maybe I should just concentrate on cleaning up the construction and initialization interface to minimise confusion.

If you are worried about the cast you can write a wrapper to hide the cast in a templated class. That's what we do.

Though, the cast(hidden or not) will be needed to do anything useful with the class. When you request an object you are requesting an interface to operate on that object. This means you need to know the interface and you need to have type information. It needs to be downcast so you can actually use the class.

Quote:
The one benefit of your method of using the data message to initialize the object is, as I see it, that you need only expose a single initialization interface for each object you want to create, which is fairly easily documented.


Right, all objects would need to provide an Init and Shutdown functions that would have a message passed into it. Abstracting the arguments will be very important if you want to provide a data driven solution.

-= Dave

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this