Testing if an expression has side effects at compile time

David Brown david.brown@hesbynett.no
Fri Jan 9 15:10:56 GMT 2026


On 09/01/2026 13:07, Alejandro Colomar via Gcc wrote:
> Hi!
> 
> I'm trying to find a way to verify that an expression (usually passed as
> an argument to a macro) has no side effects.  Is this possible?
> 
> Let's say I want to implement strnul():
> 
> 	#define strnulA(s)  (s + strlen(s))  // Problem: evaluates twice
> 
> 	#define strnulB(s)        \
> 	({                        \
> 		auto  s_ = s;     \
> 		s_ + strlen(s_);  \
> 	})
> 
> The problem with strnulB() is that it has a local variable 's_'.  This
> means I can't pass an argument called s_.  That is, the following call
> doesn't work:
> 
> 	strnulB(s_);

That is always the risk of macros.  Usually people just pick parameter 
names and local variable names that are very unlikely to collide.  If 
you have -Winit-self (or -Werror=init-self) then collisions will be 
detected (as "auto s_ = s_;_" now gives a warning or error).

You can also use __LINE__ to generate unique symbols :

#define strnulX(s) ({ \
     auto s__ ## __LINE__ = (s); \
     s__ ## __LINE__ + strlen(s__ ## __LINE__ ); \
     })

But here you can probably just use a static inline function.  It is, 
IME, always better to use a static inline function than a function-like 
macro when possible:

static inline char * strnulY(char * s) {
     return s + strlen(s);
}


<https://godbolt.org/z/oGvcfbh14>


> 
> Is there a way to implement something like this?:
> 
> 	#define strnulC(s)                           \
> 	({                                           \
> 		static_assert(!has_side_effects(s)); \
> 		s + strlen(s);                       \
> 	})
> 
> If there's no way, it would be interesting to add compound literals of
> function type to achieve this:
> 
> 	#define strnulD(...)                                          \
> 	((static inline auto                                          \
> 	  (auto s))                                                   \
> 	{                                                             \
> 		return s + strlen(s);                                 \
> 	}(__VA_ARGS__))
> 
> Which guarantees that the argument is evaluated once.
> 

Do you need your macro here to work with different types?  I'm not sure 
how that could make sense, since "strlen" is not generic and always 
treats its parameter as a char*.

If you do need something that can work with different types and you want 
to use functions to avoid possible issues with macro parameters, you can 
use _Generic :

#define strnulX(s) _Generic((s), \
		char * : strnulX_char(s), \
		char16_t * strnulX_char16(s), \
		char32_t * strnulX_char32(s)
	)(s)

Then define functions strnulX_char, strnulX_char16, and strnulX_char32 
as separate functions.  (_Generic does not evaluate its argument, so 
side-effects don't matter to it.)




More information about the Gcc mailing list