Class for static maps

G

Grey Plastic

I have a program where several classes each have a single static
std::map to keep track of all the instances of that class. For each
of these classes, I want a static lookupByID(int id) method which
returns a pointer to the instance that the id references... e.g...

Item * Item::lookupByID(Uint32 id);
Level * Level::lookupByID(Uint32 id);
Lifeform * Lifeform::lookupByID(Uint32 id);

.... etc. Instead of rewriting the same code for each of these
classes, I instead wrote an Indexable class...

template <class Child> class Indexable {
private:
static std::map<Uint32,Child*> mapByID;
static Uint32 _nextID;
Uint32 _id;
public:
static Child * lookupByID(Uint32 id) { return mapByID[id]; }
Indexable() { _id=_nextID++; mapByID[_id]=(Child*)this; }
~Indexable() { mapByID[_id]=NULL; }
Uint32 id() const { return _id; }
};

and made each of my other classes subclass Indexable...

class Item : public Indexable<Item> { ... };
class Level : public Indexable<Level> { ... };
class Lifeform : public Indexable<Lifeform> { ... };

This achieves what I've wanted, but it still seems a bit weird to me.
How would you guys handle this situation?
 
J

John Harrison

Grey Plastic said:
I have a program where several classes each have a single static
std::map to keep track of all the instances of that class. For each
of these classes, I want a static lookupByID(int id) method which
returns a pointer to the instance that the id references... e.g...

Item * Item::lookupByID(Uint32 id);
Level * Level::lookupByID(Uint32 id);
Lifeform * Lifeform::lookupByID(Uint32 id);

... etc. Instead of rewriting the same code for each of these
classes, I instead wrote an Indexable class...

template <class Child> class Indexable {
private:
static std::map<Uint32,Child*> mapByID;
static Uint32 _nextID;
Uint32 _id;
public:
static Child * lookupByID(Uint32 id) { return mapByID[id]; }
Indexable() { _id=_nextID++; mapByID[_id]=(Child*)this; }
~Indexable() { mapByID[_id]=NULL; }
Uint32 id() const { return _id; }
};

and made each of my other classes subclass Indexable...

class Item : public Indexable<Item> { ... };
class Level : public Indexable<Level> { ... };
class Lifeform : public Indexable<Lifeform> { ... };

This achieves what I've wanted, but it still seems a bit weird to me.
How would you guys handle this situation?

It's a well known technique. Make a base class aware of a derived class by
passing the derived class as a template parameter to the base class.
Congratulations on discovering it yourself I'd say.

john
 
S

Sharad Kala

John Harrison said:
Grey Plastic said:
I have a program where several classes each have a single static
std::map to keep track of all the instances of that class. For each
of these classes, I want a static lookupByID(int id) method which
returns a pointer to the instance that the id references... e.g...

Item * Item::lookupByID(Uint32 id);
Level * Level::lookupByID(Uint32 id);
Lifeform * Lifeform::lookupByID(Uint32 id);

... etc. Instead of rewriting the same code for each of these
classes, I instead wrote an Indexable class...

template <class Child> class Indexable {
private:
static std::map<Uint32,Child*> mapByID;
static Uint32 _nextID;
Uint32 _id;
public:
static Child * lookupByID(Uint32 id) { return mapByID[id]; }
Indexable() { _id=_nextID++; mapByID[_id]=(Child*)this; }
~Indexable() { mapByID[_id]=NULL; }
Uint32 id() const { return _id; }
};

and made each of my other classes subclass Indexable...

class Item : public Indexable<Item> { ... };
class Level : public Indexable<Level> { ... };
class Lifeform : public Indexable<Lifeform> { ... };

This achieves what I've wanted, but it still seems a bit weird to me.
How would you guys handle this situation?

It's a well known technique. Make a base class aware of a derived class by
passing the derived class as a template parameter to the base class.
Congratulations on discovering it yourself I'd say.

And it's called the "Curiously recurring template pattern (CRTP)".
Also as you have correctly noted that even though the base class depends
on the derived class, it cannot do so in a way that requires the complete type
of derived to be known. That means you can refer to Child*/Child& but not Child
in the base class.

-Sharad
 
J

John Harrison

Sharad Kala said:
John Harrison said:
Grey Plastic said:
I have a program where several classes each have a single static
std::map to keep track of all the instances of that class. For each
of these classes, I want a static lookupByID(int id) method which
returns a pointer to the instance that the id references... e.g...

Item * Item::lookupByID(Uint32 id);
Level * Level::lookupByID(Uint32 id);
Lifeform * Lifeform::lookupByID(Uint32 id);

... etc. Instead of rewriting the same code for each of these
classes, I instead wrote an Indexable class...

template <class Child> class Indexable {
private:
static std::map<Uint32,Child*> mapByID;
static Uint32 _nextID;
Uint32 _id;
public:
static Child * lookupByID(Uint32 id) { return mapByID[id]; }
Indexable() { _id=_nextID++; mapByID[_id]=(Child*)this; }
~Indexable() { mapByID[_id]=NULL; }
Uint32 id() const { return _id; }
};

and made each of my other classes subclass Indexable...

class Item : public Indexable<Item> { ... };
class Level : public Indexable<Level> { ... };
class Lifeform : public Indexable<Lifeform> { ... };

This achieves what I've wanted, but it still seems a bit weird to me.
How would you guys handle this situation?

It's a well known technique. Make a base class aware of a derived class by
passing the derived class as a template parameter to the base class.
Congratulations on discovering it yourself I'd say.

And it's called the "Curiously recurring template pattern (CRTP)".
Also as you have correctly noted that even though the base class depends
on the derived class, it cannot do so in a way that requires the complete type
of derived to be known. That means you can refer to Child*/Child& but not Child
in the base class.

-Sharad

I don't think that's right. How about this?

template <class Rep>
class RCObject
{
friend class RCPtr<Rep>;
public:
RCObject() : _ref(0) {}
RCObject(const RCObject<Rep>&) : _ref(0) {}
RCObject<Rep>& operator=(const RCObject<Rep>&) {}
~RCObject() { assert(_ref == 0); }
Rep* clone() const { return new Rep(*static_cast<const Rep*>(this)); }
private:
size_t _ref;
};

RCObject is a base class for intrusively reference counted objects. It
defines a clone method that calls the derived class copy ctor and therefore
needs to have the complete type.

I think the point is that Rep needs to be known when RCObject is
instantiated not when it is compiled.

john
 
S

Sharad Kala

I think the point is that Rep needs to be known when RCObject is
instantiated not when it is compiled.
True, I should I have been clearer.
The error will come during the instantiation of the base class.

In this code -

template<typename T>
struct B {
T p;
};

struct D: B<D> { // CRTP
};
int main(){
}

When B tries to get instatntiated with T=D, compiler would throw hands saying
B<T>::p has incomplete type.

Best wishes,
Sharad
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top