Reputation:
I am trying to make a configuration manager class, that can store arbitrary objects by std::string.
My starting idea for my interface (abstract base class) was this (of course this is horribly incomplete)
class ConfigurationManager
{
public:
static boost::shared_ptr<ConfigurationManager> create();
template<typename T>
virtual T getOption(const std::string& name) = 0;
};
But then my compiler pointed out that template's cannot be virtual(and then I realized that I cannot have exported templates anyways).
Internally I am going to be using boost::any's(pretty much a runtime checked void*), but I do not want to expose boost::any in my interface.
What would be the best way to go about this?
Upvotes: 11
Views: 10502
Reputation: 9691
I don't know what boost::any
does for you, but aside from that your (only, I think) options are either 1) Make ConfigurationManager
a template class, or 2) make ConfigurationManager::getOption
non-virtual but use a separate non-template virtual function (called within getOption
) that manages the functionality you want in your derived classes. There are also variants on 2), such as including a pointer to an object which specifies the intended functionality of (non-virtual) getOption
. This object would be an instance of a class which is itself part of an inheritance hierarchy--the Strategy pattern basically. Seems more complicated though. So basically I am suggesting
class ConfigurationManager
{
public:
...
template<typename T>
getOption(...);
private:
virtual getOptionSpecial(...) = 0; //Called within getOption
};
The top answer to this SO thread is (in part) why I think this is pretty much all you can do.
Upvotes: 1
Reputation: 11406
An alternative approach would be to pass the name of the derived type to ConfigurationManager
:
template<typename Derived>
class ConfigurationManager
{
public:
static boost::shared_ptr<ConfigurationManager> create();
template<typename T>
T getOption(const std::string& name)
{
// call Derived::getOption
return static_cast<Derived*>(this)->getOption(name);
}
};
The derived type Foo
would then be defined like this:
class Foo : public ConfigurationManager<Foo>
{
template<typename T>
T getOption(const std::string& name)
{
// do something Foo-specific here
}
};
The end result is something similar to an abstract virtual function. This idiom is referred to as the curiously recurring template pattern.
Upvotes: 5
Reputation: 726609
Make a protected virtual abstract function returning boost::any
, and a non-virtual, non-abstract, public template function to hide it from the users of your interface.
class ConfigurationManager {
protected:
virtual boost::any getOptionProtected(const std::string& name) = 0;
public:
static boost::shared_ptr<ConfigurationManager> create();
template<typename T> T getOption(const std::string& name) {
return boost::any_cast<T>(getOptionProtected(name));
}
};
Upvotes: 9