15

So I am writing a library that has to build with -pedantic -ansi -std=c++98 -Werror and -Weverything for clang and -Wall -Wextra for gcc and I have this macro TESTSUITE(X) which is intended to be used in global scope like this:

TESTSUITE(current testsuite);

and what it does is call a function (on program startup by initializing a dummy var) with the string:

#define TESTSUITE(name) \
static int ANONYMOUS_VARIABLE(SOME_PREFIX) = setTestSuiteName(#name)

The problem is that this generates a warning under clang for -Wglobal-constructors.

If I surround it with _Pragma like this:

#define TESTSUITE(name)                                              \
_Pragma("clang diagnostic push");                                    \
_Pragma("clang diagnostic ignored \"-Wglobal-constructors\"");       \
static int ANONYMOUS_VARIABLE(SOME_PREFIX) = setTestSuiteName(#name) \
_Pragma("clang diagnostic pop")

the semicolon after using the macro will not be required for compilation (and when it is missing -pedantic gives an error).

If I add this at the end of the macro

static int ANONYMOUS_VARIABLE(SOME_PREFIX) = 5

the semicolon will be required but I will get a warning for an unused variable which I cannot silence (because if I surround it with _Pragma statements I will be back to square 1 not requiring a semicolon).

So does anyone have an idea how I can require the semicolon and also have 0 warnings?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
onqtam
  • 4,356
  • 2
  • 28
  • 50
  • 1
    To call a function on startup, you can give it the non-standard `__attribute((constructor))__`. – chris Feb 21 '16 at 01:27
  • @chris good to know - thanks. but I prefer the solution I already have because I also have to deal with windows compilers and having the same set of macros is better for me – onqtam Feb 21 '16 at 01:34
  • Yeah, [looks complicated](http://stackoverflow.com/a/2390626/962089). If it's any consolation, Clang is available with Visual Studio now. – chris Feb 21 '16 at 02:47

4 Answers4

24

Similar to @thomas-eding 's solution, you can put static_assert(true, "") at the end of a macro to require a semicolon.

This works both inside and outside classes and functions.

And it does not pollute any namespaces and does not generate any code.

zwhconst
  • 1,352
  • 9
  • 19
  • This is the perfect answer because: (a) it's one trick that works everywhere (unlike `using namespace requireSemicolon` which I've always used before) (b) it has no side effects and generates no code, and (c) the comment string can document its purpose, e.g. `static_assert( true, "semi-colon required after this macro, per zwhconst's excellent suggestion at https://stackoverflow.com/a/59153563/1261599" )` – Conrad Poelman May 17 '20 at 01:25
  • 1
    This will fail inside an if-else without a block. – Sapphire_Brick Oct 13 '20 at 17:07
  • This answer should take a higher place – curiouscupcake Dec 12 '20 at 16:50
  • @Sapphire_Brick : By now this is the most upvoted answer. What do you mean by "fail inside an if-else without a block"? Could you maybe edit in when tis would break? – Martin Ba Mar 15 '21 at 11:09
  • 4
    @MartinBa Sorry for not being clear. `if (y == 7) SOME_MACRO(); else { return; }` expands to `if (y == 7) { /* ... */ } static_assert(true, ""); else { return; }` because `SOME_MACRO()` expands to `{ /* ... */ } static_assert(true, "")`. – Sapphire_Brick Mar 15 '21 at 17:54
  • @Sapphire_Brick Yes, it will fail if used like that, as will the accepted answer. Good reason for requiring braces in your if else statements. This is still the best solution. – Eric Roller Mar 31 '21 at 17:31
  • 3
    @EricRoller Elsewhere, I have seen `do { ... } while (0)` around a macro, and it does not have this problem. – Sapphire_Brick Mar 31 '21 at 18:23
  • This is perfect for a macro that doesn't need to be inside a function – alfC Apr 14 '21 at 19:44
8

You can add a function declaration at the end of the macro:

#define TESTSUITE(name)  \
//...                    \
void ANONYMOUS_FUNCTION()

Demo

The function name doesn't even have to be different across different TESTSUITE macros. It's sufficient if it's just not used anywhere else so it doesn't participate in any overloading.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • 1
    It looks like GCC can give better errors with `(void)0`, or possibly `(void)"TESTSUITE requires a semicolon"` if the string literal ever gets shown in an error. Clang's error remains almost identical. – chris Feb 21 '16 at 01:25
  • 1
    @chris `(void)0;` is an error outside of a function – M.M Feb 21 '16 at 03:49
7

A bit late to the party, but I thought I'd chime in for people looking for an answer later. The recommended way to achieve this is to just wrap the macro inside a do {....} while (0). This only gets executed once and the compiler will often optimize this away for you, generating no extra code but still achieving the abstraction.

Source: https://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

Chase
  • 5,315
  • 2
  • 15
  • 41
6

I use enum {} at the end of a macro to force a semicolon.

This works both inside and outside classes and functions.

This approach does not pollute any namespaces and does not generate any code.

Thomas Eding
  • 35,312
  • 13
  • 75
  • 106