Testing if an expression has side effects at compile time

David Brown david@westcontrol.com
Fri Jan 9 16:39:55 GMT 2026


On 09/01/2026 16:44, Alejandro Colomar via Gcc wrote:
> Hi David,
> 
> On Fri, Jan 09, 2026 at 04:10:56PM +0100, David Brown wrote:
>> On 09/01/2026 13:07, Alejandro Colomar via Gcc wrote:
>>> 	#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).
> 
> Yup, I have that enabled, and indeed is triggering.
> 
>> You can also use __LINE__ to generate unique symbols :
>>
>> #define strnulX(s) ({ \
>>      auto s__ ## __LINE__ = (s); \
>>      s__ ## __LINE__ + strlen(s__ ## __LINE__ ); \
>>      })
> 
> This doesn't solve it, because it is called from another macro, which
> has the same __LINE__ value.
> 

You are really setting yourself up for a challenge here, aren't you? :-)

>> 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);
>> }
> 
> I can't use this.  I want something that preserves 'const', like the
> C23 APIs using QChar.
> 
> I could in fact do both a function and a macro, plus all the magic for
> doing QChar, but that's too much complexity, and would be better solved
> with 'auto' in inline functions.
> 

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

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

#define strnulZ(s) _Generic((s), \
         char * : strnulZ_char, \
         const char * : strnulZ_cchar \
     )(s)


Sure, it would be easier if C supported templates, but it is a /lot/ 
easier to write one _Generic macro than for the GCC developers to 
implement templates in C!

You might consider switching to C++ as an alternative, even if you only 
use a few of the language's features (like templates).

David


> 
> Have a lovely day!
> Alex
> 
>>
>>
>> <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