2012-06-24 13 views
5

Estoy intentando escribir una aplicación que está cargando sus extensiones dinámicamente durante el tiempo de ejecución. Usé la biblioteca Boost Preprocessor para escribir una función de preprocesador que, dada una lista de nombres, declara una clase para cada nombre (y los convierte en subclases de alguna clase AbstractPlugin) y luego declara una secuencia de Boost MPL que contiene esas clases. Luego escribí una clase que intenta un puntero a AbstractPlugin si podría ser lanzado a cualquiera de los tipos en esa secuencia de MPL. El problema aquí es que mi función de preprocesador necesita una lista completa de todas las extensiones que quiero crear y cargar. ¿Hay alguna técnica que me permita registrar cada extensión en un archivo separado?Registre una clase C++ para que luego una función pueda iterar sobre todas las clases registradas

Actualización:

creo, mi explicación de la situación era demasiado vaga, así que decidí hacerlo más específico.

Me gustaría definir una colección de tipos de extensión. Para cada tipo de extensión, puede haber cualquier cantidad de extensiones. Durante el tiempo de ejecución, el programa carga una biblioteca externa, resuelve la función de punto de entrada, la llama y, como resultado, obtiene un puntero. A continuación, intenta convertir ese puntero a todos los tipos de extensión registrados (usando dynamic_cast, por lo que las clases para los tipos de extensión heredan de alguna clase de base polimórfica). Si un lanzamiento a algún tipo de extensión tiene éxito, el puntero fundido se usa en una llamada al manejador especial para ese tipo de extensión.

El número de tipos de extensión se conoce en tiempo de compilación (mientras que, obviamente, el número de extensiones es infinito). Usando mi enfoque, la clase de cargador usa este conocimiento para verificar si existe un controlador para cada tipo de extensión (si no, el programa no compila). Además, mi enfoque no obliga a las clases porque los tipos de extensión saben algo sobre el cargador (por lo que es fácil modificar el cargador). Pero sería más conveniente si cada tipo de extensión se registrara solo.

+0

¿Está generando un encabezado una solución aceptable? – Arpegius

Respuesta

0

Como resulta que lo que quiero es imposible. La razón de esto es "registrarse" en este contexto significa "poner un tipo dentro de la secuencia de tipo" y las secuencias de tipo son inmutables porque son tipos en sí mismos. Entonces, uno debe crear esta secuencia de tipo manualmente, o como algunas personas sugirieron mover el "registro" al tiempo de ejecución.

0

No es difícil implementar un marco de extensión de este tipo utilizando el patrón abstracto de fábrica.

http://en.wikipedia.org/wiki/Abstract_factory_pattern

Puede registrar esas funciones abstractas de fábrica/objetos en una lista global, y hacer lo que quiere hacer de base en ella.

8

Puede hacer que todas sus clases se autoregistren en algún tipo de colección. Aquí hay un enfoque esqueleto:

Base.hpp:

#include <memory> 
#include <unordered_map> 
#include <string> 

struct Base 
{ 
    virtual ~Base() = default; 

    using create_f = std::unique_ptr<Base>(); 

    static void registrate(std::string const & name, create_f * fp) 
    { 
     registry()[name] = fp; 
    } 

    static std::unique_ptr<Base> instantiate(std::string const & name) 
    { 
     auto it = registry().find(name); 
     return it == registry().end() ? nullptr : (it->second)(); 
    } 

    template <typename D> 
    struct Registrar 
    { 
     explicit Registrar(std::string const & name) 
     { 
      Base::registrate(name, &D::create); 
     } 
     // make non-copyable, etc. 
    }; 

private: 
    static std::unordered_map<std::string, create_f *> & registry(); 
}; 

Base.cpp:

#include "Base.hpp" 

std::unordered_map<std::string, Base::create_f *> & Base::registry() 
{ 
    static std::unordered_map<std::string, Base::create_f *> impl; 
    return impl; 
} 

Ahora usar esto en un cliente:

Derivado. hpp:

#include "Base.hpp" 

struct Derived : Base 
{ 
    static std::unique_ptr<Base> create() { return std::make_unique<Derived>(); } 
    // ... 
}; 

Derived.cpp:

#include "Derived.hpp" 

namespace 
{ 
    Base::Registrar<Derived> registrar("MyClass"); 
} 

El constructor de la Base::Registrar<Derived> se encarga de registrar la clase Derived bajo el nombre "MyClass".Puede crear instancias de Derived de forma dinámica a través de:

std::unique_ptr<Base> p = Base::instantiate("MyClass"); 

El código podría/debería ser mejorada mediante la detección de registros de repetición, la impresión de una lista de clases disponibles, etc. Nota cómo evitamos cualquier problema de inicialización de pedidos estáticas mi haciendo que el real registry map object es un objeto estático de bloques, que se garantiza que se inicializará antes de su primer uso, y por lo tanto se destruirá solo después de su último uso.

+0

Consideré algo como esto, pero estoy más interesado en un enfoque en tiempo de compilación. No estoy seguro de que exista uno, pero aún así, la tierra de las plantillas de C++ está llena de milagros. – grisumbras

+0

No hay uno estático. No puede hacer que las clases se registren por sí mismas, ya que cada archivo de implementación tiene su propia unidad de compilación, y cada una puede ubicarse en una biblioteca diferente. El poder de esta solución es que cuando carga una biblioteca dinámica, la clase de extensión se convirtió automágicamente disponible. Es un patrón bien conocido y muy útil. – Arpegius

+0

Esta es una implementación muy buena. Lo único que no entiendo es = 0 en la función static create(). Por lo que sé, C++ no es válido porque las funciones estáticas nunca pueden ser virtuales. ¿Cómo se supone que esa parte funcione? – Shenjoku

Cuestiones relacionadas