Question

Récemment, je trouve une bibliothèque C que je veux utiliser dans mon projet C ++. Ce code est configuré avec variables globales et écrit sa sortie à la mémoire pointée par pointeurs statiques . Quand j'exécute mon projet, je voudrais 2 instances du programme C pour exécuter: une avec la configuration A et une configuration avec B. Je ne peux pas se permettre d'exécuter mon programme deux fois, donc je pense qu'il ya 2 options:

  • C ++ wrapper : Le problème ici est que l'emballage de classe doit contenir toutes les variables globales / statique, la bibliothèque C a. Étant donné que les fonctions de la bibliothèque C utilisent ces variables, je vais devoir créer de très grandes listes argument pour ces fonctions.
  • Copier-coller la bibliothèque C: Ici, je vais devoir adapter le nom de toutes les fonctions et toutes les variables à l'intérieur de la bibliothèque C
  • .

Laquelle est la solution la plus rapide? Y at-il d'autres possibilités pour exécuter 2 instances de la même source C?

Merci,

Max

Était-ce utile?

La solution

C ++ -Wrapper Vous obtenez loin plus facile en collant « toute la bibliothèque » - peu modfied - dans une classe

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

devient

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

Il y a surtout des changements déclaratifs (comme statique « de mort » et déclarations externat). Vous devrez traquer les variables statiques à l'intérieur des méthodes, cependant, et les transformer en membres et

espaces de noms distincts C'est une solution laid, mais peut-être suffisant pour vous:

// 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 vous êtes chanceux, l'optimiseur / linker réussit à plier le code identique. Cependant, les types de A:: et B:: ne sont pas liés.

Autres conseils

Si vous ne pouvez pas se permettre de courir deux fois, que diriez-vous 3 fois? Vous pourriez peut écrire un processus petit front-end qui lance deux instances distinctes de votre programme C. Du point de vue de l'utilisation, il regarderait toujours comme un seul .exe que vous exécutez une seule fois, mais dans les coulisses que vous auriez un processus parent avec deux enfants. Je ne sais pas si cette approche convient à vos besoins réels, mais ce serait certainement plus rapide que l'une de vos deux autres options.

IIUC, ce que vous avez est, en gros, ceci:

extern int a;
extern int b;

void f();
void g(); 

a et b modifient le comportement de f() et g(). Est-ce exact?

Si vous avez cela et que vous voulez envelopper cela en C ++, alors ce que vous pouvez faire est la suivante:

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 fonction de ce que vous avez au lieu de a et b, cela pourrait ne pas être terriblement efficace.

Bien sûr, comme Raki dit dans les commentaires, car cela utilise des variables globales, il est pas du tout sûr de fil.

J'aime l'idée ici. Mais je devrais faire un pointeur de tous les besoins variables I à modifier. Voici un exemple:

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;
}

Peut-être il y a quelque chose qui m'a échappé, mais ...

... Les variables globales sont partagées entre les threads, processus non ...

Cela signifie que dans votre cas, vous pouvez avoir deux processus du même travail de programme C, et ils n'influeront pas un avec l'autre, ils travaillent en quelque sorte SAUF SI avec une mémoire de processus partagé.

... Si vous avez besoin de deux instances du code C fonctionne dans le même processus ...

Ensuite, vous êtes foutus.

TLS, peut-être?

Soit vous pouvez les lancer dans des threads séparés et déclarer les variables globales comme variables locales de thread-stockage. Par exemple, sur Visual C ++, le code suivant:

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

Chaque thread aura sa propre version de la variable. De cette façon, à la fin du fil, vous pouvez copier le contenu ailleurs.

Réécrire le code ...

Vous n'avez pas besoin d'ajouter une couche de C ++ pour cela. Vous pouvez garder votre code C et déclarer toutes vos variables globales dans un struct:

/* 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 ;

Et puis vous modifiez les prototypes des fonctions d'accepter un pointeur vers cette structure comme premier paramètre, puis au lieu de modifier la variable globale, vous modifiez l'élément struct:

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

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

qui deviennent:

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

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

Ensuite, dans la principale, au lieu d'appeler la première fonction, vous l'appelez en lui donnant le pointeur sur struct droite. Au lieu de:

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

Vous écrivez:

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 ;
}

Dans l'exemple ci-dessus, les deux sont appelés l'un après l'autre, mais votre fils peut lancer à la place.

Enregistrer l'état global?

Si votre code est mono-thread, vous pouvez imbriquer les appels à la première instance et appelle à la seconde en enregistrant / réinitialisation de l'état global. Utilisons le même struct ci-dessus:

/* 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 ;
}

Et puis, vous appelez les fonctions de sauvegarde et de remise à zéro en cas de besoin:

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 ;
}

Cela pourrait être enveloppé par le code C pour envelopper automatiquement les fonctions resetState / saveState. Par exemple:

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 ;
} ;

Ce qui vous vous activez la réécriture principale comme:

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 ;
}

Ce qui ressemble beaucoup mieux que la version C. Pourtant, MyWrapper est thread-safe ...

Conclusion

La première solution (TLS) est la solution de quick'n'dirty, tandis que le second est le code refactoring pour l'écrire correctement (il y a de très bonnes raisons variables globales sont mal vus, et apparemment, vous avez trébuché sur l'un d'entre eux ), et le troisième est un « hack » permettant de vous entrelacer les deux appels.

De toutes les trois solutions, la deuxième, il sera facile d'envelopper ce code à l'intérieur robuste, C ++ des classes thread-safe si elle est encore nécessaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top