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 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 friend class RegisterTemplate;
typedef std::unique_ptr SelfPtr;
typedef std::map 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
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
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
struct register_all {
register_all () {
RegisterTemplate(X)>::register_type();
register_all();
}
};
template <> struct register_all {};
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::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.