1

I'm using the variadic macro to simulate a default argument. I compile with -Wunused-value. Thus, I get the following warning:

warning: left-hand operand of comma expression has no effect

Is there a way to somehow fix this warning without having to remove -Wunused-value? or do I have to end up using #pragma GCC diagnostic ignored "-Wunused-value"?

#include <stdio.h>

#define SUM(a,...) sum( a, (5, ##__VA_ARGS__) )

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

int main()
{
  printf("%d\n", SUM( 3, 7 ) );
  printf("%d\n", SUM( 3 ) );
}
Pete Darrow
  • 455
  • 5
  • 20

2 Answers2

2

The ## construct that you are using is a gcc speciality and not portable. Don't use it, there are other ways.

The following should do what you expect

#define SUM2(A, B, ...) sum((A), (B))
#define SUM1(...) SUM2(__VA_ARGS__)
#define SUM(...) SUM1(__VA_ARGS__, 5, 0)

Such games with macro default arguments are frowned upon by many, because they may make the code more difficult to read. For the moment I'd suggest that you don't use such constructs in your programs. You should perhaps learn more of the basics before you go into such esoteric stuff.

Also your idea to want to silence the compiler is really a bad one. The compiler is there to help you, listen to him. In the contrary, raise the warning level to a maximum and improve your code until it compiles without any warning.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • No, the 5 is not always ignored. It is used when no variable arguments are provided. My understanding of the OP's code is that the macro `SUM()` expects one or two arguments, and 5 is used as the default value for the second argument, and the code correctly achieves this. The actual use case is probably something different from summing numbers. – Sven Marnach Aug 17 '15 at 08:14
  • 1
    @SvenMarnach, right, this bizarre gcc `##` extension does that. So my code given here achieves the same functionality, is standard C and shouldn't produce any warning. – Jens Gustedt Aug 17 '15 at 08:39
  • Yep, it's fairly bizarre, and I completely agree with the gist of your answer. Unfortunately, your code, while more portable than the original, isn't more _readable_, but that's kind of exactly what you are saying yourself. :) – Sven Marnach Aug 17 '15 at 09:44
0

Jens Gustedt proposed a very good problem-specific portable solution. I didn't know, ,##__VA_ARGS__ is a GCC extension (maybe Clang too?). There are however GCC-specific solutions for the authors initial intension.

As a problem-specific and very GCC-specific solution, you can use _Pragma("GCC diagnostic ignored \"-Wunused-value\"") and delimit it around the macro expansion. This will keep the comfort of readability. This does not work everywhere. It mainly fails within static initializer lists placed outside of functions where those pragmas can't be applied. I really was looking for a solution within such initializer lists because I couldn't find any which hides the warning pragmas from the reader. Other than that, for a function call like sum() for example - which I suppose to be only valid in a function body itself -, you can use it:

#define SUM(a,...) ({\
    _Pragma("GCC diagnostic push")\
    _Pragma("GCC diagnostic ignored \"-Wunused-value\"")\
    sum( a, (5, ##__VA_ARGS__) );\
    _Pragma("GCC diagnostic pop")\
})

Remember, you can only use it in function bodies and where an expression is expected. The warning will remain turned on after the macro expansion.

But I found a general solution! Conditional-macro-expansion is possible with the ,##__VA_ARGS__ feature. It gives you the power of conditional expansion based on blankness of the argument.

This feature does not necessarily add substitution power to the preprocessor. If you use arguments which include commas like (<...>) for false or 0 and (<...>,<...>) for true or 1, you can achieve the same. But only the conditional comma allows you the comfort of expanding conditionally based on the blankness of an argument.

See: you might be able to write your code like SUM(A) expanding to sum((A),5) without ##__VA_ARGS__ but you might not be able to write SUM(,B) expanding to sum((somevalue),B) . But you can do that with ##__VA_ARGS__ .

Example:

#define _VADIC(...) , ##__VA_ARGS__

//expands to A if A is not blank else to __VA_ARGS__ as fallback value
#define TRY(A,B) _TRY0(_VADIC(A), B)
#define _TRY0(...) _TRY1(__VA_ARGS__) /*expand before call*/
#define _TRY1(A, B, ...) B

//expands to THEN if A is blank otherwise expands to blank
#define IF(A,THEN) _IF0(_VADIC(A),THEN)
#define _IF0(...) _IF1(__VA_ARGS__) /*expand before call*/
#define _IF1(A,B,...) __VA_ARGS__

//expands to ELSE if A is not blank otherwise expands to blank
#define IFNOT(A,ELSE) _IFNOT0(_VADIC(A),,ELSE)
#define _IFNOT0(...) _IFNOT1(__VA_ARGS__) /*expand before call*/
#define _IFNOT1(A,B,C,...) C

#define IF_ELSE(A, THEN, ELSE) IF(A,THEN)IFNOT(A,ELSE)

Without the conditional comma, you only can expand conditionally on the number of arguments or on predefined concatenations but this way, you can use ANY single undefined symbol as condition.

PS: What about loops? Macros in C are designed to be finite for faster compilation. You won't get infinite loops since the limit of loop cycles depends on the source code size. Limited loops is the only thing which hinders you from turing-completeness, but practical real-world computer science problems (different from embedded or operating systems) don't need infinite loops for calculations. They are all limited depending with the problem size. The turing machine also uses a finite alphabet of symbols. You could know the limit of loop cycles which are needed in the worst case and it is possible to create a functional loop (a "reduce" or "filter" macro) running on variable-length macro argument lists which can reformat the macro argument list and do magic. The only requirement is the comma. You can't iterate over elements without a comma in between.

ChrisoLosoph
  • 459
  • 4
  • 8