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.)


Reply via email to