2009-05-17 11 views
7

Implementé un "sistema de plugins" muy básico como parte de una biblioteca estática . Cada "complemento" implementa el soporte para un formato de imagen específico, p. GIF, JPEG, etc. Además, tengo un Singleton (una clase llamada PluginManager) que mantiene una lista de todos los complementos disponibles.Registro de objetos en la biblioteca estática

La parte complicada es que quiero deshabilitar/habilitar los complementos al agregar o eliminar sus archivos fuente del archivo de proyecto. Para lograr esto, cada plugin crea una variable global (con diferentes nombres) y registra el plugin en el constructor de esa clase en PluginManager.

Algo como esto para el formato JPEG ...

struct JPEGPlugin 
{ 
    // constructor will register plugin 
    JPEGPlugin() 
    { 
    PluginManager::Singleton().RegisterPlugin(this); 
    } 

    // plenty of other code 
    ... 
}; 

JPEGPlugin jpeg_instance; // instantiate in global scope 

Sin embargo, aunque esto funciona perfectamente en teoría, se produce un error al vincular esta biblioteca estática a otro código para construir un ejecutable. Mientras este ejecutable no acceda a los plugins globales (como jpeg_instance), el enlazador no ve una conexión (ignora por completo los efectos secundarios del constructor) y no incluye el código en el ejecutable final. En otras palabras, el complemento JPEG no está disponible en la aplicación final.

Me encontré con los problemas un par de veces en los últimos años, y siempre busqué soluciones en la red. Cada vez, acabo de encontrar páginas que básicamente dicen que es un problema conocido y que tengo que vivir con eso.

Pero tal vez alguien en SO sabe cómo hacer que esto funcione?

Respuesta

2

No sé si esto una solución de la forma en que resolvió este problema, pero hemos tenido un problema similar con el registro estático de una factoría de objetos y en Visual Studio nos resuelto al declarar las clases involucradas con __declspec (dllexport) esto era necesario aunque las bibliotecas involucradas no eran dlls. Pero sin esto el enlazador omitiría las clases no referenciadas.

La solución de registro trabajamos un poco diferente y no involucraba objetos asignados de pila. Levanté partes de la unidad CPP, que es también donde descubrí el enfoque de __declspec iirc.

[edit] También tuvimos que #include la declaración de la clase registrada de alguna parte del código.

+0

OK, acabo de notar su edición, ¡ese es un gran punto! :) –

2

Dado que es una biblioteca estática, puede considerar que el administrador registre los complementos (en lugar de que los complementos se registren). El archivo de cabecera puede definir algún símbolo preproc (es decir JPEG_PLUGIN) que controla si o no el gerente registra el plugin basado en la inclusión de la cabecera:

 
#include "JpegPlugin.h" 

void PluginManager::RegisterPlugins() 
{ 
#idef JPEG_PLUGIN 
    RegisterPlugin(&jpeg_instance); 
#endif 
} 

JpegPlugin.h no necesariamente tiene que incluir la definición de el JpegPlugin. Podría ser algo como esto:

 
#ifndef JPEG_PLUGIN_HEADER 
#define JPEG_PLUGIN_HEADER 

#if 0 // change this to 1 to use the plugin 
#define JPEG_PLUGIN 
#include "Jpeg_PluginCls.h" 
#endif 

#endif 
+1

Gracias por la respuesta. Sin embargo, como usted mismo señala, esto introduce una dependencia del administrador al complemento, ¡algo que me gustaría evitar! La razón es que otras personas deberían implementar complementos sin la necesidad de cambiar la clase de administrador. – beef2k

+1

La solución aquí no impide que los usuarios de la lib estáticas puedan registrar sus propios complementos a través del mecanismo en su ejemplo (el administrador llama al mismo método RegisterPlugin en su ejemplo original (después de que lo arreglé)). Solo garantiza que los complementos que se envían con (dentro de) la biblioteca estática están disponibles para el administrador con el que los usuarios se conectan (sin hacer referencia explícita a los plugins) –

1

Este es un seguimiento de Harald Scheirich's answer.

He hecho algunos experimentos, y parece que MSVC++ de 2005 Modo de disparo (pero no el modo de depuración) se convertirá en la bandera /OPT:REF al enlazador, que de acuerdo con el LINK documentation, causará ningún símbolos sin referencia a ser retirados de el EXE final. Y, the webpage for __declspec(selectany) parece indicar que los constructores de objetos globales no se consideran referencias a un objeto (incorrectamente en mi humilde opinión, pero ahí lo tiene). Así que supongo que este problema "desaparece" para compilaciones de depuración, ¿es correcto?

Por lo tanto, creo que la sugerencia de Harald de usar __declspec(dllexport) es una forma conveniente de marcar el símbolo como "referenciado" porque está especificado dentro del código fuente. Si por alguna razón desea evitar exportar el símbolo, sospecho que puede lograr lo mismo ya sea usando el /INCLUDE:mysymbol linker flag, o apagando el indicador /OPT:REF.

+0

Estábamos teniendo el mismo problema con las versiones de depuración, todo el sistema que tenemos es un poco frágil de todos modos, otra condición para este funcionamiento fue que el archivo .h debe incluirse desde algún lugar al que se llegue. Entonces, el problema no desaparece con las versiones de depuración. El único otro que tengo el problema para irme es cuando hay una referencia directa en código que se alcanza estáticamente desde otro lugar. –

+0

Interesante. Es una pena que sea tan difícil hacer esto bien, ya que registrar una clase en el constructor de una variable global es una forma tan agradable y sostenible de desarrollar un sistema de complementos. –

+0

Nos enfrentamos al problema de la compilación de depuración y liberación. Y no solo en Visual Studio, sino también en GCC. – beef2k

0

favor:

  1. añadir el proyecto lib estática como una referencia al proyecto exe
  2. establecer tanto de las "dependencias de bibliotecas de enlace" y "uso de biblioteca de vínculos de dependencia como entradas" como verdadero.

ver esto: config

Cuando "Usar las dependencias de bibliotecas enlace como entradas" se establece en Sí, los enlaces del sistema de proyectos en los archivos .obj para .libs producidos por los proyectos dependientes. Entonces todos los símbolos son guardados.

Cuestiones relacionadas