AlexCawley
AlexCawley

Reputation: 27

In C can you pass a macro with multiple values to a function

I have a messaging function that takes a variable amount of arguments because it supports string formatting in the same way printf does. My plan was to then define message code and messages with a single #define and be able to call this function by passing it a single argument for something that is tightly related.

Here is the message macro:

#define MSG(a, b, ...) message(__FILE__, __LINE__, a, b, __VA_ARGS__);

And here is an example of the message code and string define:

#define MSG_INIT 0000,"%s INITIALIZED SUCCESSFULLY"

The issue is occurring when I try to make a call in the form of:

MSG(MSG_INIT);

My IDE is giving me an error along the lines of:

'Expands to message("file.c", 1, 0000, "INITIALIZED SUCCESSFULLY",);

where the last ',' is causing the issue.

I was expecting to see:

'Expands to message("file.c", 1, 0000, "INITIALIZED SUCCESSFULLY");

If I change the call to:

MSG(MSG_INIT,NULL);

All is good but I'd rather not pass the NULL because the point of the MSG_INIT define in the first place was to pass less parameters. There are messages that make use of the __VA_ARGS__ (Example: #define MSG_CONNECT 0001,"CONNECTION TO [%s] ESTABLISHED" with matching call MSG(MSG_CONEST, server_ip);) so getting rid of that is not an option. I am just looking for the best way to fix this problem and I'm aware a lot of people suggest staying away from the preprocessor but this was the best solution I thought of.

Upvotes: 0

Views: 1161

Answers (1)

paxdiablo
paxdiablo

Reputation: 881103

I'm aware a lot of people suggest staying away from the preprocessor ...

I wonder why that is. Could it possibly be for the same reason you had to ask this question? :-)

Honestly, the pre-processor should be limited pretty much to including header files and doing conditional compilation. Anything else that it used to be used for, such as:

#define RC_OK    0
#define RC_NOMEM 1

#define HALF_OF(x) x / 2

is far better done with enumerations or functions.

The days of function-like macros should be put behind us, especially since modern compilers can quite easily out-optimise most hand-crafted code, and without subtle errors creeping in such as with that HALF_OF macro above (do not use it, I specifically made it buggy to illustrate a point).

It's really just a text substitution thing, one which understands the lexical elements, but not the full grammar of C. The fact that you have to perform what I call "macro gymnastics" to get it to do anything other than simple stuff is reason enough to steer clear. Use functions for this, it'll make your life a lot easier.


And apologies for not solving your specific issue, I just think any solution using the pre-processor is going to be half-baked at best. But there's plenty of precedent on SO for answering the question "How do I X?" with "Don't X, it's better to Y." Think of writing an accounting package in assembler, an operating system in object-oriented COBOL, or anything in Pascal :-)

But, if you really wanted to stick to macros against my advice, you could try starting with something like:

#include <stdio.h>

#define MSG(fmtStr, ...) printf("%s:%d> " fmtStr, __FILE__, __LINE__, __VA_ARGS__)
#define MSG_INIT "Initialised %s\n"
#define MSG_TERM "Terminated %s with values %d and %d\n"

int main(void) {
    MSG("%f\n", 42.3);
    MSG(MSG_INIT, "something");
    MSG(MSG_TERM, "something else", 7, 42);
}

I think that's portable, and it allow you to put the heavy lifting into MSG and using a separate format string and parameters for the rest of the stuff:

testprog.c:8> 42.300000
testprog.c:9> Initialised something
testprog.c:10> Terminated something else with values 7 and 42

Upvotes: 1

Related Questions