Skip to content

Conversation

@alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Dec 7, 2025

This needs some ISO C23 feature (__VA_OPT__), but we're already using that in stprintf_a(), which we have since 4.15.0, so it should be fine.

alx@devuan:~/src/shadow/shadow/master$ git blame -- lib/string/sprintf/snprintf.h | grep __VA_OPT__
ba3456c9fd lib/string/sprintf/snprintf.h (Alejandro Colomar       2025-05-21 13:33:37 +0200 22) 	snprintf_(s, countof(s), fmt __VA_OPT__(,) __VA_ARGS__)               \
alx@devuan:~/src/shadow/shadow/master$ git show ba3456c9fd | grep __VA_OPT__
-	snprintf_(s, NITEMS(s), fmt __VA_OPT__(,) __VA_ARGS__)                \
+	snprintf_(s, countof(s), fmt __VA_OPT__(,) __VA_ARGS__)               \
alx@devuan:~/src/shadow/shadow/master$ git blame ba3456c9fd^ -- lib/string/sprintf/snprintf.h | grep __VA_OPT__
22272347b6 lib/string/sprintf/snprintf.h (Alejandro Colomar 2024-06-27 11:29:56 +0200 21) 	snprintf_(s, NITEMS(s), fmt __VA_OPT__(,) __VA_ARGS__)                \
alx@devuan:~/src/shadow/shadow/master$ git show 22272347b6 | grep -e __VA_OPT__ -e ^diff | grep -B1 -e ^- -e ^+
diff --git a/lib/string/sprintf.h b/lib/string/sprintf/snprintf.h
-	snprintf_(s, NITEMS(s), fmt __VA_OPT__(,) __VA_ARGS__)
+	snprintf_(s, NITEMS(s), fmt __VA_OPT__(,) __VA_ARGS__)                \
alx@devuan:~/src/shadow/shadow/master$ git blame 22272347b6^ -- lib/string/sprintf.h | grep __VA_OPT__
8c6634d9bc lib/string/sprintf.h (Alejandro Colomar 2023-08-26 14:54:44 +0200 23) 	snprintf_(s, NITEMS(s), fmt __VA_OPT__(,) __VA_ARGS__)
alx@devuan:~/src/shadow/shadow/master$ git show 8c6634d9bc | grep -e __VA_OPT__ -e ^diff | grep -B1 -e ^- -e ^+
diff --git a/lib/string/sprintf.h b/lib/string/sprintf.h
-	len_ = snprintf(s, sz_, fmt __VA_OPT__(,) __VA_ARGS__);               \
+	snprintf_(s, NITEMS(s), fmt __VA_OPT__(,) __VA_ARGS__)
alx@devuan:~/src/shadow/shadow/master$ git blame 8c6634d9bc^ -- lib/string/sprintf.h | grep __VA_OPT__
ce4c4d4ad5 lib/string/sprintf.h (Alejandro Colomar 2023-08-26 12:11:59 +0200 27) 	len_ = snprintf(s, sz_, fmt __VA_OPT__(,) __VA_ARGS__);               \
alx@devuan:~/src/shadow/shadow/master$ git show ce4c4d4ad5 | grep -e __VA_OPT__ -e ^diff | grep -B1 -e ^- -e ^+
diff --git a/lib/string/sprintf.h b/lib/string/sprintf.h
+	len_ = snprintf(s, sz_, fmt __VA_OPT__(,) __VA_ARGS__);               \
alx@devuan:~/src/shadow/shadow/master$ git ref ce4c4d4ad5
ce4c4d4ad592 (2023-12-15; "lib/string/sprintf.h: Add SNPRINTF() macro")
alx@devuan:~/src/shadow/shadow/master$ git describe --contains ce4c4d4ad5
4.15.0-rc1~73

I've tested this in liba2i, and it works like a charm.


Revisions:

v1b
  • Rebase
$ git rd 
1:  71ee7d526 = 1:  a4ce7786a lib/macro.h: DEFAULT(): Add macro
2:  439ccd4ae ! 2:  1d838ce38 lib/atoi/a2i.h: a2i(): Specify default parameter values
    @@ lib/atoi/a2i.h
     -  T            max_ = max;                                      \
     +  T            min_ = DEFAULT(type_min(T), min);                \
     +  T            max_ = DEFAULT(type_max(T), max);                \
    -                                                                       \
    +                                                                 \
        int  status;                                                  \
    -                                                                       \
    +                                                                 \
     @@
      #define a2ul(...)   a2i(unsigned long, __VA_ARGS__)
      #define a2ull(...)  a2i(unsigned long long, __VA_ARGS__)
3:  5e3800fcf ! 3:  6316a8396 lib/, src/: Use default argument values in a2i() calls
    @@ lib/limits.c: static int setrlimit_value (unsigned int resource,
     +          if (a2i(rlim_t, &l, value, NULL, 10, 0,) == -1
                    && errno != ENOTSUP)
                {
    -                   return 0;  // FIXME: We could instead throw an error, though.
    +                   return 0;  // FIXME: we could instead throw an error, though.
     
      ## lib/shadow/shadow/sgetspent.c ##
     @@ lib/shadow/shadow/sgetspent.c: sgetspent(const char *s)

@alejandro-colomar alejandro-colomar marked this pull request as draft December 7, 2025 13:44
@alejandro-colomar alejandro-colomar marked this pull request as ready for review December 7, 2025 13:50
@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Dec 7, 2025

For the curious, here's a small example of how this preprocessor magic works:

#include <a2i.h>

#define VA_IFNOT_(...)       __VA_ARGS__
#define VA_IFNOT_1(...)
#define VA_IFNOT(cond, ...)  VA_IFNOT_ ## cond (__VA_ARGS__)

#define DEFAULT_(def, ...)                                            \
(                                                                     \
	/* default:  */ VA_IFNOT(__VA_OPT__(1), def)                  \
	/* override: */ __VA_OPT__(__VA_ARGS__)                       \
)

#define DEFAULT(def, ovr)  DEFAULT_(def, ovr)

#define foo(a, b)  bar(DEFAULT(42, a), DEFAULT(43, b))


int bar(int, int);


int
main(void)
{
	long  l;

	l = foo(7, 111);
	l = foo(77,);
	l = foo(,11);
	l = foo(,);
	l = foo();  // Too few arguments
	l = foo(,,);  // Too many arguments
}
alx@devuan:~/tmp$ gcc -Wall -Wextra -E def.c | grepc main
def.c:30:17: error: macro ‘foo’ requires 2 arguments, but only 1 given
   30 |         l = foo();  // Too few arguments
      |                 ^
def.c:15:9: note: macro ‘foo’ defined here
   15 | #define foo(a, b)  bar(DEFAULT(42, a), DEFAULT(43, b))
      |         ^~~
def.c:31:19: error: macro ‘foo’ passed 3 arguments, but takes just 2
   31 |         l = foo(,,);  // Too many arguments
      |                   ^
def.c:15:9: note: macro ‘foo’ defined here
   15 | #define foo(a, b)  bar(DEFAULT(42, a), DEFAULT(43, b))
      |         ^~~
(standard input):int
main(void)
{
 long l;

 l = bar(( 7 ), ( 111 ));
 l = bar(( 77 ), ( 43 ));
 l = bar(( 42 ), ( 11 ));
 l = bar(( 42 ), ( 43 ));
 l = foo;
 l = foo;
}

@alejandro-colomar alejandro-colomar self-assigned this Dec 11, 2025
This macro will allow writing APIs that have default values.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
And use them to implement str2i().

Signed-off-by: Alejandro Colomar <alx@kernel.org>
This removes chances of typos.  It would be possible to accidentally
pass for example ULONG_MAX or INT_MAX where LONG_MAX would be
appropriate.  Because these macros are able to automate the right value,
don't specify it manually.

Thus, when we specify it manually, it will be in those places where we
use a non-default value, which is less common.  That will make it easier
to review those few places.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
@alejandro-colomar
Copy link
Collaborator Author

@kees

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant