Pregunta

Recientemente he encontrado una librería C que quiero usar en mi proyecto C ++. Este código está configurado con variables globales y lo escribe en la salida de la memoria apuntado por estática punteros . Cuando ejecuto mi proyecto me gustaría 2 instancias del programa C para funcionar: uno con la configuración A y uno con la configuración B. No me puedo permitir ejecutar mi programa dos veces, así que creo que hay 2 opciones:

  • Hacer una C ++ envoltorio : El problema aquí es que el de Clase envolvente debe contener todas las variables globales / estática de la biblioteca de C tiene. Dado que las funciones de la biblioteca C utilizan aquellas variables que tendrá que crear muy grandes listas de argumentos para esas funciones.
  • Copiar y pegar la biblioteca C: Aquí voy a tener que adaptar el nombre de cada función y cada variable dentro de la biblioteca C
  • .

¿Cuál es la solución más rápida? ¿Hay otras posibilidades para ejecutar 2 instancias de la misma fuente de C?

Gracias,

Max

¿Fue útil?

Solución

C ++ -wrapper
Te alejas más fácil pegando "toda la biblioteca" - sólo ligeramente modfied - en una clase

.
// C
static char resultBuffer[42];
void ToResult(int x) { ... }
char const * GetResult() { return resultBuffer; }

se convierte

// C++
class CMyImportantCLib
{
  private:
    char resultBuffer[42];
    void ToResult(int x) { ... } // likely, no code changes at all
    char const * GetResult() { return resultBuffer; }
} ;

Hay cambios son en su mayoría declarativas (como estática "matar" y las declaraciones extern). Lo que se necesita para cazar a las variables estáticas dentro de los métodos, sin embargo, y convertirlos en miembros, así

Los espacios de nombres separados
Esa es una solución feo, pero podría ser suficiente para usted:

// impMyLib.h
namespace A 
{
  #include "c-lib.h"
}
namespace B
{
  #include "c-lib.h"
}

// impMyLib.cpp
namespace A 
{
  #include "c-lib.c"
}
namespace B
{
  #include "c-lib.c"
}

Si tiene suerte, el optimizador / enlazador tiene éxito en el plegado del mismo código. Sin embargo, los tipos de A:: y B:: no están relacionados.

Otros consejos

Si usted no puede permitirse el lujo de correr dos veces, ¿qué hay de 3 veces? Usted posiblemente podría escribir un proceso de front-end pequeña que pone en marcha dos casos separados de su programa C. Desde la perspectiva de uso sería tener la apariencia de un solo .exe que se ejecuta sólo una vez, pero detrás de las escenas que tendría un proceso padre con dos hijos. No tengo ni idea de si ese enfoque se adapte a sus necesidades reales, pero es casi seguro que sería más rápido que cualquiera de sus otras dos opciones.

IIUC, lo que tenemos es, básicamente, la siguiente:

extern int a;
extern int b;

void f();
void g(); 

donde a y b modifican el comportamiento de f() y g(). ¿Es eso correcto?

Si usted tiene este y quiere terminar con esto en C ++, a continuación, lo que podría hacer es lo siguiente:

class the_library {
public:
  the_library(int a, int b) : a_(a), b_(b) {}

  void f() {a=a_; b=b_; ::f();}
  void g() {a=a_; b=b_; ::g();}
private:
  int a_;
  int b_;

};

En función de lo que tiene lugar de a y b, esto podría no ser muy eficiente.

Por supuesto, como dijo Raki en los comentarios, ya que esto es usar variables globales, no es en absoluto seguro de rosca.

Me gusta la idea aquí. Pero debería hacer un puntero de cada variable de I necesidad de modificar. He aquí un ejemplo:

lib.h:

void f();
int g();

lib.c:

#include "lib.h"
extern int a;
extern int * output;

void f(){
    *output=(*output+6)*a;
}
int g(){
    return *output;
}

object.cc:

#include "lib.h"
#include <iostream>
using namespace std;

int a;
int * output;

class the_library {
public:
  the_library(int a, int * output) : a_(a), output_(output) {}

  void f() {a=a_; output=output_; ::f();}
  int g() {a=a_; output=output_; ::g();}
private:
  int a_;
  int * output_;

};

int main(){

    int out1=2;
    the_library icache(3,&out1);
    icache.f();
    cout<<"icache.f() -> icache is "<<icache.g()<<endl;
    icache.f();
    cout<<"icache.f() -> icache is "<<icache.g()<<endl;

    int out2;
    out2=8;
    the_library dcache(7,&out2);
    dcache.f();
    cout<<"dcache.f()\t-> icache is "<<icache.g()<<endl;
    cout<<"\t\t-> dcache is "<<dcache.g()<<endl;
    return 0;
}

Tal vez hay algo que se me escapaba, pero ...

... Las variables globales son compartidos entre los hilos, no procesa ...

Esto significa que en su caso, puede tener dos procesos de trabajo del mismo programa en C, y que no interfieran uno con el otro a menos que trabajen de alguna manera con el proceso de la memoria compartida.

... Si necesitas dos instancias del código C se ejecuta en el mismo proceso ...

A continuación, se atornillan.

TLS, tal vez?

O usted puede lanzarlos en hilos separados y declarar las variables globales como variables de almacenamiento de subproceso local. Por ejemplo, en Visual C ++, el código siguiente:

int myGlobalVariable = 42 ;                 // Global variable
__declspec(thread) int myTLSVariable = 42 ; // Thread local variable

Cada hilo tendrá su propia versión de la variable. De esta manera, al final de la rosca, se puede copiar el contenido en otro lugar.

La reescritura del código ...

no es necesario añadir una capa de C ++ para eso. Usted puede mantener su código en C, y declarar todas las variables globales en una estructura:

/* C global variable */
int iMyGlobalVariable = 42 ;
const char * strMyGlobalString = NULL ;
short iMyShortData = 7 ;

/* C struct */
typedef struct MyStruct
{
   int iMyGlobalVariable ;
   const char * strMyGlobalString ;
   short iMyShortData ;
}
MyStruct ;

Y entonces modificar los prototipos de las funciones de aceptar un puntero a esta estructura como primer parámetro, y luego en lugar de modificar la variable global, que modifica el miembro de estructura:

/* old function */
int foo(char *p)
{
   /* fudge with the global variables */
   iMyShortData = 55 ;

   /* etc. */
   fooAgain("Hello World", 42) ;
}

que se convierten en:

/* new function */
int foo(MyStruct * s, char *p)
{
   /* fudge with the struct variables */
   s->iMyShortData = 55 ;

   /* etc. */
   fooAgain(s, "Hello World", 42) ;
}

A continuación, en lo principal, en lugar de llamar a la primera función, usted lo llama, dándole el puntero a la estructura correcta. En lugar de:

int main(int argc, char * argv[])
{
   bar(42, 55) ;
}

Usted escribe:

int main(int argc, char * argv[])
{
   MyStruct A = { /* initialize A's members if needed */ }  ;
   MyStruct B = { /* initialize B's members if needed */ }  ;

   bar(&A, 42, 55) ;
   bar(&B, 42, 55) ;

   return 0 ;
}

En el ejemplo anterior, los dos son llamados uno tras otro, pero los hilos de lanzamiento puede en su lugar.

Almacenamiento del estado global?

Si el código es de un solo subproceso, se pueden entrelazar las llamadas para el primer caso y llamadas por segundo por el ahorro / restablecer el estado global. Vamos a usar la misma estructura anterior:

/* C global variable */
int iMyGlobalVariable = 42 ;
short iMyShortData = 7 ;

void saveState(MyStruct * s)
{
   s->iMyGlobalVariable = iMyGlobalVariable ;
   s->iMyShortData = iMyShortData ;
}

void resetState(const MyStruct * s)
{
   iMyGlobalVariable = s->iMyGlobalVariable ;
   iMyShortData = s->iMyShortData ;
}

Y a continuación, se llama a guardar y restablecer las funciones cuando sea necesario:

int main(int argc, char * argv[])
{
   MyStruct A = { /* initialize A's members if needed */ }  ;
   MyStruct B = { /* initialize B's members if needed */ }  ;

   resetState(&A) ; /* now, we work on A */
   bar(42, 55) ;
   saveState(&A) ;  /* we save the progress on A */

   resetState(&B) ; /* now, we work on B */
   bar(42, 55) ;
   saveState(&B) ;  /* we save the progress on B */

   resetState(&A) ; /* now, we work on A */
   foo("Hello World", 3.14159) ;
   saveState(&A) ;  /* we save the progress on A */

   resetState(&B) ; /* now, we work on B */
   foo("Hello World", 3.14159) ;
   saveState(&B) ;  /* we save the progress on B */

   /* etc. */
   return 0 ;
}

Esto podría ser envuelto por el código C ++ para envolver automáticamente las funciones resetState / savestate. Por ejemplo:

struct MyWrapper
{
    void foo(const char * p, double d)
    {
       resetState(&m_s) ;
       foo(p, d) ;
       saveState(&m_s) ;
    }

    void bar(int i, short i2)
    {
       resetState(&m_s) ;
       bar(i, i2) ;
       saveState(&m_s) ;
    }

    MyStruct m_s ;
} ;

¿Qué se habilita la re-escritura, el principal como:

int main(int argc, char * argv[])
{
   MyWrapper A ;
   MyWrapper B ;

   A.bar(42, 55) ;
   B.bar(42, 55) ;

   A.foo("Hello World", 3.14159) ;
   B.foo("Hello World", 3.14159) ;

   // etc.

   return 0 ;
}

¿Qué se parece mucho mejor que la versión C. Aún así, mywrapper no es seguro para subprocesos ...

Conclusión

La primera solución (TLS) es la solución quick'n'dirty, mientras que el segundo es refactorización del código para escribir correctamente (hay muy buenas razones variables globales no están bien vistas, y al parecer, se tropezó con una de ellas ), y el tercero es un "hack" que permite a que intercalar las dos llamadas.

De todas las tres soluciones, sólo el segundo hará que sea fácil para envolver el código en el interior robusta, C ++ flujos seguros clases si todavía es necesario.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top