2010-02-07 12 views
7

¿Existe un patrón que pueda usar para llamar a las rutinas de inicialización y limpieza necesarias de una biblioteca subyacente (C)? En mi caso, me gustaría crear la clase contenedora para que pueda ser compuesta en otros objetos. El problema es que, cuando destruyo la clase contenedora, se invocan las rutinas de limpieza de la biblioteca subyacente. Eso está bien hasta que instanciar varios objetos de mi clase contenedora. Mi pregunta es ¿cuál es la mejor forma de para manejar realmente esta situación? Me viene a la mente un contador de referencia estático, pero quería saber si existían otras opciones potencialmente mejores y los intercambios involucrados.¿Patrón común para la inicialización y el cierre de la biblioteca?

+1

No es una gran idea, pero ¿ayudará el patrón Singleton? – batbrat

+1

¿Está buscando simplemente el patrón utilizado por std :: ios_base :: Init? Inicializa una biblioteca de C++ (iostreams), pero lo mismo podría funcionar para usted. –

+0

Un recuento de ref estático para su clase contenedora suena como una buena solución para mí. – munificent

Respuesta

4

No todo tiene que ser una clase. El patrón Singleton le permitiría convertir esto en una clase, pero en realidad no comprar nada sobre las funciones globales:

bool my_library_init(); 
void my_library_shutdown(); 

la primera llamada devuelve verdadero si la biblioteca se ha inicializado correctamente, el segundo silenciosamente hace lo que tiene que hacerse y salir. Puede agregar cualquier tipo de conteo de referencia o tipo de rastreo de hilo detrás de estas interfaces que desee.

Además, no olvide la posibilidad de que su biblioteca pueda hacer todo esto de forma transparente. Cuando se llama a la primera función de la biblioteca, ¿podría detectar que aún no se ha inicializado y configurar todo antes de realizar el trabajo? Para el apagado, solo registre los recursos que se destruirán con un objeto global, de modo que se destruyan cuando el programa finalice. Hacerlo de esta manera es ciertamente más complicado, pero puede valer la pena el beneficio de usabilidad para las personas que llaman a su biblioteca.

+0

Si te entiendo correctamente, esto funcionaría si pudiera cambiar la biblioteca subyacente. En este caso, no puedo. – jdt141

+0

No, en absoluto. Escriba estas dos funciones globales usted mismo, luego llámelas. Puede ser que su biblioteca C subyacente proporcione tales funciones. Principalmente, estoy argumentando contra el pensamiento de "todo debe ser una clase". Estoy totalmente de OOP donde tiene sentido, pero no veo el sentido aquí. –

+0

El trabajo que estoy haciendo es una biblioteca. Soy un cliente del recurso compartido en cuestión. Quiero abstraer las llamadas específicas de la biblioteca subyacente de mi cliente. Sería una mala práctica no inicializar por completo el objeto que estoy creando en el ctor (inicialización parcial). ¿Qué ocurre si el cliente olvida llamar a las llamadas de inicialización o apagado? Entonces (probablemente) obtendrá un comportamiento indefinido, no es algo bueno. – jdt141

1

Si la inicialización puede ser llamado antes de que comience principales, y la limpieza llamó después de extremos principales, este pequeño truco (? Piratear) podría funcionar para usted:

#include <iostream> 

// C library initialization routine 
void init() {std::cout << "init\n";} 

// C library cleanup routine 
void fini() {std::cout << "fini\n";} 

// Put this in only one of your *.cpp files 
namespace // anonymous 
{ 
    struct Cleaner 
    { 
     Cleaner() {init();} 
     ~Cleaner() {fini();} 
    }; 
    Cleaner cleaner; 
}; 

int main() 
{ 
    std::cout << "using library\n"; 
} 

Salida:

init 
using library 
fini 

Se Utiliza (¿abuses?) el hecho de que los constructores de objetos estáticos se llaman antes que main, y que los destructores se llaman después de main. Es como RAII para todo el programa.

+4

también google para Inicialización estática Orden Fiasco. por ejemplo, el uso de 'std :: cout' en' init' y 'fini' ha puesto nervioso a este novato de C++: ¿está garantizado que ya existe/todavía existe? –

+0

@somebody: No fue mi intención usar un objeto global dentro de otro objeto estático, pero de todos modos planteas un punto excelente. –

+0

Gracias, alguien. Me hiciste repudiar esta respuesta hackosa. ;-P –

0

Si puede cambiar la implementación de la biblioteca, puede hacer que cada llamada a una de las funciones de la biblioteca acceda a un singleton que se crea en el primer uso.

O coloca una variable global/estática en la biblioteca que la inicializa durante la construcción y la apaga durante la destrucción. (Esto puede volverse molesto si la biblioteca usa variables globales y el orden de inicialización/apagado entra en conflicto con ellas. Además, los vinculadores podrían decidir eliminar las variables globales no referenciadas ...)

De lo contrario, no veo cómo desea para evitar el conteo de referencias (Sin embargo, tenga en cuenta que tiene el inconveniente de crear ciclos múltiples de inicio/apagado durante la vida del programa.)

0

Si su conjunto de rutinas de la biblioteca C no es demasiado grande, puede intentar combinar los patrones Singleton y Facade para las rutinas de la biblioteca C solo se invocan a través de la Fachada. Facade garantiza la inicialización y la limpieza de la biblioteca C. Singleton asegura que solo hay una instancia de la Fachada.

#include <iostream> 

// C library initialization and cleanup routines 
void init() {std::cout << "init\n";} 
void fini() {std::cout << "fini\n";} 

// C library routines 
void foo() {std::cout << "foo\n";} 
void bar() {std::cout << "bar\n";} 


class Facade // Singleton 
{ 
public: 
    void foo() {::foo();} 
    void bar() {::bar();} 
    static Facade& instance() {static Facade instance; return instance;} 

private: 
    Facade() {init();} 
    ~Facade() {fini();} 
}; 

// Shorthand for Facade::instance() 
inline Facade& facade() {return Facade::instance();} 


int main() 
{ 
    facade().foo(); 
    facade().bar(); 
} 

Salida:

init 
foo 
bar 
fini 
+2

tal vez OT para el OP, pero ¿es seguro para este subproceso? –

+0

@somebody: No, 'Facade :: instance()' no es seguro para subprocesos. Una solución fácil (pero fea) sería llamar a 'Facade :: instance()' en 'main()' antes de que se engendren hilos. En ese punto, parece un poco tonto usar la fachada, y también podrías usar 'init' /' fini' directamente en main. –

+0

Me encanta cómo SO me hace aprender sobre las sutilezas de la programación. –

1

he visto un montón de Singleton hablar, por lo que sólo puede recomendar un vistazo a Alexandrescu's work.

Sin embargo, no estoy seguro de que realmente necesite un Singleton allí. Porque si lo haces, supones que todas tus llamadas compartirán el estado ... ¿es así? ¿Realmente desea llamar a la biblioteca a través de una instancia diferente de Wrapper para obtener el estado en el que la última llamada lo configuró?

Si no es así, debe serializar el acceso y reiniciar los datos cada vez.

class Wrapper 
{ 
public: 
    Wrapper() { lock(Mutex()); do_init_c_library(); } 
    ~Wrapper() { do_clean_up_c_library(); unlock(Mutex()); } 

private: 
    static Mutex& Mutex() { static Mutex MMutex; return MMutex; } 
}; // class Wrapper 

Muy sencillo ... aunque es necesario asegurarse de que Mutex se ha inicializado correctamente (una vez) y vivir hasta que no se necesita más tiempo.

Boost ofrece instalaciones para el once issue, y dado que usamos un enfoque basado en pila con MMutex, no debería salir mal ... Creo (hum).

Cuestiones relacionadas