Simplify implementation of strscpy()
Subsystems (2)
cover Simplify implementation of strscpy()
Hi!
Here's a simpler implementation of the default arguments in strscpy(9). It avoids having to build concatenated names, the ignored '...' in __strscpy0, counting arguments, etc. It's replaced by a quite simple macro that takes two arguments, and picks the last non-empty one.
It requires a C23 feature, but it's supported as a GNU extension in GNU C11 mode, and was implemented a long time ago, so it should be fine.
Have a lovely day!
Alex
Alejandro Colomar (2):
args: Add DEFARG() to implement macro default arguments
string: Simplify strscpy[_pad]()
include/linux/args.h | 5 +++++ include/linux/string.h | 22 ++++------------------ 2 files changed, 9 insertions(+), 18 deletions(-)
Range-diff against v0:
-: ------------ > 1: be1057524eb2 args: Add DEFARG() to implement macro default arguments -: ------------ > 2: 31096484d9be string: Simplify strscpy[_pad]()
Signature · 1 line
2.55.0
PATCH 1/2 args: Add DEFARG() to implement macro default arguments
It needs __VA_OPT__, which is a C23 feature, but it's provided by GCC and Clang in GNU-C mode as an extension even without C23. __VA_OPT__ was implemented in GCC 9 and Clang 6, I think, so it should be widely available.
Here's a small example program showing how this works:
alx@devuan:~/tmp$ cat d.c #define D__(x, ...) (x) #define D_(x, ...) D__(__VA_ARGS__ __VA_OPT__(,) x) #define D(a, b) D_(a, b) D(1, 2) D(1, ) D(, 2) D(,) alx@devuan:~/tmp$ gcc -E -Wall -Wextra d.c | grep '^[^#]' (2) (1) (2) ()
--- include/linux/args.h | 5 +++++ 1 file changed, 5 insertions(+)
include/linux/args.h +5 -0
@@ -3,6 +3,11 @@#ifndef _LINUX_ARGS_H#define _LINUX_ARGS_H +/* Expand to 'arg' if non-empty, or to 'def' if 'arg' is empty. */ +#define ___DEFARG (x, ...) (x) +#define __DEFARG (def, ...) ___DEFARG(__VA_ARGS__ __VA_OPT__(,) def) +#define DEFARG (def, arg) __DEFARG(def, arg) +/* * How do these macros work? *
Signature · 1 line
2.55.0
Re: [PATCH v1 1/2] args: Add DEFARG() to implement macro default arguments
Quoted 6 lines
Date: 2026-11-03 14:27:11+0100
From: Alejandro Colomar <[email protected]>It needs
__VA_OPT__, which is a C23 feature, but it's provided by GCC and Clang in GNU-C mode as an extension even without C23.__VA_OPT__was implemented in GCC 9 and Clang 6, I think, so it should be widely
Actually, it seems it was implemented in Clang 12, not Clang 6. I got that 6 from llvmorg-6.0.0-rc1, but that version number seems to not be the same as the Clang version number. Anyway, Clang 12 should be fine, I think.
Cheers,
Alex
Quoted 42 lines
available.
Here's a small example program showing how this works:
alx@devuan:~/tmp$ cat d.c #define D__(x, ...) (x) #define D_(x, ...) D__(__VA_ARGS__ __VA_OPT__(,) x) #define D(a, b) D_(a, b) D(1, 2) D(1, ) D(, 2) D(,) alx@devuan:~/tmp$ gcc -E -Wall -Wextra d.c | grep '^[^#]' (2) (1) (2) ()Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/args.h | 5 +++++ 1 file changed, 5 insertions(+)include/linux/args.h +5
-0@@ -3,6 +3,11 @@#ifndef _LINUX_ARGS_H#define _LINUX_ARGS_H +/* Expand to 'arg' if non-empty, or to 'def' if 'arg' is empty. */ +#define ___DEFARG (x, ...) (x) +#define __DEFARG (def, ...) ___DEFARG(__VA_ARGS__ __VA_OPT__(,) def) +#define DEFARG (def, arg) __DEFARG(def, arg) +/* * How do these macros work? * Signature · 1 line
2.55.0
Signature · 1 line
<https://www.alejandro-colomar.es>
PATCH 2/2 string: Simplify strscpy[_pad]()
In reply to Alejandro ColomarRemove the need for intermediate macros and COUNT_ARGS().
--- include/linux/string.h | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-)
include/linux/string.h +4 -18
@@ -69,22 +69,6 @@ extern char * strcpy(char *,const char *);#endif ssize_t sized_strscpy (char *,const char *,size_t ); -/* -* The 2 argument style can only be used when dst is an array with a -* known size. -*/ -#define __strscpy0 (dst, src, ...) \ - sized_strscpy(dst, src, sizeof(dst) + __must_be_array(dst) + \ - __must_be_cstr(dst) + __must_be_cstr(src)) -#define __strscpy1 (dst, src, size) \ - sized_strscpy(dst, src, size + __must_be_cstr(dst) + __must_be_cstr(src)) - -#define __strscpy_pad0 (dst, src, ...) \ - sized_strscpy_pad(dst, src, sizeof(dst) + __must_be_array(dst) + \ - __must_be_cstr(dst) + __must_be_cstr(src)) -#define __strscpy_pad1 (dst, src, size) \ - sized_strscpy_pad(dst, src, size + __must_be_cstr(dst) + __must_be_cstr(src)) -/** * strscpy - Copy a C-string into a sized buffer * @dst: Where to copy the string to
@@ -108,7 +92,8 @@ ssize_t sized_strscpy(char *, const char *, size_t);* truncated. */ #define strscpy (dst, src, ...) \ - CONCATENATE(__strscpy, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__) + sized_strscpy(dst, src, DEFARG(sizeof(dst) + __must_be_array(dst), __VA_ARGS__) + \ + __must_be_cstr(dst) + __must_be_cstr(src))#define sized_strscpy_pad (dest, src, count) ({ \ char *__dst = (dest); \
@@ -143,7 +128,8 @@ ssize_t sized_strscpy(char *, const char *, size_t);* * -E2BIG if count is 0 or @src was truncated. */ #define strscpy_pad (dst, src, ...) \ - CONCATENATE(__strscpy_pad, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__) + sized_strscpy_pad(dst, src, DEFARG(sizeof(dst) + __must_be_array(dst), __VA_ARGS__) + \ + __must_be_cstr(dst) + __must_be_cstr(src))#ifndef __HAVE_ARCH_STRCATextern char *strcat (char *,const char *);
Signature · 1 line
2.55.0
Quoted 8 lines · Alejandro Colomar
Here's a simpler implementation of the default arguments in strscpy(9). It avoids having to build concatenated names, the ignored '...' in
__strscpy0, counting arguments, etc. It's replaced by a quite simple macro that takes two arguments, and picks the last non-empty one.It requires a C23 feature, but it's supported as a GNU extension in GNU C11 mode, and was implemented a long time ago, so it should be fine.
Yes and no.
See 551d44200152 ("default_gfp(): avoid using the "newfangled" __VA_OPT__ trick") for the details.
So far NAK from me.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Hi Andy,
Quoted 10 lines
Date: 2026-11-03 21:10:23+0200
From: Andy Shevchenko <[email protected]>Quoted 6 lines · Alejandro Colomar
Here's a simpler implementation of the default arguments in strscpy(9). It avoids having to build concatenated names, the ignored '...' in
__strscpy0, counting arguments, etc. It's replaced by a quite simple macro that takes two arguments, and picks the last non-empty one.
Quoted 6 lines
Quoted 2 lines
It requires a C23 feature, but it's supported as a GNU extension in GNU C11 mode, and was implemented a long time ago, so it should be fine.
Yes and no.
See551d44200152("default_gfp(): avoid using the "newfangled"__VA_OPT__trick") for the details.
Hmmmm, so sparse is a problem. DEFARG() cannot be implemented with ##__VA_ARGS__ (I believe), so I think this is frozen until sparse is improved to add support for __VA_OPT__.
The implementation in that commit you pointed takes advantage of the fact that the default value is hard-coded in the macro, and the macro has zero fixed parameters, which makes it work, but if you add one fixed parameter (which we need for a generic DEFARG() macro), then it doesn't work. GCC's ## is quite limited compared to __VA_OPT__().
I've CCed the sparse matinainer and mailing list. Let's see if they can add support for __VA_OPT__().
Quoted 1 line
So far NAK from me.
Makes sense. Thanks for the review!
Have a lovely night!
Alex
Quoted 7 lines
Signature · 3 lines
-- With Best Regards, Andy Shevchenko
Signature · 1 line
<https://www.alejandro-colomar.es>