0

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?

  • Why are you doing this way? I suspect that second My_MACRO will work. – doptimusprime Apr 23 '14 at 10:59
  • In which case will you want this expansion: `#define MY_MACRO(prototype, content) prototype, content` – 0xF1 Apr 23 '14 at 11:23
  • What's the purpose of this ? – Jabberwocky Apr 23 '14 at 11:31
  • Let me try to explain why I need this. I need this to be able write unit tests. Say, I have two modules A and B. A module depends on B. I want to unit test A. B has a header file that has an inline function. Inline function has the definition (content) in the header file. Module A calls that function. While unit testing module A, I need to be able to create mock version of inline function in module B header. But since it has the implementation in header file, I cannot create a mock version of that function. That's why I need macros similar to ones in the question. – user3472693 Apr 23 '14 at 11:48

5 Answers5

4

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?

Community
  • 1
  • 1
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • Yes, this wouldn't work. I've corrected the question. I made the mistake while I was transforming original source code. – user3472693 Apr 23 '14 at 14:28
  • If you really want to do this, a [variadic macro](http://stackoverflow.com/q/679979/1741542) or http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html, like in Jens Gustedt 's answer, might do the trick. – Olaf Dietsche Apr 23 '14 at 15:15
2

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.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
1

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Actually, I'm trying to solve unit testing problem that I explained in the comment of the question. – user3472693 Apr 23 '14 at 14:22
  • @user3472693 So the problem you are trying to solve is "I can't create a function". This seems like an artificial problem. Why not? Why would it matter if it is inline or not? Writing obscure macros is most often not a good solution to anything. If the unit test is buggier than the code to be tested, it is worthless. – Lundin Apr 23 '14 at 14:38
0

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);

ravibhuva9955
  • 199
  • 1
  • 11
0

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)

R.D.
  • 2,471
  • 2
  • 15
  • 21