I'll simplify my problem of hundreds of classes to a count of two and try to explain what I mean:
class Base {
};
class A: public Base {
};
class B: public Base{
};
static Base* foo (int bar){
switch (bar) {
case 0:
return new A();
break;
case 1:
return new B();
break;
default:
return new Base();
}
}
I want to instantiate objects according to the value of bar. I just feel switch-case isn't the best way in C++ to do so for way more inheritors of Base
.
Edit: going with the std::map
approach I came up with this:
struct Dictionary {
typedef Base* (Dictionary::*FunctionPointer)(void);
std::map <int, FunctionPointer> fmap;
Dictionary() {
fmap.insert(std::make_pair(0, new A()));
fmap.insert(std::make_pair(1, new B()));
}
Base* Call (const int i){
FunctionPointer fp = NULL;
fp = fmap[i];
if (fp){
return (this->*fp)();
} else {
return new Base();
}
}
};
static Dictionary dictionary;
A lot depends on the circumstances, but the most frequent solution is probably to use a static instance of a map to factory functions. If the key type of the map is a small integer value, as in your example, the "map" can be nothing more than a C style array:
static Base*
foo( int bar )
{
static Base* (*factories[])() = [ &aFactory, &bFactory ];
return bar >= 0 && bar < size( factories )
? (*factories[bar])()
: baseFactory();
}
More generally, you can use an std::map
(to discriminate on
any imaginable type), and you can map to static instances of
factory objects, rather than factory functions, if different
keys should result in the same type, but with different
arguments.
Some suggestions to improve your Dictionary::Call
function:
Base* Dictionary::Call( int i ) const
{
std::map<int, FunctionPointer>::const_iterator
entry = fmap.find( i );
return entry == fmap.end()
? new Base()
: (this->*(entry->second))();
}
I've made the function const
, since it doesn't modify
anything, and most impprtantly, I use std::map<>::find
, to
avoid inserting extra entries into the map if the object isn't
already there.
And since I'm adding the const, you'll have to update the typedef:
typedef Base* (Dictionary::*FunctionPointer)() const;
Another suggestion: unless the factory functions need access
Dictionary
, make them static. The syntax is a lot simpler
(and it will probably also improve performance).
static changes the typedef again:
Also: in the constructor, new A()
is not a function
constructing a new object. There may be something to
facilitate this in C++11 (between lambda and std::function
),
but otherwise, you'll still have to write each of the factory
functions by hand. Or you could use a template:
template <typename Target>
Base* construct() const
{
return new Target();
}
Dictionary()
{
fmap.insert( std::make_pair( 0, &Dictionary::construct<A> ) );
// ...
}
Or if you make them static:
typedef Base* (*FunctionPointer)();
// ...
template <typename Target>
static Base* construct()
{
return new Target();
}
Base* Dictionary::Call( int i ) const
{
std::map<int, FunctionPointer>::const_iterator
entry = fmap.find( i );
return entry == fmap.end()
? new Base()
: (*entry->second)();
}
You'll notice how the static simplifies the declarations (and the function call through the pointer—your pointer to member function has become a simple pointer to function).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With