views:

479

answers:

4

what I'd like to do (for logging purposes) is something like this:

this code has been written to show my problem, actual code is complex and yes, I have good reasons to use macros even on C++ =)

# define LIB_SOME 1
# define LIB_OTHER 2

# define WHERE "at file #a, line #l, function #f: "
// (look for syntax hightlighting error at SO xd)
# define LOG_ERROR_SIMPLE(ptr, lib, str) ptr->log ("ERROR " str \
                                                   " at library " #lib);
# define LOG_ERROR(ptr, lib, str) LOG_ERROR_SIMPLE(ptr, lib, WHERE str)

LOG_ERROR_SIMPLE (this, LIB_SOME, "doing something")
LOG_ERROR (this, LIB_OTHER, "doing something else")

LOG_ERROR_SIMPLE() writes the stringification of the lib parameter (a macro name surrounded by " ")

but then LOG_ERROR writes the stringification of the macro already expanded ("2"). this is expected, since lib got its expansion before expanding and calling LOG_ERROR_SIMPLE. but this is not what I need.

basically my question is this: how to avoid macro expansion of a macro function parameter when calling another macro function?

there is a trick I use that avoids macro expansion:

  LOG_ERROR(ptr, lib, str, x) LOG_ERROR_SIMPLE(ptr, x##lib, WHERE str)

  LOG_ERROR(this, LIB_OTHER, "some error",)

(pasting x and lib produces LIB_OTHER and this value is used to call LOG_ERROR_SIMPLE, its not macro expanded before that call)

There is some way to obtain this same behaviour without using a trick?

Thanks =)!

A: 

I don't think you can. What you could do, though, is add a layer of macro for it to unpeel in its place:

#define WRAP(x) x
#define LOG_ERROR(ptr, lib, str) LOG_ERROR_SIMPLE(ptr, lib, WHERE WRAP(str))
Kaz Dragon
BOOST_PP_IDENTITY(X)
KitsuneYMG
@kts Ooh, whaddya know. I think of something, and the boffins at Boost are waaaaaay ahead of me.
Kaz Dragon
neither WRAP or _IDENTITY works.. because (at least in CPP) it calls LOG_ERROR_SIMPLE with WRAP(<parameter expanded>) as the parameter, and not as "whats inside of the name but then not expanded"
conejoroy
Erk. In that case, I think you either foist LOG_ERROR(ptr, lib, WRAP(str)) on your users, or write out LOG_ERROR without referring to LOG_ERROR_SIMPLE.
Kaz Dragon
+3  A: 

I'm doing:

#include <cstdio>

#define FOO 1
#define BAR 2

#define LOG_SIMPLE(ptr, lib, str) printf("%s\n", #lib);
#define LOG(ptr, lib, str) LOG_SIMPLE(ptr, ##lib, str)

int main()
{
  LOG_SIMPLE(0, FOO, "some error");
  LOG(0, BAR, "some other error");
}

which prints out:

FOO
BAR

Works with MSVC2005 but not with gcc/g++.


EDIT: to make it work with gcc/g++ you can abuse variadic macros:

#include <stdio.h>

#define FOO 1
#define BAR 2

#define LOG_SIMPLE(ptr, str, lib) printf("%s\n", #lib);
#define LOG(ptr, str, lib, ...) LOG_SIMPLE(ptr, str, lib##__VA_ARGS__)

int main()
{
  LOG_SIMPLE(0, "some error", FOO);
  LOG(0, "some other error", BAR);
  LOG(0, "some other error", FOO, BAR);
}

However, it's your discipline not to use the macro with too many parameters. MSVC2005 prints out

FOO
BAR
FOO2

while gcc prints out

FOO
BAR
FOOBAR
Gregory Pakosz
When using GCC's preprocessor (removing the `#include` that does not exist), I get the correct program on stdout, but an error message on stderr. t.c:11:1: error: pasting "," and "BAR" does not give a valid preprocessing token
Pascal Cuoq
same here, doesn't work with gcc/g++
Gregory Pakosz
Well, "doesn't work" is a bit strong. I did get a compilable program on stdout when I ran `gcc -E t.c`.
Pascal Cuoq
yes, pasting is a way of not expanding "whats inside of lib" in place and letting it reach LOG_SIMPLE. my previous example pasted lib with an empty parameter, yours pasted lib with the left comma (which of course is not totally "right"). I wonder if I could paste lib with "a special, hidden parameter" to get the same results as using an extra parameter in the macro, deliverately left empty for that purpose..
conejoroy
I achieved something with variadic macros, see my edit
Gregory Pakosz
Gregory, I'm impressed but I can't vote you up again :)
Pascal Cuoq
yes It's a good trick! unfortunately I'm already using variadic macros on LOG and LOG_SIMPLE.. I didn't specify it for the sake of simplicity, my fault. that's why I wasn't counting on __VA_ARGS__ as a source for paste nothing in a more elegant way.
conejoroy
i tried more today without much luck, do we decide you can't avoid expansion in the general case?
Gregory Pakosz
Gregory, AFAIK the only way for a macro name inside a macro function parameter to survive more than 1 level of nested macro function calls to get #param -> "MACRO_NAME" in the 2nd call or higher is the trick we discovered (either by pasting param with `__VA_ARGS__` or with another empty one) perhaps there is no solution apart from that trick.. perhaps we should suggest the CPP developers to implement an "empty value macro parameter" (lets call it `__EMPTY__`) as to be able to pass a macro parameter to other macro functions without expansion using the trick.. I think it'll be useful if they do
conejoroy
A: 

You almost had it. Use

#define LOG_ERROR(ptr, lib, str) LOG_ERROR_SIMPLE(ptr, ##lib, WHERE str)

On gcc
LOG_ERROR(this, LIB_OTHER, "some error")
yields
this->log ("ERROR " "at file #a, line #l, function #f: " "some error" " at library " "LIB_OTHER");

I would also remove the trailing ';' from your macro so that your code would look like:
LOG_ERROR(this, LIB_OTHER, "some error");

KitsuneYMG
I think the OP wants to avoid the quoting of the argument hence the question. If "LIB_OTHER" was fine with him, I think he woudld'nt have asked in the first place
Gregory Pakosz
pasting lib with a comma gives this error: pasting "," and "LIB_OTHER" does not give a valid preprocessing token. is there some "neutral" character or hidden empty parameter I could use to paste lib with "something empty" and without errors?
conejoroy
Sorry. I misunderstood your needs. BOOST_PP_EMPTY is a macro that expands to nothing. You could also try /**/ (thats an empty c-style comment in case markdown kills it)
KitsuneYMG
Could you perhaps provide the desired output of your macro? Given LOG_ERROR(this, LIB_OTHER, "some error) what is the code you want generated?
KitsuneYMG
@kts, its more on the macro side itself, the way it works and macro-expands things, not C code generation. I just want to stringify LIB_OTHER to obtain "LIB_OTHER".. of course, LIB_OTHER must be passed as a parameter and survive 2 levels of macro function calls, something not trivial to achieve.
conejoroy
@conejoroy Would `BOOST_PP_IDENTITY( LIB_OTHER )` suffice? It expands to a token that, when called with () returns the original argument.
KitsuneYMG
@kts, no, inside of a macro function already called from the source, `LIB_OTHER` gets its expansion (from macro name to 2) before a second nested macro function is called, that is `BOOST_PP_IDENTITY( 2 )`... that macro function will always receive 2 instead of the macro name `LIB_OTHER`. thats the "2 level macro function call" problem I metioned, on the 2nd nested function call `LIB_OTHER` is always replaced by 2, there is no trivial workaround to avoid this, I just know one trick (pasting the parameter with another empty one, the result is `LIB_OTHER` and is left as is).
conejoroy
+2  A: 

If you don't need the expanded lib aliases (i.e. '1' & '2') in your cpp macros, you could also use an enum instead of defined values.

quinmars
Although I would have liked to find the solution to my macro expansion problem, you made me re-think one of its elements. I've changed my macro constants to enums, their name don't get macro expanded (since they aren't a macro) and now the problem is not solved, but its gone =)
conejoroy
Now I still want to solve it, but as far a my program is concerned, thanks to your suggestion I dont need to..
conejoroy