I would like to initialize 2 classes(say Class ARegister, Class BRegister) that registers some values(A,B). I want to initialize these two classes from a super(?) class( say Cla
It seems you have already decided on some kind of relationship between your objects. But, you only vaguely describe the relationship.
If RegisterALL
is using simple containment, then you would have a very simple relationship. This relationship might be expressed like this (please excuse the ASCII graphics):
+-------------+
| RegisterALL | --> := has
+-------------+
| |
v v
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
The advantage is that the picture for two dependents is very simple. However, if you are registering many objects, then the picture starts to look like RegisterALL
is exploding into a bunch of XRegister
objects.
If RegisterALL
is meant to contain ARegister
and BRegister
, you might want to create a common base class for ARegister
and BRegister
so that RegisterALL
can maintain a container.
+-------------+ +------------------+ <>--> := aggregates
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+ |
| _/_\_ := inherits
/ \
___/___\___
| |
+-----------+ +-----------+
| ARegister | | BRegister |
+-----------+ +-----------+
We see that no matter how many new items get registered, the relationship between RegisterALL
and AbstractRegister
remains the same. We can go a step further, and derive ARegister
and BRegister
from a template.
+-------------+ +------------------+
| RegisterALL |<>--->| AbstractRegister |
+-------------+ +------------------+
|
/ \
/___\
|
| +--------------+
+--------------| RegisterType |
| +--------------+
| RegisterTemplate |
+------------------+
Okay, so much for the the OO design lesson. This translates to code pretty quickly. Let's start with the easy things. RegisterType
enumerates the different things to register. RegisterTypeName
and the overloaded operator allow the code to print a string instead of a number when printing a RegisterType
.
enum RegisterType { A, B, MAX_RegisterType };
static inline std::string
RegisterTypeName (RegisterType t)
{
static const char * names[] = { "A", "B" };
return names[t];
}
static inline std::ostream &
operator << (std::ostream &output, RegisterType t)
{
return output << RegisterTypeName(t);
}
AbstractRegister
provides an interface to query for this type. In addition, a poke
interface is provided with a default implementation. Note in C++, abstract types should provide a virtual destructor.
class AbstractRegister {
public:
virtual ~AbstractRegister () {}
virtual RegisterType type () const = 0;
virtual void poke () { std::cout << "Poked " << type(); }
};
typedef std::unique_ptr<AbstractRegister> AbstractRegisterPtr;
static const AbstractRegisterPtr AbstractRegisterNullPtr;
The RegisterALL
class has a container to hold things of type AbstractRegister
. It uses a map to associate RegisterType
to the AbstractRegister
instance, which we are taking to be the registration. RegisterALL
is implemented as a singleton, meaning it only allows one instance of itself. The add
method performs the registration, and the find
method allows a registered instance to be found. The implementation of the RegisterALL
constructor is deferred until after the definition of a RegisterTemplate
.
class RegisterALL {
template <RegisterType> friend class RegisterTemplate;
typedef std::unique_ptr<RegisterALL> SelfPtr;
typedef std::map<RegisterType, AbstractRegisterPtr> RegisterMap;
void add (AbstractRegister *r) { all[r->type()] = AbstractRegisterPtr(r); }
RegisterALL ();
public:
static const SelfPtr & instance () {
if (!one) new RegisterALL;
return one;
}
const AbstractRegisterPtr & find (RegisterType t) const {
RegisterMap::const_iterator i = all.find(t);
return (i != all.end()) ? i->second : AbstractRegisterNullPtr;
}
private:
static SelfPtr one;
RegisterMap all;
};
RegisterALL::SelfPtr RegisterALL::one;
The RegisterTemplate
class derives from the AbstractRegister
and is parameterized by a RegisterType
. It implements the type
virtual method by returning the value of its template parameter. It also employs singleton, but it does not make its instance public. Instead, its instance is managed by RegisterALL
. It provides the register_type
method that registers itself with RegisterALL
, and this instance can only be found by using the find
method on RegisterALL
.
template <RegisterType RT>
class RegisterTemplate : public AbstractRegister {
RegisterType type () const { return RT; }
void poke () {
std::cout << "Poked " << RegisterTypeName(RT) << std::endl;
}
RegisterTemplate () {
std::cout << "Created " << RegisterTypeName(RT) << std::endl;
}
~RegisterTemplate () {
std::cout << "Destroying " << RegisterTypeName(RT) << std::endl;
}
public:
static void register_type () {
if (RegisterALL::instance()->find(RT)) {
std::cout << "Already registered " << RegisterTypeName(RT)
<< std::endl;
return;
}
RegisterALL::instance()->add(new RegisterTemplate<RT>);
}
};
The RegisterALL
constructor employs the helper template register_all
that iterates through the RegisterType
enum, and instantiates the corresponding RegisterTemplate
, thereby causing all RegisterType
's to be registered with RegisterALL
.
template <unsigned X>
struct register_all {
register_all () {
RegisterTemplate<static_cast<RegisterType>(X)>::register_type();
register_all<X+1>();
}
};
template <> struct register_all<MAX_RegisterType> {};
inline RegisterALL::RegisterALL ()
{
one = std::move(SelfPtr(this));
register_all<0>();
}
So to try it out, we use the following code:
RegisterALL::instance(); // registers all RegisterType's
RegisterTemplate<B>::register_type(); // try to register B again
RegisterALL::instance()->find(A)->poke(); // poke at A
And this is the output:
Created A
Created B
Already registered B
Poked A
Destroying B
Destroying A
Notice how the smart pointers automatically clean up the registered items for us.
the best way do to this is using CRTP (curiously recurring template pattern), the derived classes ARegister and BRegister pass themselves as template arguments to the base class RegisterALL. It will look like this:
class RegisterAll
{
public:
template<class DerivedType>
DerivedType *createType()
{
RegisterAll *r = (*(m_creators[DerivedType::id]))();
return dynamic_cast<DerivedType *>(r); //static_cast will work too if you didn't make a mistake
}
protected:
static std::map<int,RegisterAll *(*)()> m_creators;
};
std::map<int,RegisterAll *(*)()> RegisterAll::m_creators = std::map<int,RegisterAll *(*)()>();
template<class Derived>
class CRTPRegisterAll : public RegisterAll
{
public:
static bool register()
{
RegisterAll::m_creators.push_back(std::make_pair(Derived::id,Derived::create);
return true;
}
private:
static bool m_temp;
};
template<class Derived>
bool CRTPRegisterAll<Derived>::m_temp = CRTPRegisterAll<Derived>::register();
class RegisterA : public CRTPRegisterAll<RegisterA>
{
private:
static RegisterA *create()
{
//do all initialization stuff here
return new RegisterA;
}
public:
static const int id = 0;
};
Now the initialization of the static variable m_temp
in CRTPRegisterAll
pushes a creator function for each derived type onto RegisterAll
's list. It is generally not very good design to have RegisterAll
know about all the derived classes (it isn't very extensible). This way the actual creation method can be implemented in each derived class and the creator function will be automatically registered in RegisterAll
. One of the main advantages of CRTP is that the derived classes don't need to know how to register themselves to the base class' list, it is done for them. As for error handling that too can be implemented in each derived class' creator functions. There are better ways to handle the id issue but I don't want to get into that here.
If you want a simpler method I suggest reading about the Factory design pattern.