Frage

Vor kurzem fand ich eine C-Bibliothek, dass ich in meinem C ++ Projekt verwenden möchten. Dieser Code wird mit globalen Variablen konfiguriert und schreibt es die Ausgabe in dem Speicher zeigte von statischen Zeigern . Wenn ich mein Projekt ausführen würde Ich mag 2 Instanzen des C-Programm auszuführen: eine mit Konfiguration A und eine mit Konfiguration B. kann ich nicht mein Programm leisten zweimal zu laufen, so dass ich dort denken gibt 2 Möglichkeiten:

  • Erstellen Sie ein C ++ Wrapper : Das Problem hierbei ist, dass die Wrapper-Klasse alle global / statische Variablen die C-Bibliothek hat enthalten soll. Da die Funktionen in der C-Bibliothek diese Variablen verwenden, werde ich sehr große Argument-Listen für diese Funktionen erstellen haben.
  • Kopieren-Einfügen , um die C-Bibliothek: Hier werde ich den Namen jeder Funktion und jede Variable in der C-Bibliothek anpassen muß
  • .

Welche ist die schnellste Lösung? Gibt es andere Möglichkeiten, 2 Instanzen derselben C-Quelle zu laufen?

Danke,

Max

War es hilfreich?

Lösung

C ++ -Wrapper
Sie erhalten weg leichter durch „die gesamte Bibliothek“ Einfügen - nur leicht modfied - in eine Klasse

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

wird

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

Es gibt meist deklarative Änderungen (wie „Töten“ statische und extern-Deklarationen). Sie müßten statische Variablen innerhalb der Methoden auf der Jagd nach, obwohl, und sie in Mitglieder als auch

Separate Namespaces
Das ist eine hässliche Lösung, aber könnte ausreichen, um für Sie:

// 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"
}

Wenn Sie Glück haben, der Optimierer / Linker erfolgreich in den identischen Code-Folding. Allerdings Typen in A:: und B:: sind unabhängig.

Andere Tipps

Wenn Sie es sich nicht leisten zweimal zu laufen, wie etwa 3-mal? Sie könnten möglicherweise einen kleinen Front-End-Prozess, dass Starts zwei separate Instanzen von Ihrem C-Programm schreiben. Aus der Nutzung Perspektive wäre es wie eine einzige .exe sieht immer noch, dass Sie nur einmal ausführen, aber hinter den Kulissen würden Sie einen übergeordneten Prozess mit zwei Kindern haben. Ich habe keine Ahnung, ob dieser Ansatz würde Ihre tatsächlichen Bedürfnisse anzupassen, aber es würde mit ziemlicher Sicherheit schneller als die beiden Ihre beiden anderen Optionen.

IIUC, was Sie haben, ist, im Grunde diese:

extern int a;
extern int b;

void f();
void g(); 

wo a und b das Verhalten von f() und g() ändern. Ist das korrekt?

Wenn Sie diese haben, und Sie wollen dies in C ++ wickeln, dann, was Sie tun können, ist dies:

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

};

Je nachdem, was Sie haben statt a und b, könnte dies nicht sehr effizient sein.

Natürlich, wie Raki in den Kommentaren gesagt, da diese globale Variablen verwendet, ist es nicht an allen Thread-sicher.

Ich mag die Idee hier. Aber ich soll einen Zeiger jeden Variable I Notwendigkeit zu ändern machen. Hier ein Beispiel:

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

Vielleicht ist es etwas, das mir entzog sich aber ...

... Globale Variablen werden zwischen Threads gemeinsam genutzt, nicht verarbeitet ...

Das bedeutet, dass in Ihrem Fall Sie zwei Prozesse der gleichen C-Programm Arbeits haben können, und sie wird man nicht mit den anderen stören, wenn sie nicht Speicher irgendwie mit Prozess-shared arbeiten.

... Wenn Sie zwei Instanzen des C-Code läuft im gleichen Prozess ...

Dann sind Sie geschraubt.

TLS vielleicht?

Sie können entweder sie in separate Threads starten und die globalen Variablen als Themen-Local-Speicher Variablen deklarieren. Zum Beispiel auf Visual C ++, den folgenden Code:

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

Jeder Thread wird seine eigene Version der Variablen haben. Auf diese Weise am Ende des Fadens, können Sie den Inhalt an anderer Stelle kopieren.

den Code Umschreiben ...

Sie brauchen keine C ++ Schicht hinzuzufügen. Sie können Ihren C-Code halten, und erklären Sie alle Ihre globalen Variablen in einer Struktur:

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

Und dann ändern Sie die Prototypen der Funktionen einen Zeiger auf diese Struktur als erster Parameter zu akzeptieren, und dann stattdessen die globale Variable zu modifizieren, können Sie das struct Mitglied ändern:

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

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

, die sich:

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

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

Dann wird in der Hauptsache, anstatt den ersten Aufruf der Funktion, rufen Sie es, indem sie es den Zeiger auf die richtige Struktur zu geben. Statt:

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

Sie schreiben:

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

In dem obigen Beispiel sind die beiden hintereinander genannt, aber Ihr Lieblings Start Themen statt.

den globalen Zustand zu speichern?

Wenn Ihr Code ist single-threaded, können Sie Anrufe für die erste Instanz und fordert, dass die zweite verschachteln, indem Speichern / den globalen Zustand zurückzusetzen. Lassen Sie uns verwenden die gleiche Struktur oben:

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

Und dann rufen Sie die speichern und Reset-Funktionen bei Bedarf:

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 ;
}
automatisch wickeln Sie die Reset / savestate Funktionen

Dies könnte durch C ++ Code gewickelt werden. Zum Beispiel:

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

Welche Sie ermöglichen es Ihnen, das Umschreiben der Haupt wie:

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

Welche wie viel besser ist als die C-Version aussieht. Dennoch ist MyWrapper nicht Thread-sicher ...

Fazit

Die erste Lösung (TLS) ist die quick'n'dirty Lösung, während die zweiten den Code Refactoring es richtig zu schreiben (es gibt sehr gute Gründe, globale Variablen verpönt sind, und es scheint, stießen sie auf einen von ihnen „hack“), und die dritte ist ermöglicht Ihnen die beiden Anrufe zu verschachteln.

Von allen drei Lösungen, nur die zweite wird es einfach, den Code innerhalb robust, thread-safe C ++ Klassen wickeln, wenn nach wie vor erforderlich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top