0

I am having issues while trying to pass a macro constant as an argument to a macro function.

Consider the following code -

#define ERROR 10
#define MAIN "Main:"

#define LOG(lvl,mod,fmt,...) \
    char msg[256] = {0}; \
    snprintf(msg, 256, "%s: %d: "fmt,mod,lvl,##__VA_ARGS__)

int main()
{   ....
    LOG(ERROR, MAIN, "This is a log statement.\n"); // Doesn't compile
    LOG(10, "Main:", "This is a log statement.\n"); // Compiles
    ....
}

The second log statement compiles but the first log statement generate the following compilation error -

error: expected `)' before ‘;’ token
error: expected primary-expression before ‘,’ token
error: expected `;' before ‘)’ token

Why is this happening? I want to be able to define a set of Logging levels and Modules constants, and use them while invoking the LOG() macro.

siri
  • 123
  • 1
  • 13
  • 3
    Unrelated to your (current) problem, but your `LOG` macro won't work with a simple `if` statement or a loop body without explicit bracers. There's a reason most people creating multi-statement macros surround them with `do { ... } while(0)`. – Some programmer dude Feb 14 '17 at 14:26
  • 6
    That macro is broken as it will declare multiple `msg` variables within the same scope. – Droppy Feb 14 '17 at 14:26
  • 1
    As for how to solve your problem, tell the compiler to stop after running the preprocessor, to see what code the macro expansion creates. – Some programmer dude Feb 14 '17 at 14:30
  • 2
    Not reproducible. Please provide a [mcve]. (That is , a complete short program which can be passed to a compiler and will demonstrate the problem.) Also please specify which compiler you are using, and with what command-line flags. – rici Feb 14 '17 at 14:43
  • I tried to keep the example simple. I know the msg variable will be declared twice. In this example I only try to use one of the LOG statements while compiling. – siri Feb 14 '17 at 14:56
  • Is there a way to debug the preprocessor directives? – siri Feb 14 '17 at 14:56
  • 2
    @siri, it is good to keep examples simple. But they must be reproducible. You should include in your question a complete program which we could try compiling. Something like [dasblinkenlight's example](http://ideone.com/F9TW7H). – rici Feb 14 '17 at 15:20
  • So see the pre-processor output do `cpp mysourcefile.c` – alk Feb 14 '17 at 15:58
  • I concur with @rici — I cannot reproduce the problem from the code in the question. I checked with GCC versions 4.8, 4.9. 5.1, 5.2, 5.3, 6.1, 6.2, 6.3 (for at least one sub-version in each of those releases) running on a Mac. – Jonathan Leffler Feb 14 '17 at 15:59

2 Answers2

0

There are three two issues I can see:

  1. You will have multiple msg variables within the same scope.
  2. You are missing a comma (as pointed-out by @Ninetainedo).
  3. You do nothing with the formatted string.

I would prefer to declare a global logger() function that accepts a logging-level and use macros to shorten calling it.

Droppy
  • 9,691
  • 1
  • 20
  • 27
  • The missing comma is not an issue. It allows concatenation of string constants. – 001 Feb 14 '17 at 14:37
  • @JohnnyMopp True. Corrected. – Droppy Feb 14 '17 at 14:39
  • Presumably the real code does do something with `msg`, but it is not relevant to the question and so has been omitted. – rici Feb 14 '17 at 14:45
  • I tried to keep the example simple. I do flush msg to disk using another function call from within the macro. – siri Feb 14 '17 at 14:58
  • I get an error only when I use macro constants as arguments to the LOG macro . Is there anyway to debug this? – siri Feb 14 '17 at 14:59
  • @siri It will depend on your compiler, but with `gcc`, for example, you can use `-E` to get the pre-processed output. – Droppy Feb 14 '17 at 15:05
  • @Droppy: we encourage posters to leave out irrelevant details; criticising them for doing so is not an effective communications strategy. The easiest problem to debug is one accompanied by a [mcve] with an emphasis on *minimal*. – rici Feb 14 '17 at 15:26
  • @rici I didn't criticise; i simply enumerated the issues that I saw. If the OP knows he's actually doing something with the formatted string then he can ignore my observation. There is no reason to assume that minimal or more important than complete when providing an example. – Droppy Feb 14 '17 at 15:50
0

One problem with your macro is that it declares a variable at a random spot in a block of code, which C89 does not allow: you were allowed to declare variables only at the top of a block. Even with C99 compiler, the problem does not go away, because now you can introduce multiple declarations of the same name in the same scope, which is prohibited.

You can use the do/while(0) trick to solve this problem:

#define LOG(lvl,mod,fmt,...) do {\
    char msg[256] = {0}; \
    snprintf(msg, 256, "%s: %d: "fmt,mod,lvl,##__VA_ARGS__) \
} while(0)
Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • It also doesn't explain why the second variant compiles, when the OP isn't using macros for the first two arguments. – Some programmer dude Feb 14 '17 at 14:34
  • 1
    @Someprogrammerdude The most likely reason is that there's something in front of OP's code that doesn't compile, where he has `.....`, because when you compile his code by itself, it compiles perfectly fine ([demo](http://ideone.com/F9TW7H)). – Sergey Kalinichenko Feb 14 '17 at 14:40
  • In my actual code, I am calling this LOG macro from within a constructor and it is the very first instruction in the constructor. Also, the macros are defined in a different .h file than where its being invoked from. The .h file has the macro definitions in the same exact order as my above example. Can the ordering of macro definitions have anything to do with this? – siri Feb 14 '17 at 15:02
  • 1
    @siri Wait, a constructor? This question is tagged `[C]`, which has no constructors. Are you writing in C++? – Sergey Kalinichenko Feb 14 '17 at 15:10
  • Yes, I am writing a C++ program. I tagged it with C++ but seems like it was removed? – siri Feb 14 '17 at 18:29