5

最近、C++プロジェクトで使用したいCライブラリを見つけました。このコードはグローバル変数で構成され、静的ポインターが指すメモリに出力を書き込みます。プロジェクトを実行するときに、Cプログラムの2つのインスタンスを実行したいと思います。1つは構成Aで、もう1つは構成Bです。プログラムを2回実行する余裕がないため、2つのオプションがあると思います。

  • C ++ラッパーを作成する:ここでの問題は、ラッパークラスにCライブラリが持つすべてのグローバル/静的変数が含まれている必要があることです。Cライブラリの関数はこれらの変数を使用するため、これらの関数の非常に大きな引数リストを作成する必要があります。
  • Cライブラリをコピーして貼り付ける:ここでは、Cライブラリ内のすべての関数とすべての変数の名前を調整する必要があります。

どれが最速の解決策ですか?同じCソースの2つのインスタンスを実行する他の可能性はありますか?

ありがとう、

マックス

4

5 に答える 5

3

C ++ -Wrapper
「ライブラリ全体」(わずかに変更されただけ)をクラスに貼り付けることで、簡単に逃げることができます。

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

になります

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

ほとんどの場合、宣言型の変更があります(静的宣言と外部宣言を「強制終了」するなど)。ただし、メソッド内の静的変数を探し出し、それらをメンバーに変換する必要があります。

個別の名前空間
これは醜い解決策ですが、あなたにとっては十分かもしれません:

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

運が良ければ、オプティマイザー/リンカーは同じコードを折りたたむことに成功します。ただし、入力A::B::は無関係です。

于 2010-04-22T15:33:32.550 に答える
2

IIUC、あなたが持っているのは、基本的に、これです:

extern int a;
extern int b;

void f();
void g(); 

whereabの動作を変更しf()ますg()。あれは正しいですか?

これがあり、これを C++ でラップしたい場合は、次のようにします。

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

};

aとの代わりに何を持っているかによってはb、これはあまり効率的ではないかもしれません。

もちろん、Raki がコメントで述べたように、これはグローバル変数を使用しているため、まったくスレッドセーフではありません。

于 2010-04-22T12:32:11.047 に答える
2

2回実行する余裕がない場合は、3回実行してみませんか? C プログラムの 2 つの別個のインスタンスを起動する小さなフロントエンド プロセスを作成することも考えられます。使用の観点からは、1 回だけ実行する 1 つの .exe のように見えますが、裏では 2 つの子を持つ親プロセスが存在します。そのアプローチが実際のニーズに合っているかどうかはわかりませんが、他の2つのオプションのいずれよりもほぼ確実に高速です.

于 2010-04-22T12:27:53.580 に答える
0

何か見落としがあるのか​​もしれませんが…

...グローバル変数は、プロセスではなくスレッド間で共有されます...

これは、あなたの場合、同じ C プログラムの 2 つのプロセスを動作させることができ、プロセス共有メモリで何らかの形で動作しない限り、他のプロセスに干渉しないことを意味します。

...同じプロセスで実行する C コードの 2 つのインスタンスが必要な場合...

それからあなたはめちゃくちゃです。

TLS、おそらく?

それらを別々のスレッドで起動し、グローバル変数を Thread-Local-Storage 変数として宣言することができます。たとえば、Visual C++ では、次のコード:

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

各スレッドには、独自のバージョンの変数があります。このようにして、スレッドの最後で、コンテンツを別の場所にコピーできます。

コードを書き直して...

それに C++ レイヤーを追加する必要はありません。C コードを保持し、すべてのグローバル変数を 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 ;

次に、この構造体へのポインターを最初のパラメーターとして受け入れるように関数のプロトタイプを変更し、グローバル変数を変更する代わりに、構造体メンバーを変更します。

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

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

次のようになります。

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

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

次に、メインでは、最初の関数を呼び出す代わりに、正しい構造体へのポインターを渡して呼び出します。それ以外の :

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

あなたが書く :

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

上記の例では、2 つが次々に呼び出されますが、代わりにスレッドを起動できます。

グローバル状態を保存しますか?

コードがシングルスレッドの場合、グローバル状態を保存/リセットすることで、最初のインスタンスの呼び出しと 2 番目のインスタンスの呼び出しをインターリーブできます。上記と同じ構造体を使用しましょう:

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

そして、必要に応じて save 関数と reset 関数を呼び出します。

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

これを C++ コードでラップして、resetState/saveState 関数を自動的にラップすることができます。例えば ​​:

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

メインを次のように書き換えることができます。

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

これは、C バージョンよりもはるかに優れているように見えます。それでも、MyWrapper はスレッドセーフではありません...

結論

最初の解決策 (TLS) は簡単な解決策ですが、2 番目の解決策はコードをリファクタリングして正しく記述します (グローバル変数が嫌われているのには十分な理由があり、どうやらそのうちの 1 つに出くわしたようです)。 3 つ目は、2 つの呼び出しをインターリーブできるようにする「ハック」です。

3 つすべてのソリューションのうち、2 番目のソリューションだけが、必要に応じて堅牢でスレッドセーフな C++ クラス内にこのコードを簡単にラップできます。

于 2010-04-22T15:14:00.250 に答える
0

ここのアイデアが好きです。しかし、変更が必要なすべての変数のポインターを作成する必要があります。次に例を示します。

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;
}
于 2010-04-22T13:58:56.357 に答える