views:

242

answers:

5

Hi,

Recently I found a C library that I want to use in my C++ project. This code is configured with global variables and writes it's output to memory pointed by static pointers. When I execute my project I would like 2 instances of the C program to run: one with configuration A and one with configuration B. I can't afford to run my program twice, so I think there are 2 options:

  • Make a C++ wrapper: The problem here is that the wrapper-class should contain all global/static variables the C library has. Since the functions in the C library use those variables I will have to create very big argument-lists for those functions.
  • Copy-paste the C library: Here I'll have to adapt the name of every function and every variable inside the C library.

Which one is the fastest solution? Are there other possibilities to run 2 instances of the same C source?

Thanks,

Max

+2  A: 

If you can't afford to run it twice, how about 3 times? You could conceivably write a tiny front-end process that launches two separate instances of your C program. From the usage perspective it would still look like a single .exe that you run only one time but behind the scenes you'd have a parent process with two children. I have no idea if that approach would suit your actual needs but it'd almost certainly be faster than either of your other two options.

Rakis
I think this would ask a lot of inter process communication..
Maximilien
So the two configurations also need to communicate with each other?
Mark B
Say I have a project Bar and I create two processes foo1 and foo2 that each have a separate configuration.During the run of Bar, it needs to transfer information to foo1 and foo2.At the very end of the run, Bar collects the information gathered by foo1 and foo2.
Maximilien
+2  A: 

IIUC, what you have is, basically, this:

extern int a;
extern int b;

void f();
void g(); 

where a and b modify the behavior of f() and g(). Is that correct?

If you have this and you want to wrap this in C++, then what you could do is this:

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

};

Depending on what you have instead of a and b, this might not be terribly efficient.

Of course, as Raki said in the comments, since this is using global variables, it's not at all thread safe.

sbi
It's probably worth mentioning that this approach is safe only for single-threaded applications.
Rakis
@Rakis: Since the library is configured using globals, I'd think this goes without mentioning.
sbi
see my new answer ;-)
Maximilien
@sbi: The comment was intended for C++ newcommers that might happen this thread in the future. I can see this topic being found fairly easily by a search engine. "It goes without saying" is relative to your level of experience. Remember college your college physics professor dropping that comment? Yeah.
Rakis
@Rakis: You got a point there. I'll add a sentence to the answer. Thanks.
sbi
A: 

Hey sbi, I like the idea here. But I should make a pointer of every variable I need to modify. Here's an example:

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;
}
Maximilien
I'm not sure what "make a pointer of every variable I need to modify" refers to. instead of `int a; int b;` you have `int a; int* output;`, but otherwise no difference. Am I missing something?
sbi
What I mean is the following:suppose you have 2 global variables in your C code:int a; int output;The output of your C library is written to "output". When you want to write a C++ wrapper like this you'll need to create a pointer to memory instead of just a variable.
Maximilien
@Maximilien: Could it be that you (mistakingly) believe that a pointer is not a variable?
sbi
@Maximilien: BTW, you should start your answering comments to me with @sbi, so that SO has a chance to notify me. I found the above one by pure coincidence.
sbi
@sbi: thanks for the hint ;-).Yeah, it would've been more clear if I replaced "variable" with "primitive non-pointer datatypes".
Maximilien
A: 

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.

paercebal
+1  A: 

C++ -Wrapper
You get away easier by pasting "the entire library" - only slightly modfied - into a class.

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

becomes

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

There are mostly declarative changes (such as "killing" static and extern declarations). You would need to hunt down static variables inside the methods, though, and turn them into members as well

Separate Namespaces
That is an ugly solution, but might be enough for you:

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

If you are lucky, the optimizer/linker succeeds in folding the identical code. However, types in A:: and B:: are unrelated.

peterchen
@paercebal,@peterchen: Eventually I did it by pasting the entire library and hunting the static/extern/global variables.Another difficulty were writing destructors.Thanks
Maximilien