Question

I would like to create two macros. One of them will expand to function prototype and function content and the other one will expand to only function prototype. I'm thinking to create followings:

#ifdef SOME_CONDITION
#define MY_MACRO(prototype, content) prototype;
#else
#define MY_MACRO(prototype, content) prototype content
#endif

As an example usage

MY_MACRO(int foo(int a, int b)
,
{
     return a + b;
}
)

These macros seems working fine. Do you think are those macros safe enough so that they will work for every kind of C code as intended? Or do you see any pitfall?

Was it helpful?

Solution 4

I actually did something similar quite recently and learned the pitfalls of passing in code as a macro argument.

For example try this seemingly correct code (using the first macro definition):

MY_MACRO(int foo(int a, int b)
,
{
    int c = 1, d = 2;
    return a + b + c + d;
}
)

You'll most likely see a compile error something to the tune of the macro expecting only 2 arguments while you provided 3.

What happens is that the macros are compiled by the pre-processor which doesn't care about C syntax. So in it's eyes, each , is a argument separator. So it thinks that the first argument to the macro is int foo(int a, int b), the second argument { int c = 1 and the third argument as d = 2; return a + b + c + d; }.

So basically, the moral of the story is that "never pass in code as argument". Most macros that want to be flexible code-wise compile down into a if or a for statement (e.g. list_for_each in http://lxr.free-electrons.com/source/include/linux/list.h#L407).

I'd suggest that you stick to standard #ifdefery in your situation. e.g.

#ifndef UNIT_TEST

int foo(...) {
   //actual implementation
}

#else

int foo(..) {
    return 0;
}
#endif

This sort of code is fairly standard: http://lxr.missinglinkelectronics.com/#linux+v3.13/include/linux/of.h (search for CONFIG_OF)

OTHER TIPS

The first major pitfall, it doesn't work. When the second macro is used, it creates

int foo(int a, int b), { return a + b; }

which is not a valid function definition. To fix this, you must remove the , in the macro definition.

The second pitfall I see, usually C programmers don't use such fancy macros. It's simply confusing, when you're used to reading C source code.

If you're worried about diverging prototype declarations and corresponding function definitions, I suggest using appropriate compiler flags or tools. See this question and answers, How to find C functions without a prototype?

There are a lot of pitfalls, it is simply too naive, I think.

  1. never have macros that change the grammatical parsing, here in particular that add a ; at the end. Nobody will be able to comprehend code like that that has function-like macros invocations in file scope without a terminating semicolon.
  2. your macro expects two arguments, exactly. If your code block in the second argument contains an unprotected , operator, you are screwed.
  3. Your second variant should definitively not have a , on the right hand side.

This would work a bit better

#ifdef SOME_CONDITION
#define MY_MACRO(prototype, ...) prototype
#else
#define MY_MACRO(prototype, ...) prototype __VA_ARGS__ extern double dummyForMY_MACRO[]
#endif

you'd have to use that as

MY_MACRO(int foo(int a, int b), { return a + b; });

So this provides at least something visually more close to C code (well...) and handles the problem of the intermediate commas. The unused variable dummyForMY_MACRO should cause no harm, but "eats" the ; in the second form.

Not that I'd suggest that you use such a thing untested like this.

Do you think are those macros safe enough so that they will work for every kind of C code as intended? Or do you see any pitfall?

Do not attempt to re-invent the C language. The people who read your code will be other C programmers. You can expect them to know C. You cannot expect them to know "the-home-brewed-garage-hacker-macro-language".

Strive to write code that is as simple as readable as possible. Avoid complexity, avoid confusion. Don't attempt to create solutions when there exists no problem to solve.

Find below example which helps you to fulfils both of your requirements:

#ifdef SOME_CONDITION
   #define MY_MACRO(a, b) \
   int foo(int a, int b);
#else
   #define MY_MACRO(a, b) \
   int foo(int a, int b) \
   {\
      return 0;\
   }
#endif

You can use 1st macro by MY_MACRO(a, b) and 2nd macro as MY_MACRO(a, b);

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top