For an application I'm writing, an important part is that the user can select any of available options for different processes. All these options are derived from the same base class. I do add new options once in a while though, and I wanted to make this process as easy as possible. So, after looking around the web and SO, here is what I have:
Base class:
class Base {
double some_member;
virtual double some_method() = 0;
};
Derived class:
class Derived : Base {
//...
};
A factory to hold a table of available types and create a derived class based on an assigned name:
template <typename B>
class Factory{
public:
template <typename D>
void registerType(std::string name)
{
static_assert(std::is_base_of<B, D>::value, "class doesn't derive from the base");
table_[name] = &createFunc<D>;
}
B* create(std::string name)
{
const auto it = table_.find(name);
if(it != table_.end())
return it->second();
FILE_LOG(logERROR) << "unidentified option, acceptable options are:";
for(auto const &m : list())
FILE_LOG(logERROR) << '\t' << m;
return nullptr;
}
std::vector<std::string> list()
{
std::vector<std::string> lst;
for(auto const &iter : table_)
lst.push_back(iter.first);
return lst;
}
private:
template<typename D>
static B* createFunc()
{
return new D();
}
typedef B* (*PCreateFunc)();
std::map<std::string, PCreateFunc> table_;
};
A class that contains all the options for a certain process:
class OptsContainer {
private:
std::vector<std::unique_ptr<Base>> opts_;
public:
//some other stuff
void addOption(const std::string &); //adds a new option with given name
static Factory<Base> factory;
};
Factory<Base> OptsContainer::factory;
void OptsContainer::addOption(const std::string &name)
{
opts_.push_back(std::unique_ptr<Base>(factory.create(name)));
}
At this point, whenever I add a new derived class, I just need to register the class in the factory table:
///All older includes
#include "derived42.h"
void initOptions()
{
//all other registrations
OptsContainer::factory.registerType<Derived42>("Derived42");
}
And I call initOptions at the very beginning of my program. This is working great, but I feel that it should be possible to make it even better. What I want is to have to only add #include "derived42" and the type to register itself, therefore no need for the initOptions function and calling it at the very beginning.
Do I have to use Boost/Loki/... (as mentioned in another post)? Is this even possible in vanilla c++? or should I just live with what I have at the moment? Just FYI, I'm using Visual Studio Express 2015 to write my program, but I can move the code to Linux as well (gcc). In other words, prefer not using c++11/c++14 patterns that are not implemented in VS2015, but if I need to, I can easily move everything to gcc.
Aucun commentaire:
Enregistrer un commentaire