What's a portable way to implement no-op statement in C++?
Asked Answered
P

12

44

One in a while there's a need for a no-op statement in C++. For example when implementing assert() which is disabled in non-debug configuration (also see this question):

#ifdef _DEBUG
#define assert(x) if( !x ) { \
                     ThrowExcepion(__FILE__, __LINE__);\
                  } else {\
                     //noop here \
                  }
#else
#define assert(x) //noop here
#endif

So far I'm under impression that the right way is to use (void)0; for a no-op:

(void)0;

however I suspect that it might trigger warnings on some compilers - something like C4555: expression has no effect; expected expression with side-effect Visual C++ warning that is not emitted for this particular case but is emitted when there's no cast to void.

Is it universally portable? Is there a better way?

Pearce answered 2/11, 2011 at 10:10 Comment(10)
It is usually not such a good idea to have macros that will change the behavior of the program based on DEBUG/RELEASE... you might end up with issues by which the DEBUG (easy to work with) build behaves correctly but the RELEASE build doesn't. As of a no-op: ; should do it, (void)0; (your macro should not contain the ;, that should be up to the caller to add)Southeastwards
@David Rodríguez - dribeas: Yes, I know, but it's a widespread practice to have assertions disabled in non-debug builds and I only use it as an example.Pearce
I do not get why you need to insert a no-op in an otherwise empty else block. If you want to fill it later, you can leave the else block empty.Alben
@Alben he's more-so talking about #define assert(x) //noop hereSarcomatosis
Your first macro might be better written as #define assert(x) if(x); else ThrowException(__FILE__, __LINE__), which will require the semicolon to compile properly and will look/act like a statement. Leaving in the brackets allow that to be omitted, which can be a problem (particularly if your release macro definition requires the semicolon, and you forgot to use it somewhere).Eolithic
@sharptooth: whether it is widespread or not is unrelated to the fact that it is a bad idiom, if a condition is impossible to reach, and your program cannot continue from there in DEBUG, how will it continue in RELEASE mode? In our product, asserts are enabled in release mode, and we dump a small diagnostics and core file so that we can try and diagnose what went wrong out in the field.Southeastwards
@David Rodríguez - dribeas: We go further - we have two kinds of assertions. One kind is always enabled and is used for critical checks. The other is enabled in debug builds only and is used for various paranoid checks that would be expensive to leave in non-debug builds.Pearce
@Chris Lutz That will cause problems if you write (why ever): if (cond) assert(cond2); - you'll get an if(cond) ; if(cond2) ; else (....); which is ambiguous. Better place the whole thing in a block on its own.Reminisce
@Reminisce - It's not ambiguous. The C grammar specifies that the else binds with the innermost if. It looks ambiguous if you add incorrect indentation, but it isn't. (A compiler with sufficiently high warnings settings may claim it to be ambiguous, but in that case the best solution is to adjust your compiler warnings to the code you want to write, or use do { if(!x) ThrowException(__FILE__, __LINE__); } while(0).)Eolithic
@Chris Lutz - you are right. It is not ambiguous, as properly defined by the C grammar, but you'll might get that silly warning and I don't like warnings, especially not in project-wide used constructs. That's why I prefer to wrap those constructs.Reminisce
F
24

I suspect that it might trigger warnings on some compilers

Unlikely, since ((void)0) is what the standard assert macro expands to when NDEBUG is defined. So any compiler that issues warnings for it will issue warnings whenever code that contains asserts is compiled for release. I expect that would be considered a bug by the users.

I suppose a compiler could avoid that problem by warning for your proposal (void)0 while treating only ((void)0) specially. So you might be better off using ((void)0), but I doubt it.

In general, casting something to void, with or without the extra enclosing parens, idiomatically means "ignore this". For example in C code that casts function parameters to void in order to suppress warnings for unused variables. So on that score too, a compiler that warned would be rather unpopular, since suppressing one warning would just give you another one.

Note that in C++, standard headers are permitted to include each other. Therefore, if you are using any standard header, assert might have been defined by that. So your code is non-portable on that account. If you're talking "universally portable", you normally should treat any macro defined in any standard header as a reserved identifier. You could undefine it, but using a different name for your own assertions would be more sensible. I know it's only an example, but I don't see why you'd ever want to define assert in a "universally portable" way, since all C++ implementations already have it, and it doesn't do what you're defining it to do here.

Franek answered 2/11, 2011 at 12:35 Comment(0)
W
28

The simplest no-op is just having no code at all:

#define noop

Then user code will have:

if (condition) noop; else do_something();

The alternative that you mention is also a no-op: (void)0;, but if you are going to use that inside a macro, you should leave the ; aside for the caller to add:

#define noop (void)0
if (condition) noop; else do_something();

(If ; was part of the macro, then there would be an extra ; there)

War answered 2/11, 2011 at 10:19 Comment(0)
F
24

I suspect that it might trigger warnings on some compilers

Unlikely, since ((void)0) is what the standard assert macro expands to when NDEBUG is defined. So any compiler that issues warnings for it will issue warnings whenever code that contains asserts is compiled for release. I expect that would be considered a bug by the users.

I suppose a compiler could avoid that problem by warning for your proposal (void)0 while treating only ((void)0) specially. So you might be better off using ((void)0), but I doubt it.

In general, casting something to void, with or without the extra enclosing parens, idiomatically means "ignore this". For example in C code that casts function parameters to void in order to suppress warnings for unused variables. So on that score too, a compiler that warned would be rather unpopular, since suppressing one warning would just give you another one.

Note that in C++, standard headers are permitted to include each other. Therefore, if you are using any standard header, assert might have been defined by that. So your code is non-portable on that account. If you're talking "universally portable", you normally should treat any macro defined in any standard header as a reserved identifier. You could undefine it, but using a different name for your own assertions would be more sensible. I know it's only an example, but I don't see why you'd ever want to define assert in a "universally portable" way, since all C++ implementations already have it, and it doesn't do what you're defining it to do here.

Franek answered 2/11, 2011 at 12:35 Comment(0)
P
12

How about do { } while(0)? Yes it adds code, but I'm sure most compilers today are capable of optimizing it away.

Polik answered 2/11, 2011 at 11:7 Comment(0)
A
8

; is considered as standard no-op. Note that it is possible that the compiler will not generate any code from it.

Alben answered 2/11, 2011 at 10:18 Comment(1)
Well, the answers to the linked question suggest that it is not such a good idea.Pearce
L
5

I think the objective here, and the reason not to define the macro to nothing, is to require the user to add a ;. For that purpose, anywhere a statement is legal, (void)0 (or ((void)0), or other variations thereupon) is fine.

I found this question because I needed to do the same thing at global scope, where a plain old statement is illegal. Fortunately, C++11 gives us an alternative: static_assert(true, "NO OP"). This can be used anywhere, and accomplishes my objective of requiring a ; after the macro. (In my case, the macro is a tag for a code generation tool that parses the source file, so when compiling the code as C++, it will always be a NO-OP.)

Longdrawn answered 4/9, 2018 at 14:29 Comment(0)
F
5

I'm rather late to the party on this one but I needed the same for a loop() in an Arduino project where all processing is done in timer interrupt service routines (ISR). Found the inline assembler code worked for me without defining a function:

void loop(){
  __asm__("nop\n\t");             // Do nothing.
}
Farmyard answered 13/1, 2020 at 0:55 Comment(2)
+1 but..... The difference between this no-op, and the other answers of "noop" - is that this one actually takes a processor cycle to perform, while the others are "if this can be optimised away into nothing, do that". Not saying it's better or worse, because it depends on use case; and some might get stung by the performance difference.Flapdoodle
Thanks for the comment, I was searching for a good "cpp-version" of executing the nop instruction, but I found this question. Luckily, your answer was, what I was looking for :) Is this code portable, though?Juba
E
1

I recommend using:

static_cast<void> (0)   
Effeminacy answered 2/11, 2014 at 3:11 Comment(0)
S
1

And what about:

#define NOP() ({(void)0;})

or just

#define NOP() ({;})
Silo answered 13/4, 2018 at 8:14 Comment(1)
Is this portable?Bushtit
S
0

AFAIK, it is universally portable.

#define MYDEFINE()

will do as well.

Another option may be something like this:

void noop(...) {}
#define MYDEFINE() noop()

However, I'd stick to (void)0 or use intrinsics like __noop

Show answered 2/11, 2011 at 10:21 Comment(4)
#define assert in your own code is undefined behavior; the standard says what assert must mean.Smashing
+1 @JamesKanze I didn't mean to literally define assert name, it was a user-defined name placeholder. Clarified.Show
I suspected as much, but you never know what some reader will read into what was for you a purely arbitrary choice:-). (The standard assert usually uses (void)0 because it is required to be usable as a subexpression; e.g. someCondition || assert(otherCondition).)Smashing
@JamesKanze I agree, completely.Show
U
0
    inline void noop( ) {}

Self-documenting

Ulund answered 22/3, 2015 at 18:0 Comment(6)
How does this answer "Is it universally portable? Is there a better way?"Learn
Generally avoid code-only answers. Consider adding a description that helps to explain your code. I think you can do better than "Self-documenting". Thanks.Learn
If the reader does not understand what the code does I suggest either en.cppreference.com or www.cplusplus.com. It's obviously portable if using a C++ compiler, and whether it is "better" than some other way is strictly opinion; readers can use it, or not, as they wish.Ulund
This can cause a function call and that's hardly a "no-op".Pearce
Which C++ compiler generates code for that when optimizing?Ulund
An alternative is to put []{} at the call site.Grin
L
0

this code will not omitted by optimization

static void nop_func()  {   }
typedef void (*nop_func_t)();
static nop_func_t nop = &nop_func;

for (...)
{
    nop();
}
Lever answered 13/11, 2015 at 16:34 Comment(3)
@sharptooh yep, call func through variable pointer not inlineableLever
Is the optimizer prohibited to deduce the function being called and inline the call?Pearce
This will be optimized away, I just looked at the compiler output, wether you say static, inline or nothing at all. But you can block all optimizations by saying void nop_func(){asm("");}Perilous
G
0

There are many ways, and here is the comparison I've made to some of them in MSVS2019 cpp compiler.

__asm {
        nop
       }

Transaltes to nop operation in disassembly which takes 1 machine cycle. do {} while (0); generates some instructions which generates some more cycles. Simple ; generates nothing.

Gdynia answered 28/1, 2023 at 5:1 Comment(1)
an empty do-while will generate 0 instructions unless you compile it with all optimisations disabled. so you got 0 instructions vs 1 instruction that also pessimises other optimisations cause your code is no longer c++ .Cohette

© 2022 - 2024 — McMap. All rights reserved.