Hi Paul, On Fri, Sep 19, 2025 at 01:18:57AM -0700, Paul Eggert wrote: > On 2025-09-18 18:10, Alejandro Colomar wrote: > > My original proposal was > > > > strprefix(), stpprefix() > > strsuffix(), stpsuffix() > > There is a tension between following the standard naming conventions > (str[a-z]*, mem[a-z]*) and having readable names. I expect the C committee > would look with more favor on names that follow existing naming conventions, > even if they're a bit less readable. > > Gnulib already has names str_startswith and str_endswith for the boolean > flavors. Perhaps we can add just the pointer flavors, and use > more-standardish names. strprefix and strsuffix are reasonably easy to read > and understand; stpprefix and stpsuffix less so. So one possibility is to > implement strprefix and strsuffix in Gnulib, using a pointer API.
That would be fine. Actually, stppreffix and suffix was my second proposal, because the committee wanted to separate the pointer and the bool variants. My first proposal was a single set of functions, as I think the pointer-returning variants are fine to use as booleans too. After all, I don't expect people to be using these as callbacks anywhere close to streq. I think that division was a design-by-committee mistake. So, I'm fully in favour of having them as only strprefix() and strsuffix() returning a pointer, and having no bool variant at all. > The classic problem with a pointer API, though, is that it's not > polymorphic. strchr, for example, accepts char const * but returns char * > and that is a pain. Since C23, that's not true anymore. C23 specifies these as const-generic macros using _Generic(). (The standard calls them generic functions, but they mean macros.) <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#subsection.7.26.5> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#subsubsection.7.26.5.6> This is the new specification of strrchr(3): QChar *strrchr(QChar *s, int c); For strprefix(), this is how I did it in shadow utils: alx@devuan:~/src/shadow/shadow/master$ grepc -h strprefix . #define strprefix(s, prefix) \ ({ \ const char *p_; \ \ p_ = strprefix_(s, prefix); \ \ _Generic(s, \ const char *: p_, \ const void *: p_, \ char *: const_cast(char *, p_), \ void *: const_cast(char *, p_) \ ); \ }) alx@devuan:~/src/shadow/shadow/master$ grepc -htfd strprefix_ . inline const char * strprefix_(const char *s, const char *prefix) { if (strncmp(s, prefix, strlen(prefix)) != 0) return NULL; return s + strlen(prefix); } alx@devuan:~/src/shadow/shadow/master$ grepc -h const_cast . #define const_cast(T, p) _Generic(p, const T: (T) (p)) (const_cast() is unnecessary; I just wanted to add some safety checks in the cast.) > We could work around this problem by having strprefix > and strsuffix return a length rather than a pointer. But perhaps I'm > starting to bikeshed too much here. I've tried, but it's not possible. By returning a length, you loose the ability to return a null pointer if it is not found, which is essential. Have a lovely day! Alex -- <https://www.alejandro-colomar.es> Use port 80 (that is, <...:80/>).
signature.asc
Description: PGP signature
