9

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?

Lalaland
  • 8,180
  • 3
  • 30
  • 47

3 Answers3

8

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));
    }
};
Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
4

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.

Jared Hoberock
  • 10,654
  • 2
  • 33
  • 74
  • 1
    I know this is old, but what's the point of the ConfigurationManager class in this usage of CRTP? We can't use that type to store Foo. – Constantin Mar 08 '16 at 23:08
1

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.

Community
  • 1
  • 1
Matt Phillips
  • 8,691
  • 8
  • 41
  • 72