何か見落としがあるのかもしれませんが…
...グローバル変数は、プロセスではなくスレッド間で共有されます...
これは、あなたの場合、同じ 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++ クラス内にこのコードを簡単にラップできます。