views:

153

answers:

7

Hi,

that may be really simple but i'm unable to find a good answer. How can I make a macro representing first a certain value and then a different one ?

I know that's nasty but i need it to implicitly declare a variable the first time and then do nothing. This variable is required by other macros that i'm implementing.

Should i leverage "argument prescan" ?

thanks for the answers.

EDIT The thing you need to know is the fact i'm generating the code.

#define INC_X x++ //should be declared if needed to
#define PRINT_X printf("VALUE OF X: %d\n", x)

int func() {
[...]
INC_X;
[...]
INC_X;
[...]
PRINT_X;
[...]
}
+3  A: 

As far as I know, this is impossible. I know of no way for the expansion of a macro to control the way another macro -- or itself -- will be expanded after. C99 introduced _Pragma so that #pragma things can be done in macros, but there is no equivalent for #define or #undef.

AProgrammer
Even if this was possible, it would arguably be a case of pre-processor abuse and would be an easy place for all sorts of nasty bugs to creep in (not to mention making the program harder to debug). If you need this level of complexity, a function is better suited for the task (and you can always alias the function with a `#define` if needed).
bta
A: 

What about having the macro #define some flag at the end of it's execution and check for that flag first?

#def printFoo
   #ifdef backagain
       bar
   #else
       foo
       #def backagain

Need to add some \ chars to make it work - and you probably don't want to actually do this compared to an inline func()

Martin Beckett
yep, this is what i wanted to do but I didn't know where to put the \ chars. The problem with an inline func is the scoping of the variables.
LB
+1  A: 
#include <stdio.h>
#define FOO &s[ (!c) ? (c++, 0) : (4) ]
static int c = 0;
const char s[] = { 'f', 'o', 'o', '\0', 'b', 'a', 'r', '\0' };

int main() {
 puts(FOO);
 puts(FOO);
 return 0;
}

Does the above help?

dirkgently
Tricky tricky. :]
GMan
way too tricky.
LB
@LB: Let us know if this helps solve your problem or not. I am waiting with bated breath ;)
dirkgently
I had to look up whether `?` is a sequence point. It is, but the fact that I had to look that up would make me uncomfortable using this. Maybe `(!(c++)) ? 0 : 4` would be clearer.
asveikau
@asveikau: What value does the extra-parentheses around the not operation bring? AFAIK, the ternary operator is used in terser ways e.g. I'd have simply written `!c ? (c++, 0) : 4` had it not been for readability. And further, as the OP mentions, this is not about readability.
dirkgently
that doesn't declare any variable at all, all of them must be pre-declared... am I right?
fortran
@fortran: You can wrap the declaration in a different macro of course.
dirkgently
then it's not what the op was asking... :-/ anyway. it's pretty confusing as it is, I think some re-engineering of the solution would be better than hacking with macros here
fortran
A: 
#define FOO __COUNTER__ ? bar : foo

Edit: removed all unneeded code

Tuomas Pelkonen
and if i want to expand something different according to the value of __COUNTER__
LB
Edited to show how to use it for different values.
Tuomas Pelkonen
the problem is that you're mixing C and CPP code, and the tokens wich are going to be expanded have a meaning a C, your array cannot contain arbitrary tokens which are going to be expanded into a variable declaration
LB
What do you mean mixing C and CPP? The second example can expand V2 and V1 to pretty much anything. I guess there is something that I do not completely understand, if the second example won't work for you.
Tuomas Pelkonen
Can you expand a variable declaration using your approach ?
LB
Yes, you should be able to expand anything. 'foo' and 'bar' can be other macros, variables, functions, etc. Try it out, if you can, because I am still not sure if this is what you actually want.
Tuomas Pelkonen
+1  A: 

From the look of it, you could try if Boost.Preprocessor contains what you are looking for. Look at this tutorial

http://www.boostpro.com/tmpbook/preprocessor.html

from the excellent C++ Template Metaprogramming book.

Francesco
+1 for "use a better preprocessor", even though I tried to answer the question anyway. If it can be done in Boost.Preprocessor then I guess it can be done in CPP, at least on the platforms Boost supports, but I don't know how. Personally I'd probably `awk`/`python`/whatever the source in the makefile.
Steve Jessop
A: 

An alternative to some of the methods proposed thus far would be to use function pointers. It might not be quite what you are looking for, but they can still be a powerful tool.

void foo (void);
void bar (void);

void (*_func_foo)(void) = foo;

void foo (void) {
    puts ("foo\n");
}

void bar (void) {
    puts ("bar"\n");
}

#define FOO()  _func_foo(); \
               _func_foo = bar;

int main (void) {
    FOO();
    FOO();
    FOO();
    return 0;
}
Sparky
+1  A: 

With the edit, I'll have a go at an answer. It requires your compiler to support __FUNCTION__, which MSVC and GCC both do.

First, write a set of functions which maps strings to integers in memory, all stored in some global instance of a structure. This is left as an exercise for the reader, functionally it's a hashmap, but I'll call the resulting instance "global_x_map". The function get_int_ptr is defined to return a pointer to the int corresponding to the specified string, and if it doesn't already exist to create it and initialize it to 0. reset_int_ptr just assigns 0 to the counter for now, you'll see later why I didn't just write *_inc_x_tmp = 0;.

#define INC_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    /* maybe some error-checking here, but not sure what you'd do about it */ \
    ++*_inc_x_tmp; \
} while(0)

#define PRINT_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    printf("%d\n", *_inc_x_tmp); \
    reset_int_ptr(&global_x_map, _inc_x_tmp); \
} while(0)

I've chose the separator "{}" on the basis that it won't occur in a mangled C function name - if your compiler for some reason might put that in a mangled function name then of course you'd have to change it. Using something which can't appear in a file name on your platform would also work.

Note that functions which use the macro are not re-entrant, so it is not quite the same as defining an automatic variable. I think it's possible to make it re-entrant, though. Pass __LINE__ as an extra parameter to get_int_ptr. When the entry is created, store the value of __LINE__.

Now, the map should store not just an int for each function, but a stack of ints. When it's called with that first-seen line value, it should push a new int onto the stack, and return a pointer to that int thereafter whenever it's called for that function with any other line value. When reset_int_ptr is called, instead of setting the counter to 0, it should pop the stack, so that future calls will return the previous int.

This only works of course if the "first" call to INC_X is always the same, is called only once per execution of the function, and that call doesn't appear on the same line as another call. If it's in a loop, if() block, etc, it goes wrong. But if it's inside a block, then declaring an automatic variable would go wrong too. It also only works if PRINT_X is always called (check your early error exits), otherwise you don't restore the stack.

This may all sound like a crazy amount of engineering, but essentially it is how Perl implements dynamically scoped variables: it has a stack for each symbol name. The difference is that like C++ with RAII, Perl automatically pops that stack on scope exit.

If you need it to be thread-safe as well as re-entrant, then make global_x_map thread-local instead of global.

Edit: That __FILE__ "{}" __FUNCTION__ identifier still isn't unique if you have static functions defined in header files - the different versions in different TUs will use the same counter in the non-re-entrant version. It's OK in the re-entrant version, though, I think. You'll also have problems if __FILE__ is a basename, not a full path, since you could get collisions for static functions of the same name defined in files of the same name. That scuppers even the re-entrant version. Finally, none of this is tested.

Steve Jessop
ok, i see what you're doing. The macro declaration is the best. :-)I've accepted AProgrammer's answer, because it doesn't seem to be possible.
LB