2011-12-23 8 views
9

Estoy tratando de crear una clase de administrador de configuración, que pueda almacenar objetos arbitrarios por std :: string.¿Alguna manera de tener una función de plantilla en una clase base abstracta?

Mi idea de partida de mi interfaz (clase base abstracta) era esto (por supuesto, esto es terriblemente incompleta)

class ConfigurationManager 
{ 
public: 
    static boost::shared_ptr<ConfigurationManager> create(); 

    template<typename T> 
    virtual T getOption(const std::string& name) = 0; 
}; 

Pero entonces mi compilador señaló que quieras que no puede ser virtual (y luego me di cuenta de que yo no puede haber exportado plantillas de todos modos).

Internamente voy a utilizar boost :: any's (casi un tiempo de ejecución anulado vacío *), pero no quiero exponer boost :: any en mi interfaz.

¿Cuál sería la mejor manera de hacerlo?

+1

¿Para qué, exactamente? –

+0

@Oli Para simular la interfaz sintácticamente imposible que publiqué en mi pregunta. – Lalaland

+0

Entonces, ¿desea hacer plantillas de función polimórficas, aunque no sean realmente posibles? –

Respuesta

7

Realice una función abstracta virtual protegida que devuelva boost::any, y una función de plantilla pública, no virtual y no abstracta para ocultarla a los usuarios de su interfaz.

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)); 
    } 
}; 
+0

Debería ser 'boost :: any_cast (getOptionProtected (name));' – Xeo

+0

@Xeo ¡gracias por la corrección! (Se puede decir que nunca usé boost solo desde esa línea). – dasblinkenlight

+0

Terminó usando casi exactamente esto. – Lalaland

4

Un enfoque alternativo sería pasar el nombre del tipo derivado a 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); 
    } 
}; 

El tipo derivado Foo sería entonces definido como esto:

class Foo : public ConfigurationManager<Foo> 
{ 
    template<typename T> 
    T getOption(const std::string& name) 
    { 
    // do something Foo-specific here 
    } 
}; 

El extremo el resultado es algo similar a una función virtual abstracta. Esta expresión se conoce como curiously recurring template pattern.

+1

Sé que esto es antiguo, pero ¿cuál es el sentido de la clase ConfigurationManager en este uso de CRTP? No podemos usar ese tipo para almacenar Foo. – Constantin

1

No sé lo que boost::any para con vosotros, pero aparte de que sus (sólo, creo) opciones son 1) Hacer ConfigurationManager una clase de plantilla, o 2) hacer ConfigurationManager::getOptionno -virtual pero uso una función virtual sin plantilla separada (llamada dentro de getOption) que administra la funcionalidad que desea en sus clases derivadas. También hay variantes en 2), como incluir un puntero a un objeto que especifica la funcionalidad prevista de (no virtual) getOption. Este objeto sería una instancia de una clase que es en sí misma parte de una jerarquía de herencia, básicamente el patrón de Estrategia. Parece más complicado sin embargo. Así que, básicamente estoy sugiriendo

class ConfigurationManager 
{ 
    public: 
     ... 
     template<typename T> 
     getOption(...); 
    private: 
     virtual getOptionSpecial(...) = 0; //Called within getOption 
}; 

La respuesta es superior a this SO thread (en parte) por qué creo que esto es más o menos todo lo que puede hacer.

Cuestiones relacionadas