Thalia
Thalia

Reputation: 14615

Define directive inside code to separate code versions

I am modifying code written by someone else.

The existing code contains algorithms, valid for two situations. The differences are minimal - some data types and sometimes a few extra lines in processing.

The original code sets everything as

#define ABC

#ifdef ABC
typedef struct {
    unsigned char a, b, c;
} MyStruct;
#else  
typedef unsigned char MyStruct;
#endif 

#if defined(ABC)
#define SIZE1 3
#else  
#define SIZE1 2
#endif 

typedef struct {
    MyStructtable[SIZE1];
} MyStructA;

void MyMethod(){
   statement1;
   #if defined(ABC)
   staement2;
   #endif
}

It works... if the choice is made inside the source code, but i a trying to give the user a option to use either one version or another, and having the hardest time separating the code.

I could create a complete set of files for each version. The problem I have:

since there are complex algorithms, it would make sense to keep the algorithms in a single place... replication code may lead to errors, if a change is made in one version and not other.

I have tried to do something of the type

if(option)
{
  #define ABC
} 
else
{
  #undef ABC
}

but the debugger skips over and does not see ABC as defined even when going over the "yes" branch.

Is there a way to untangle this code, or do i have to create clear separate paths - duplicating the entire code ?

Upvotes: 0

Views: 204

Answers (1)

Gutblender
Gutblender

Reputation: 1350

I've done something like this before. I had a library for which I had no source code, but I did have a header. There was tons of code that used this library, too, way more than I could change myself.

Well I had to code up another library to do the same thing as this other one, but with a different device, driver etc. You get the picture. So at runtime I wanted to substitute my library for theirs, but still have the ability to use the old (had to be backwards-compatible).

So what I did was this: I came up with a few handy macros that could transform text into other definitions. There are 2 header files, and one source file. For example, say I want to substitute FooLibrary for BarLibrary selectable at runtime. In one header file called FooLibrary.h:

// don't ask me why, but two levels of indirection is necessary
// to pull off a certain concatenation
// this I got from another question on SO
#define CAT(x, y) x ## y
#define cat(x, y) CAT(x, y)

#ifdef FOO_HIJACK_NAMESPACE

// library init functions
bool ShouldHijack();
void SetHijack(bool yn);

// handy-dandy function-changing macros
#define FOO_FN_NAME(X) cat(X, FOO_HIJACK_NAMESPACE)
#define FOO_FN_NAME_NS(X) FOO_HIJACK_NAMESPACE :: FOO_FN_NAME(X)

#define FOO_DECL(X) FOO_API FOO_FN_NAME(X)
#define FOO_IMPL(X) FOO_API FOO_FN_NAME_NS(X)
#define FOO_CALL(X) (ShouldHijack() ? FOO_FN_NAME_NS(X) : X)

namespace FOO_HIJACK_NAMESPACE {
#else

#define FOO_DECL(X) X
#define FOO_IMPL(X) X
#define FOO_CALL(X) X

#endif // FOO_HIJACK_NAMESPACE

Then, inside this namespace wrapper you re-define the function calls like so: say there's a function bool BarFun(fooargs...) in your other library, you might redefine it as

bool FOO_DECL(BarFun)(fooargs...);
// more FOO_DECL's...

in FooLibrary.h. FOO_API is your usual cdecl-type junk, for me I was doing #define FOO_API WINAPI for example. Add similar declarations til the end, and don't forget to close the namespace:

#ifdef FOO_HIJACK_NAMESPACE
}
#endif // FOO_HIJACK_NAMESPACE

Then, in FooLibrary.cpp, you provide an implementation like so:

#include "FooLibrary.h"

// hijack
static bool bShouldHijack = false;
bool ShouldHijack() { return bShouldHijack; }
void SetHijack(bool yn) { bShouldHijack = yn; }

bool FOO_IMPL(BarFun)(fooargs...)
{
    // your implementation here...
}
// more FOO_IMPL's...

and so on. Finally, you make one more header file I like to call "FooLibraryHijacked.h":

#include "FooLibrary.h"
#define BarFun FOO_CALL(BarFun)
// more #define's...

Provide one re-declaration, one re-implementation, and one #define for each function. I had some 20 of these functions so I used regex to generate them all for me.

Finally, to use it all, in the target code file, #include "FooLibraryHijacked.h" as your last #include, or at least after you #include "BarLibrary.h". Then #define the macro FOO_HIJACK_NAMESPACE as something, say "Foo", and BarFun becomes Foo::BarFunFoo after the preprocessor gets a hold of it.

Now how to do it with separate type definitions will be interesting. Perhaps you could use opaque pointers if the application code is only passing variables from one function to another.

Upvotes: 1

Related Questions