Hi Chris,

On Fri, Jan 09, 2026 at 03:39:28PM +0000, Christopher Bazley wrote:
> 
> Hi Alex,
> 
> On 09/01/2026 12: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_);
> > 
> > Is there a way to implement something like this?:
> > 
> >     #define strnulC(s)                           \
> >     ({                                           \
> >             static_assert(!has_side_effects(s)); \
> >             s + strlen(s);                       \
> >     })
> 
> I think it would be impossible to implement such a feature perfectly because
> the expression 's' might invoke functions that are defined in other
> translation units (even source code that hasn't been written yet). Any
> implementation would need to be conservative.
> 
> > 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.
> 
> I don't favour statement literals precisely because they provide a mechanism
> for creating fake encapsulation. The 'fakeness' comes from the point that
> you raised: even if the programmer diligently assigns each macro argument to
> a local variable (which is a pain in itself), all of the identifiers in the
> enclosing scope are still in scope.
> 
> I would solve the same problem like this, except that I probably wouldn't
> bother with the templates for such a trivial function:
> 
> #define STRNUL_TEMPLATE(NAME, QCHAR) \
> static inline QCHAR *NAME(QCHAR *s) \
> { \
>     return s + strlen(s); \
> }
> 
> STRNUL_TEMPLATE(strnulc, const char)
> STRNUL_TEMPLATE(strnul, char)
> 
> #define STRNUL(S) \
>   _Generic(S, \
>            char const *: strnulc, \
>            char *: strnul)(S)

It can be done simpler than this.  I've implemented the QChar APIs from
libc as simple wrappers around the old functions:

        #define __QVoidptrof(p)  typeof(1?(p):(void*){0})
        #define __QCharptrof(s)  typeof                               \
        (                                                             \
               _Generic((__QVoidptrof(s)){0},                         \
                       const void *: (const char *) 0,                \
                       void *:       (char *) 0                       \
               )                                                      \
        )

        define strchr(s, chr)  ((__QCharptrof(s)) strchr(s, chr))

Essentially the same could be done with strnul() by just adding the
function definition:

        #define strnul(s)  ((__QCharptrof(s)) strnul(s))

        char *
        (strnul)(const char s)
        {
                return s + strlen(s);
        }

But I was hoping I could do it even simpler.  A function taking and
returning 'auto' would certainly be simpler.  Maybe I'm trying to
simplify too much, though.

> 
> int main(void)
> {
>     char s[] = "Mutable",
>          *ps = STRNUL(s);
>     const char cs[] = "Immutable",
>                *pcs = STRNUL(cs);
>     return 0;
> }
> 
> I do support function literals as a more scenic alternative to lambdas, but
> I don't believe the proposal currently before WG14
> (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3679.pdf) incorporates
> the idea of return type inference. I'd expect typeof
> to be used for that purpose instead:
> 
>       #define STRNUL(S) \
>       (static typeof(S) (typeof(S) s)) \
>       { \
>               return s + strlen(s); \
>       }(S)
> 
> Or maybe, in future C:
> 
>       #def STRNUL(S)
>       (static typeof(S) (typeof(S) s))
>       {
>               return s + strlen(s);
>       }(S)
>         #enddef
> 
> Note that typeof never evaluates its operand and it is much simpler than C++
> templates.

Have a lovely day!
Alex

> 
> -- 
> Christopher Bazley
> Staff Software Engineer, GNU Tools Team.
> Arm Ltd, 110 Fulbourn Road, Cambridge, CB1 9NJ, UK.
> http://www.arm.com/

-- 
<https://www.alejandro-colomar.es>

Attachment: signature.asc
Description: PGP signature

Reply via email to