Perhaps there is something that eluded me but...
...Global variables are shared between threads, not processes...
This means that in your case, you can have two processes of the same C program working, and they won't interfere one with the other UNLESS they work somehow with process-shared memory.
...If you need two instances of the C code running in the same process...
Then you're screwed.
TLS, perhaps ?
Either you can launch them in separate threads and declare the global variables as Thread-Local-Storage variables. For example, on Visual C++, the following code :
int myGlobalVariable = 42 ; // Global variable
__declspec(thread) int myTLSVariable = 42 ; // Thread local variable
Each thread will have its own version of the variable. This way, at the end of the thread, you can copy the content elsewhere.
Rewriting the code...
You don't need to add a C++ layer to that. You can keep your C code, and declare all your global variables in a 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 ;
And then you modify the prototypes of the functions to accept a pointer to this struct as first parameter, and then instead of modifying the global variable, you modify the struct member :
/* old function */
int foo(char *p)
{
/* fudge with the global variables */
iMyShortData = 55 ;
/* etc. */
fooAgain("Hello World", 42) ;
}
which become:
/* new function */
int foo(MyStruct * s, char *p)
{
/* fudge with the struct variables */
s->iMyShortData = 55 ;
/* etc. */
fooAgain(s, "Hello World", 42) ;
}
Then, in the main, instead of calling the first function, you call it by giving it the pointer to the right struct. Instead of :
int main(int argc, char * argv[])
{
bar(42, 55) ;
}
You write :
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 the example above, the two are called one after the other, but your can launch threads instead.
Saving the global state?
If your code is single-threaded, you can interleave calls for the first instance and calls for the second by saving/resetting the global state. Let's use the same struct above :
/* 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 ;
}
And then, you call the save and reset functions when needed :
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 ;
}
This could be wrapped by C++ code to automatically wrap the resetState/saveState functions. For example :
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 ;
} ;
Which you enable you the re-write the main as :
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 ;
}
Which looks like a lot better than the C version. Still, MyWrapper is not thread-safe...
Conclusion
The first solution (TLS) is the quick'n'dirty solution, while the second is refactoring the code to write it correctly (there are very good reasons global variables are frowned upon, and apparently, you stumbled upon one of them), and the third is a "hack" enabling to you interleave the two calls.
Of all the three solutions, only the second will make it easy to wrap this code inside robust, thread-safe C++ classes if still needed.