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





Reply via email to