0

I have a macro which I use to log, and within the macro I'd like to define a temprecord as follows:

#define MACRO_LOG(...)             \
temprecord t;                      \
logger->logmessage(__VA_ARGS___); 

with temprecord() { logger->increaseIndent() } and in the destructor a decreaseIndent().

The association with a name (e.g., variable) gives an object scope which controls its lifetime. By not naming an object, it's lifetime is bound

source Why do un-named C++ objects destruct before the scope block ends?

Because the temprecord has lifetime of the scope, if I have

{
   MACRO_LOG("stackoverflow example");
   MACRO_LOG("stackoverflow example");
}

Outside the scope I will have 0 indent and in the scope 1 and 2 indents. However, as I name my temprecord t I get a redefinition of variable names.. and if I do not declare it I do not get the scope that I want.

How to go about this? I thought of using a container to put the temprecords in, but can't seem to solve the puzzle..

What I wish to accomplish is double indentation in my mentioned scope, and not single indentation within the MACRO_LOG scope.

dejoma
  • 394
  • 1
  • 6
  • 18
  • 1
    If you observe the fact that using a macro is equivalent to inserting the macro's contents directly where it is referenced, and if you work out, with pen and paper, what that means in your example, the reason for your duplicate symbol error becomes very obvious, as well as the simple solution for it. – Sam Varshavchik Jun 15 '20 at 18:58

1 Answers1

2

Macro replacement is just an in-place text replacement. So, given the #define you have shown, the code:

{
   MACRO_LOG("stackoverflow example");
   MACRO_LOG("stackoverflow example");
}

Expands to this:

{
    temprecord t;
    logger->logmessage("stackoverflow example");;
    temprecord t;
    logger->logmessage("stackoverflow example");;
}

Which should make it obvious why the macro doesn't work when used multiple times in the same scope, since t is declared multiple times.

There are some different ways you can solve this:

1) wrap the content of MACRO_LOG() in curly braces so that temprecord t is in its own scope, eg:

#define MACRO_LOG(...) { \
    temprecord t;                      \
    logger->logmessage(__VA_ARGS___);  \
}

Which would expand the code to this:

{
    {
        temprecord t;
        logger->logmessage("stackoverflow example");
    };
    {
        temprecord t;
        logger->logmessage("stackoverflow example");
    };
}

2) append __LINE__ to t to give each copy of the temprecord a more unique name, eg:

(See Creating C macro with ## and __LINE__ (token concatenation with positioning macro))

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)

#define MACRO_LOG(...) \
temprecord TOKENPASTE2(t_, __LINE__); \
logger->logmessage(__VA_ARGS___);

Which would expand the code to this:

{
    temprecord t_12345;
    logger->logmessage("stackoverflow example");;
    temprecord t_12347;
    logger->logmessage("stackoverflow example");;
}

3) use the comma operator to define a nameless variable in the same expression that calls logmessage(), the variable will not go out of scope until logmessage() returns, eg:

#define MACRO_LOG(...) temprecord(), logger->logmessage(__VA_ARGS___);

Which would expand the code to this:

{
    temprecord(), logger->logmessage("stackoverflow example");;
    temprecord(), logger->logmessage("stackoverflow example");;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I am going to try 2, but 1) and 3) are solutions that I initially did before posting this (and that does not work because they close whenever the macro scope is closed). – dejoma Jun 15 '20 at 19:14
  • Awesome, 2) works with tokenizing thanks a lot! But 1) doesn't work because you get the 2 separate scopes within the scope, I see you just edited that. And 3 doesn't work because after the semi-colon on each line the destructor get's called. – dejoma Jun 15 '20 at 19:19
  • 1
    They both *work*, just not the way you want them to. Frankly, I wouldn't put `temprecord t;` inside of `MACRO_LOG()` at all, eg: `#define MACRO_LOG(...) logger->logmessage(__VA_ARGS___);` ... `{ temprecord t; MACRO_LOG("stackoverflow example"); MACRO_LOG("stackoverflow example"); }` But, lets say you have other uses of `MACRO_LOG()` that warrant the `t` variable, then I would create another macro for this particular use case: `#define MACRO_LOG_NO_T(...) logger->logmessage(__VA_ARGS___);` ... `{ temprecord t; MACRO_LOG_NO_T("stackoverflow example"); MACRO_LOG_NO_T("stackoverflow example"); }` – Remy Lebeau Jun 15 '20 at 19:26
  • In that case all current uses of MACRO_LOG in all projects will have to be accompanied by `temprecord t;` declarations within that scope if I understand correctly? That's the only reason why I am not adapting that... but maybe that's the way to go. Thanks for thinking along! – dejoma Jun 15 '20 at 19:38
  • @dejoma only in the places where you call `MACRO_LOG()` multiple times in the same scope, to avoid multiple `t` variables in the same scope. Why declare multiple variables when 1 variable will suffice? But it is up to you, that is why I also gave you the `__LINE__` alternative, too. – Remy Lebeau Jun 15 '20 at 19:49