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