1

In my VC++ project I use a macro for debugging and logging purpose.

calling:

  Logger(LogLevel::Info, "logging started");

macro:

#ifdef DEBUG
    #define Logger(level, input) \
            { \
                cerr << "[" << level << "] " << input << endl; \
            };
#else
    #define Logger();
#endif

When compiling this I get the following warning (but it still compiles):

warning C4002: too many actual parameters for macro 'Logger'

I am wondering how the compiler handles this situation. Are the macro parameters still used for compiling? E.g. will one be able to see the "logging started" string on reverse engeneering?

Peter
  • 35,646
  • 4
  • 32
  • 74
Jenny
  • 572
  • 4
  • 16
  • 4
    why not add the parameters in the #define and then don't use them in the substitution? – Ben Voigt Feb 10 '18 at 05:27
  • 1
    @BenVoigt that's what I have currently. I am wondering how this described scenario works. Also thanks for downvote. SO is fun for new people. – Jenny Feb 10 '18 at 05:28
  • @Thomas It _works out_ through plain text substitution as always, noting "special". –  Feb 10 '18 at 05:30
  • In the `#else` simply do `#define Logger(level, input)`. That will ignore the parameters. Also, get out of the habit of adding semi-colons at the end of macro definitions. Lastly, your shown "call" is using `Log` not `Logger`. – Peter Feb 10 '18 at 05:33
  • @Peter I can't edit the question anymore. If you look at the edit draft you can see I changed it to Log. – Jenny Feb 10 '18 at 05:34
  • @Thomas - yeah, okay. I'll edit to fix shortly. – Peter Feb 10 '18 at 05:35
  • One way to allow your macro to be used in blocks (like `if` statements) and avoiding the extra semicolon problem is to wrap the macro in a null-do-while like this: `#define Logger(level, input) do{ std::cerr << "[" << level << "] " << input << '\n'; }while(0)` – Galik Feb 10 '18 at 05:47

2 Answers2

3

I am wondering how the compiler handles this situation. Are the macro parameters still used for compiling?

Macros are processed in the pre-processing stage. If the pre-processor is able to deal with the extra arguments used in the usage of the macro, the only thing it can do is drop those parameters in the macro expansion.

E.g. will one be able to see the "logging started" string on reverse engeneering?

No, the code processed by the compiler will not have that line at all.


If you have the option to change those lines of code, I would recommend changing the non-debug definition of the macro to be a noop expansion. E.g.:

#define Logger(level, input) (void)level; 
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Didn't know about __noop, thanks. I guess the compiler removes the entire function call when it sees a call to noop function? – Jenny Feb 10 '18 at 05:40
  • @Thomas, I am not sure whether it removes it completely or leaves some code that has very small execution footprint. – R Sahu Feb 10 '18 at 05:44
  • @R Sahu According to https://stackoverflow.com/a/14459264/9279154 the entire function won't be called. Also thanks for being the only one actually answering the question. Appreciate that. – Jenny Feb 10 '18 at 05:54
  • @Thomas, unfortunately, `__noop` is non-standard feature in VS. You can't depend on it if you are writing code for other platforms. – R Sahu Feb 10 '18 at 06:01
  • The "compiler" doesn't remove anything. The compilation process (as specified in the standard) has a preprocessing phase (notionally performed by a "preprocessor") occur first, and the output of that (preprocessed source) is then fed into a subsequent compilation phase (notionally executed by a "compiler"). If a macro argument expands to nothing in the preprocessing phase, it won't even exist during the compilation phase, let alone be output (e.g. to an object file or executable). – Peter Feb 10 '18 at 06:02
1

As per the law, a macro function must declare as many parameters as you call it with.

So calling your macro

#define Logger()

as Logger(LogLevel::Info, "logging started") results in an error. MSVC probably allows it because it isn't standard-compliant. There's not much to reason further (which is my answer to the actual question).

You either

#define Logger(unused,unused2)

and leave the replacement part empty or

#define Logger(...) 

and suffer the consequences of being able to call with any number of arguments. [Hint: First one is recommended.]

DeiDei
  • 10,205
  • 6
  • 55
  • 80
  • 1
    And ``#define Logger(...)`` assumes you are using a C99 conformant C preprocessor that supports variadic macros. – Chuck Walbourn Feb 10 '18 at 05:42
  • It's not an error. MSVC warns in this situation but still compiles. – Jenny Feb 10 '18 at 05:43
  • 1
    @Thomas That doesn't mean it's standard behavior. Three other compilers disagree and emit a hard error. – DeiDei Feb 10 '18 at 05:46
  • There's nothing "not standard compliant" in accepting invalid code: as long as a diagnostic message is issued the standard requirements are fully met. Neither C nor C++ differentiates between "errors" and "warnings". They are all just diagnostic messages. In this specific respect (i.e. number of *extensions*) MSVC is probably one of the *most* "standard compliant" compilers on the market. – AnT stands with Russia Feb 10 '18 at 05:51
  • @Thomas: It is an "error" in a sense that your code is *ill-formed* (which is what we typically refer to as "error"). However, compilers are not required to reject your code and stop compilation when they encounter ill-formed code. They just have to inform you about the problem in some implementation-dependent manner. They can give you a meaningful message. Or they can just print "Mew! Bark!" and keep compiling. The standard does not require anything more than that. – AnT stands with Russia Feb 10 '18 at 05:54