Pluggable Factories (allllmost there..)

Started by
12 comments, last by gimp 23 years, 9 months ago
null_pointer:

The problem with typeid is that I need to have an instance of the type already. I normally use a virtual Clone function (or NewInstance) when I create a new instance of a type that I already have. I only use the factory when I don''t have a prototype for the object I want.
Advertisement
Wilka:

Can''t you access the template parameter? You can use typeid on the class name, not just an instance of the class. Requiring an instance of the class is what you are trying to avoid with templates anyway, right?


template &lttypename command_type>
class command_allocator
{
public:
command_type* allocate()
{ return new command_type; }

string identifier()
{ return string(typeid(command_type).name()); }
};



I''m not sure if this would work correctly as a factory allocator (IMHO, class factories are irritating and used too often to cover bad design, but not always ).

Of course, the allocate function allocates a new object, and the identifier (or call it ID if you like) just returns the class name.

Another thing you could do is this:


#pragma warning(disable: 4786)

#include &ltmap>
#include &ltstring>

namespace creator
{

template &lttypename base_class>
class basic_allocator
{
public:
virtual base_class* allocate() = 0;
virtual std::string identifier() = 0;
};

template &lttypename command_class, typename base_class>
class allocator
: public basic_allocator&ltbase_class>
{
public:
virtual base_class* allocate() { return (base_class*) new command_class; };
virtual std::string identifier() { return std::string(typeid(command_class).name()); }
};

template &lttypename base_class>
class factory
{
public: // make this protected later
std::map< std::string, creator::basic_allocator< base_class >* > allocators;

public:
base_class* create(const std::string& identifier)
{ return allocators[identifier].allocate(); }

void add(basic_allocator* allocator)
{ allocators.insert(std::map< std::string, creator::basic_allocator< base_class >* >::value_type(allocator->identifier(), allocator)); }
};

}; // end namespace creator

// example of how to use it
class command {};

class myclass
: public command
{
};

int main(int argc, char* argv[])
{
creator::allocator&ltmyclass, command> the_allocator;
creator::factory&ltcommand> the_factory;

the_factory.add(&the_allocator);

// try allocating an object using the class name
delete( the_factory.allocators[typeid(myclass).name()]->allocate() );

return 0;
}



The reason for the derivation would be that the creator of the allocator object will know both the abstract base and the derived type in use, and so he will use the allocator class. However, to actually use those allocators together with objects of the same abstract base but different derived types, you need an abstract allocator class which only needs the type. So, you create a specific allocator that can generate the proper unique ID, and register it as a generic allocator. Kind of neat.

You also need some kind of registration, which could be done before startup (as per the article?) or inside the factory::creat() function and the identifier isn''t in the list of allocators. I just used a simple add() function to take care of it, for the example.

(Believe it or not, I just coded up that little set of classes in about 15 minutes!! I spent more than an hour debugging it (because of VC''s template support), but it works now in VC 6.0. I couldn''t use typedef in the factory class because of the same stupid template errors... heh heh not bad for a first try! Of course, the string/name lookup thing is kind of slow, but I guess it could be optimized...

Since you are going to access the class with a const char*, you really don''t need the string constructor being called. Then, perhaps a STL hash_map or something similar would speed it up to the level of integer IDs? I''m kind of new to those things, so don''t act surprised if I just made a joke! )



- null_pointer
Sabre Multimedia
I though you meant use typeid when calling the NewInstace function on the factory, not inside the factory . If I''m using std::map I cant use char* for the key because there isn''t an < for char* (unless there''s a specialization of less<> for it, but I haven''t checked). If I want a faster lookup I can change the id type to an int (hence the template id type), and it''s been fine for everything I''ve used for so far. I''d probably switch to a vector instead of hash_map if it was going to slow (I don''t have a hash_map at the mo).
Wilka:

heh heh I love templates!


gimp:

I guess this wasn''t too helpful for those errors...

However, because you are doing tons of things based on the types, and because so much redundant code is generated for each new message, templates make an ideal solution. Besides, without templates, all of the dynamic creation code spills into your message classes, which makes them harder to understand.

Think about it: someday down the road you will want to make another factory for a different set of classes. When you go to do that, you can take a templated class set like mine or Wilka''s (which seems a bit better ) and then just write your new classes how you please and they''ll work fine.

Advantages: In order to derive your own hierarchy for your new class set, you don''t have to keep copying and pasting all of the dynamic creation code into each new class. Also, you don''t have to modify the templated classes, so there is no chance of introducing new bugs.

Happy Coding!



- null_pointer
Sabre Multimedia

This topic is closed to new replies.

Advertisement