views:

276

answers:

8

I've been hacking on a program that itself creates simulation programs in C. The user specifies the top-level design, and this programs inserts small C-fragments and a helluvalot glue-code (a few thousand lines).

It does local naming by #defines:

#define x local_x
#define vx local_vx
/* user code that uses x, ex */
vx = x / 2
#undef vx
#undef x

This approx expands to the following:

local_vx = local_x / 2

BUT if I use structs for the local_*-variables (optimize away having to pass 11 variables to every single function...):

#define x local->position.x
#define vx local->velocity.x
vx = x / 2
#undef vx
#undef x

Which gets expanded to

local->velocity.x = local->position.x

And - here's the problem - the x in the velocity gets expanded again:

local->velocity.local->position.x = local->position.x

I can't put parenthesis around, as it is not allowed to assign to the variables ((x) = 1 is illegal C, unfortunately...). Any hints?

Update: The generated simulations generally weigh in around 15 to 20.000 LOC, and there are roughly ten years worth of simulations to be backwards compatible with. Alas, simply renaming anything is not simple at all... As there does not seem to be any easy way to get around this particular problem without some major re-engineering (I thought I'd missed some particularities of the C pre-processor), I've chosen to take a step back and see what other options I have.

A: 

what compiler are you using Morten? Your first step could be to check out the compiler reference manual to see if there are any options you can change regarding levels of preprocessing or recursive substitution.

dls
I don't think there's ever recursive substitution in C preprocessor.
Nosredna
I currently use `gcc`, but I have to be compatible with the C-compilers from Microsoft and Intel as well.
Morten Siebuhr
Nosredna - yup, you are correct, my mistake.
dls
+4  A: 

It's not actually being recursive, what is happening is that

#define x local->position.x
#define vx local->velocity.x

is being expanded to

#define x local->position.x
#define vx local->velocity.local->position.x

which is then included in your statement later on. I'm not sure how you want to get around this but i'd say change your variable names/#define names to be something more unique to avoid this.

Salgar
Yeah change "x" to "px"
Nosredna
Damn! That's it! Changing too much in the variable names is not really an option, as there's ~10 years of written simulation that have to be compatible...
Morten Siebuhr
I've tried to re-order the `#defines`, so `x` is the last one, but then I just hit the problem I describe in the original question.
Morten Siebuhr
Reordering defines won't help; what's important is what defines are present when it's expanded.
bdonlan
I don't think I understand...you can't change the variable names in the struct? I thought you just introduced this struct recently as part of your refactoring.
A. Levy
The struct is composed of three coordinate structs, wich are already present in the sytem, and have a lot of code written for them. Alas, I made a new struct to avoid a lot of pacing/unpacking of said variables between function-calls, various enums and the code given from the user.
Morten Siebuhr
This description of the macro being expanded as it is defined is simply incorrect - bogus. The substitution of `local->position.x` for `x` occurs when the macro `vx` is found in the code.
Jonathan Leffler
A: 

If the defines must be x and vx (with is not so great, as you might have noticed) one way to solve this is to change the member of the struct/class local->velocity.x into local->velocity.x_ (or something similar).

Joakim Elofsson
+1  A: 

If you're using C++, you may want to consider using references instead:

int &x = local->position.x;

Since you're writing a code generator, it shouldn't be too hard to make sure they exist for the right scope:

{
    int &x = local->position.x;
    int &y = local->position.y;
    int &vx = local->velocity.x;
    int &vy = local->velocity.y;
    {
#line user.input 1234
        // user code
#line output.c 4567
    }
}

As an added bonus, the additional set of inner braces above allows the user code to shadow x if it intends to use the local pointer directly.

If you're not using C++, consider doing do - the biggest source of incompatibility between C and C++ would be the lack of implicit void pointer casts, which I would suspect are rare in your input fragments...

bdonlan
+2  A: 

Just a crazy idea but how about instead of this

#define x local->position.x
#define vx local->velocity.x
vx = x / 2
#undef vx
#undef x

Why not just name it something different? like

#define x local->position.val
#define vx local->velocity.val
vx = x / 2
#undef vx
#undef x

Here is an example program that runs fine under gcc 4.3.2

int main(int argc, char *argv[])
{
   typedef struct
    {
        unsigned char val;
    } Value;

    typedef struct
    {
        Value position;
        Value velocity;
    } Holder;

    Holder temp;
    Holder* local = &temp;
    #define x local->position.val
    #define vx local->velocity.val
    vx = x / 2;
    #undef vx
    #undef x

    return 0;
}
messenger
A: 

I'd make position and velocity arrays. Your definitions would then look like this

#define x local->position[0]
#define vx local->velocity[0]

so there's no longer a possibility for repeated macro expansion.

Renaming the structure member to something different from x would work as well, but I actually find that arrays make more sense here anyway.

Christoph
A: 

Assuming that:

  • You can't change the names of the structure members, and
  • You can't change the names of the defines

Then one way is to create a shadowing struct type. Say your position and velocity members are of this type:

struct vector {
  double x;
  double y;
};

Then you create a shadow type which is identical except for the names of the members, and a union containing both to get around aliasing rules:

struct _vector {
  double _x;
  double _y;
};

union _u_vector {
    struct vector _v1;
    struct _vector _v2;
};

and then your defines can be:

#define x ((struct _vector *)(union _u_vector *)(&local->position))->_x
#define vx ((struct _vector *)(union _u_vector *)(&local->velocity))->_x

It's a bit of a hack, but you're pretty constrained. Note that the (&struct)->member pattern will be optimised down to struct.member so this won't have any runtime overhead.

Alternatively if the definition of the "local" struct is up to you, you could make "position" and "member" pointers to the union type, and remove the need for the casting.

caf
A: 

Remove the #defines and change the code generator to generate the appropriate code. The #defines you described don't seem to be buying you anything except slightly shorter identifiers (or there's something important going on that you didn't mention). Expand variables in the code generator, not in the C preprocessor.

Regarding compatibility with 10+ years of simulations, I would hope that the original input files to the code generator have been saved so you can run it again if necessary (or even better, that generating the code is part of he build process). If this is some sort of interactive code-generating wizard and/or developers edit the generated code, you're already in a world of hurt, and I have to wonder how you're making any significant changes to the generated code in the first place (manually? post-processing script?).

bk1e