Skip to content

Conversation

@alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Oct 18, 2025

I've managed to find a way to implement a2i() in just a couple of simple macros; a short one for the body, one for massaging const, plus a one-liner for connecting both.

I've tested this thoroughly in liba2i's test suite.

I've been trying to find a way to write it this simple for a looong time. Finally, I found it! :)


Revisions:

v2
  • Use const_cast() to do all the casts for discarding const.
  • Allow a no-op cast in const_cast(), for supporting QChar.
$ git range-diff shadow/master gh/funclit funclit 
-:  -------- > 1:  fccfefc7 lib/cast.h: const_cast(): Accept cast to same type
1:  bae69feb ! 2:  bbce57a2 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +
     +#include "atoi/strtoi/strtoi.h"
     +#include "atoi/strtoi/strtou_noneg.h"
    ++#include "cast.h"
      
      
      /*
    @@ lib/atoi/a2i/a2i.h
     +#define QChar_of(s)  typeof                                           \
     +(                                                                     \
     +  _Generic(s,                                                   \
    -+          const char *:  *(const char *) "",                    \
    -+          const void *:  *(const char *) "",                    \
    -+          char *:        *(char *) "",                          \
    -+          void *:        *(char *) ""                           \
    ++          const char *:  *const_cast(const char *, ""),         \
    ++          const void *:  *const_cast(const char *, ""),         \
    ++          char *:        *const_cast(char *,       ""),         \
    ++          void *:        *const_cast(char *,       "")          \
     +  )                                                             \
      )
      
    @@ lib/atoi/a2i/a2i.h
     +          unsigned int:       strtou_noneg,                     \
     +          unsigned long:      strtou_noneg,                     \
     +          unsigned long long: strtou_noneg                      \
    -+  )(s_, (char **) endp_, base_, min_, max_, &status);           \
    ++  )(s_, const_cast(char **, endp_), base_, min_, max_, &status);\
     +                                                                      \
     +  if (status != 0)                                              \
     +          errno = status;                                       \
2:  2cec086a = 3:  5ec018e3 lib/atoi/, */: Move all a2i() macros to the same file
3:  47c20df6 ! 4:  0520cdbb lib/atoi/, */: Move all str2i() macros together with a2i()
    @@ lib/atoi/a2i.h
      
      #include "atoi/strtoi/strtoi.h"
      #include "atoi/strtoi/strtou_noneg.h"
    + #include "cast.h"
     +#include "typetraits.h"
      
      
v3
  • Fix implementation of QChar_of(). A const_cast() doesn't work there. Use a compound literal instead, which is also simpler.
  • Revert changes to const_cast().
$ git range-diff shadow/master gh/funclit funclit 
1:  fccfefc7 < -:  -------- lib/cast.h: const_cast(): Accept cast to same type
2:  bbce57a2 ! 1:  ef203087 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +#define QChar_of(s)  typeof                                           \
     +(                                                                     \
     +  _Generic(s,                                                   \
    -+          const char *:  *const_cast(const char *, ""),         \
    -+          const void *:  *const_cast(const char *, ""),         \
    -+          char *:        *const_cast(char *,       ""),         \
    -+          void *:        *const_cast(char *,       "")          \
    ++          const char *:  (const char){0},                       \
    ++          const void *:  (const char){0},                       \
    ++          char *:        (char){0},                             \
    ++          void *:        (char){0}                              \
     +  )                                                             \
      )
      
3:  5ec018e3 = 2:  55f85cd3 lib/atoi/, */: Move all a2i() macros to the same file
4:  0520cdbb = 3:  b9dd4544 lib/atoi/, */: Move all str2i() macros together with a2i()
v3b
  • Use (T){1} instead of (T){0} for the dummy compound literals. 0 is more prone to accidents where pointers are mixed with integers, as 0 is a valid null pointer constant. 1 is not acceptable as a pointer, so it has stronger type safety. We don't care about the actual value, since we use it as input to typeof(), so 1 is fine.
$ git range-diff shadow/master gh/funclit funclit 
1:  ef203087 ! 1:  af6a5dd3 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +#define QChar_of(s)  typeof                                           \
     +(                                                                     \
     +  _Generic(s,                                                   \
    -+          const char *:  (const char){0},                       \
    -+          const void *:  (const char){0},                       \
    -+          char *:        (char){0},                             \
    -+          void *:        (char){0}                              \
    ++          const char *:  (const char){1},                       \
    ++          const void *:  (const char){1},                       \
    ++          char *:        (char){1},                             \
    ++          void *:        (char){1}                              \
     +  )                                                             \
      )
      
2:  55f85cd3 = 2:  159a38ed lib/atoi/, */: Move all a2i() macros to the same file
3:  b9dd4544 = 3:  983e2440 lib/atoi/, */: Move all str2i() macros together with a2i()
v4
  • Remove local variables that are not necessary. s and base are only used once, and can have a fixed type, so we don't need the locals.
$ git range-diff shadow/master gh/funclit funclit 
1:  af6a5dd3 ! 1:  6cbdc9e8 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +#define a2i_(T, QChar, n, s, endp, base, min, max)                    \
     +({                                                                    \
     +  T      *n_ = n;                                               \
    -+  QChar  *s_ = s;                                               \
     +  QChar  **endp_ = endp;                                        \
    -+  int    base_ = base;                                          \
     +  T      min_ = min;                                            \
     +  T      max_ = max;                                            \
     +                                                                      \
    @@ lib/atoi/a2i/a2i.h
     +          unsigned int:       strtou_noneg,                     \
     +          unsigned long:      strtou_noneg,                     \
     +          unsigned long long: strtou_noneg                      \
    -+  )(s_, const_cast(char **, endp_), base_, min_, max_, &status);\
    ++  )(s, const_cast(char **, endp_), base, min_, max_, &status);  \
     +                                                                      \
     +  if (status != 0)                                              \
     +          errno = status;                                       \
2:  159a38ed = 2:  dc464daa lib/atoi/, */: Move all a2i() macros to the same file
3:  983e2440 = 3:  d54ca4f1 lib/atoi/, */: Move all str2i() macros together with a2i()
v4b
  • Rebase
$ git rd 
1:  6cbdc9e8 = 1:  9f7f1574 lib/atoi/: a2i(): Re-implement with a statement expression
2:  dc464daa = 2:  c5854458 lib/atoi/, */: Move all a2i() macros to the same file
3:  d54ca4f1 = 3:  5dd7f58a lib/atoi/, */: Move all str2i() macros together with a2i()
v5
  • Don't use const_cast(). It's hard to make it work properly, so I've moved that to a separate PR.
$ git range-diff shadow/master gh/funclit funclit 
1:  9f7f1574 ! 1:  5d622aad lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +
     +#include "atoi/strtoi/strtoi.h"
     +#include "atoi/strtoi/strtou_noneg.h"
    -+#include "cast.h"
      
      
      /*
    @@ lib/atoi/a2i/a2i.h
     +          unsigned int:       strtou_noneg,                     \
     +          unsigned long:      strtou_noneg,                     \
     +          unsigned long long: strtou_noneg                      \
    -+  )(s, const_cast(char **, endp_), base, min_, max_, &status);  \
    ++  )(s, (char **) endp_, base, min_, max_, &status);             \
     +                                                                      \
     +  if (status != 0)                                              \
     +          errno = status;                                       \
2:  c5854458 = 2:  74e10fba lib/atoi/, */: Move all a2i() macros to the same file
3:  5dd7f58a ! 3:  ff667396 lib/atoi/, */: Move all str2i() macros together with a2i()
    @@ lib/atoi/a2i.h
      
      #include "atoi/strtoi/strtoi.h"
      #include "atoi/strtoi/strtou_noneg.h"
    - #include "cast.h"
     +#include "typetraits.h"
      
      
v5b
  • Rebase
$ git rd 
1:  5d622aad = 1:  248e76bc lib/atoi/: a2i(): Re-implement with a statement expression
2:  74e10fba = 2:  386a90ca lib/atoi/, */: Move all a2i() macros to the same file
3:  ff667396 = 3:  12d4f7de lib/atoi/, */: Move all str2i() macros together with a2i()
v6
  • Move QChar_of() to a new qchar.h header.
$ git range-diff shadow/master gh/funclit funclit 
-:  -------- > 1:  95744d94 lib/qchar.h: QChar_of(): Add macro
1:  248e76bc ! 2:  9413cf2f lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +
     +#include "atoi/strtoi/strtoi.h"
     +#include "atoi/strtoi/strtou_noneg.h"
    ++#include "qchar.h"
      
      
      /*
    @@ lib/atoi/a2i/a2i.h
     -          void (*)(unsigned long long, char *):        a2ull_nc,        \
     -          void (*)(unsigned long long, void *):        a2ull_nc         \
     -  )(n, s, __VA_ARGS__)                                                  \
    -+// QChar_of - possibly-qualified char of-a-string
    -+#define QChar_of(s)  typeof                                           \
    -+(                                                                     \
    -+  _Generic(s,                                                   \
    -+          const char *:  (const char){1},                       \
    -+          const void *:  (const char){1},                       \
    -+          char *:        (char){1},                             \
    -+          void *:        (char){1}                              \
    -+  )                                                             \
    - )
    - 
    - 
    +-)
     +#define a2i_(T, QChar, n, s, endp, base, min, max)                    \
     +({                                                                    \
     +  T      *n_ = n;                                               \
    @@ lib/atoi/a2i/a2i.h
     +
     +// a2i - alpha to integer
     +#define a2i(T, n, s, ...)  a2i_(T, QChar_of(s), n, s, __VA_ARGS__)
    -+
    -+
    + 
    + 
      #endif  // include guard
     
      ## lib/atoi/a2i/a2s_c.c (deleted) ##
2:  386a90ca = 3:  e19c278c lib/atoi/, */: Move all a2i() macros to the same file
3:  12d4f7de ! 4:  a036cfb7 lib/atoi/, */: Move all str2i() macros together with a2i()
    @@ lib/atoi/a2i.h
      
      #include "atoi/strtoi/strtoi.h"
      #include "atoi/strtoi/strtou_noneg.h"
    + #include "qchar.h"
     +#include "typetraits.h"
      
      
v6b
  • Move QChar_of() to typetraits.h.
$ git range-diff --creation-factor=99 shadow/master gh/funclit funclit 
1:  95744d94 ! 1:  dcb46bfa lib/qchar.h: QChar_of(): Add macro
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    lib/qchar.h: QChar_of(): Add macro
    +    lib/typetraits.h: QChar_of(): Add macro
     
         This macro is useful to implement QChar versions of functions.
         See ISO C23 for a description of what QChar is.
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
    - ## lib/Makefile.am ##
    -@@ lib/Makefile.am: libshadow_la_SOURCES = \
    -   pwd2spwd.c \
    -   pwdcheck.c \
    -   pwmem.c \
    -+  qchar.h \
    -   remove_tree.c \
    -   root_flag.c \
    -   run_part.h \
    -
    - ## lib/qchar.h (new) ##
    + ## lib/typetraits.h ##
     @@
    -+// SPDX-FileCopyrightText: 2025, Alejandro Colomar <alx@kernel.org>
    -+// SPDX-License-Identifier: BSD-3-Clause
    -+
    -+
    -+#ifndef SHADOW_INCLUDE_LIB_QCHAR_H_
    -+#define SHADOW_INCLUDE_LIB_QCHAR_H_
    -+
    -+
    -+#include "config.h"
    -+
    -+
    -+// QChar_of - possibly-qualified char of-a-string
    + )
    + 
    + 
     +#define QChar_of(s)  typeof                                           \
     +(                                                                     \
     +  _Generic(s,                                                   \
    @@ lib/qchar.h (new)
     +)
     +
     +
    -+#endif  // include guard
    + #endif  // include guard
2:  9413cf2f ! 2:  84ff1377 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +
     +#include "atoi/strtoi/strtoi.h"
     +#include "atoi/strtoi/strtou_noneg.h"
    -+#include "qchar.h"
    ++#include "typetraits.h"
      
      
      /*
3:  e19c278c = 3:  10ee4f43 lib/atoi/, */: Move all a2i() macros to the same file
4:  a036cfb7 ! 4:  8f89996a lib/atoi/, */: Move all str2i() macros together with a2i()
    @@ lib/atoi/a2i.h
      
      #include "atoi/strtoi/strtoi.h"
      #include "atoi/strtoi/strtou_noneg.h"
    - #include "qchar.h"
    -+#include "typetraits.h"
    - 
    - 
    - /*
     @@
      #define a2ul(...)   a2i(unsigned long, __VA_ARGS__)
      #define a2ull(...)  a2i(unsigned long long, __VA_ARGS__)
v6c
  • Rebase
$ git rd 
1:  dcb46bfa = 1:  375e349e lib/typetraits.h: QChar_of(): Add macro
2:  84ff1377 = 2:  1ecc4036 lib/atoi/: a2i(): Re-implement with a statement expression
3:  10ee4f43 ! 3:  7374c784 lib/atoi/, */: Move all a2i() macros to the same file
    @@ Commit message
     
      ## lib/Makefile.am ##
     @@ lib/Makefile.am: libshadow_la_SOURCES = \
    -   alloc/x/xmalloc.h \
    -   alloc/x/xrealloc.c \
    -   alloc/x/xrealloc.h \
    +   alloc/realloc.h \
    +   alloc/reallocf.c \
    +   alloc/reallocf.h \
     -  atoi/a2i/a2i.c \
     -  atoi/a2i/a2i.h \
     -  atoi/a2i/a2s.c \
    @@ lib/idmapping.c
     @@
      
      #include "alloc/calloc.h"
    - #include "alloc/x/xmalloc.h"
    + #include "alloc/malloc.h"
     -#include "atoi/a2i/a2u.h"
     +#include "atoi/a2i.h"
      #include "idmapping.h"
    @@ src/useradd.c
     @@
      #include <unistd.h>
      
    - #include "alloc/x/xmalloc.h"
    + #include "alloc/malloc.h"
     -#include "atoi/a2i/a2s.h"
     +#include "atoi/a2i.h"
      #include "atoi/getnum.h"
    @@ src/useradd.c
     
      ## src/usermod.c ##
     @@
    + #include <time.h>
      
      #include "alloc/malloc.h"
    - #include "alloc/x/xmalloc.h"
     -#include "atoi/a2i/a2i.h"
     -#include "atoi/a2i/a2s.h"
     +#include "atoi/a2i.h"
4:  8f89996a = 4:  f9bb6124 lib/atoi/, */: Move all str2i() macros together with a2i()
v6d
  • Cosmetic changes. Add one more set of parentheses, for readability.
$ git rd --creation-factor=99
1:  375e349e6 ! 1:  024400974 lib/typetraits.h: QChar_of(): Add macro
    @@ lib/typetraits.h
      )
      
      
    -+#define QChar_of(s)  typeof                                           \
    ++#define QChar_of(s)                                                   \
     +(                                                                     \
    -+  _Generic(s,                                                   \
    -+          const char *:  (const char){1},                       \
    -+          const void *:  (const char){1},                       \
    -+          char *:        (char){1},                             \
    -+          void *:        (char){1}                              \
    ++  typeof(                                                       \
    ++          _Generic(s,                                           \
    ++                  const char *:  (const char){1},               \
    ++                  const void *:  (const char){1},               \
    ++                  char *:        (char){1},                     \
    ++                  void *:        (char){1}                      \
    ++          )                                                     \
     +  )                                                             \
     +)
     +
2:  1ecc40360 = 2:  593b6fc47 lib/atoi/: a2i(): Re-implement with a statement expression
3:  7374c7842 = 3:  52bfdcb3a lib/atoi/, */: Move all a2i() macros to the same file
4:  f9bb61248 = 4:  cf9fab54f lib/atoi/, */: Move all str2i() macros together with a2i()
v6e
  • Revert the cosmetic changes from v6d, which were incorrect (a type name can't be parenthesized; that would make it a cast.
$ git rd --creation-factor=99
1:  024400974 ! 1:  375e349e6 lib/typetraits.h: QChar_of(): Add macro
    @@ lib/typetraits.h
      )
      
      
    -+#define QChar_of(s)                                                   \
    ++#define QChar_of(s)  typeof                                           \
     +(                                                                     \
    -+  typeof(                                                       \
    -+          _Generic(s,                                           \
    -+                  const char *:  (const char){1},               \
    -+                  const void *:  (const char){1},               \
    -+                  char *:        (char){1},                     \
    -+                  void *:        (char){1}                      \
    -+          )                                                     \
    ++  _Generic(s,                                                   \
    ++          const char *:  (const char){1},                       \
    ++          const void *:  (const char){1},                       \
    ++          char *:        (char){1},                             \
    ++          void *:        (char){1}                              \
     +  )                                                             \
     +)
     +
2:  593b6fc47 = 2:  1ecc40360 lib/atoi/: a2i(): Re-implement with a statement expression
3:  52bfdcb3a = 3:  7374c7842 lib/atoi/, */: Move all a2i() macros to the same file
4:  cf9fab54f = 4:  f9bb61248 lib/atoi/, */: Move all str2i() macros together with a2i()
v6f
  • Rebase
$ git rd 
1:  375e349e6 = 1:  1003c38b4 lib/typetraits.h: QChar_of(): Add macro
2:  1ecc40360 = 2:  b0030b2bb lib/atoi/: a2i(): Re-implement with a statement expression
3:  7374c7842 = 3:  6aff20e8c lib/atoi/, */: Move all a2i() macros to the same file
4:  f9bb61248 = 4:  7c1c836cf lib/atoi/, */: Move all str2i() macros together with a2i()
v6g
  • Document a2i() in the commit message.
$ git rd 
1:  1003c38b4 = 1:  1003c38b4 lib/typetraits.h: QChar_of(): Add macro
2:  b0030b2bb ! 2:  4b0417785 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ Metadata
      ## Commit message ##
         lib/atoi/: a2i(): Re-implement with a statement expression
     
    +    Synopsis
    +            int a2i(typename T, T *restrict n, QChar *s,
    +                    QChar **_Nullable restrict endp, int base,
    +                    T min, T max);
    +
    +    Description
    +            This macro converts the initial portion of the string pointed to
    +            by 's' to an integer of base 'base', ensure that the number is
    +            in the range [min, max], and store it in *n.
    +
    +            It is similar to NetBSD's strtoi(3) and strtou(3), which itself
    +            is similar to strtol(3) and strtoul(3).
    +
    +    Arguments
    +            T
    +                    The integer type used for the number.
    +            n
    +                    A pointer to an integer.  The parsed number will be
    +                    stored there.
    +            s
    +                    See strtol(3).
    +            endp
    +                    See strtol(3).  A difference with strtol(3) is that this
    +                    macro is const-correct.  If 's' has type 'const char *',
    +                    then 'endp' must have type 'const char **', whereas if
    +                    's' has type 'char *', 'endp' must have type 'char **'.
    +            base
    +                    See strtol(3).
    +            min
    +            max
    +                    See strtoi(3) and strtou(3).
    +
    +    Return value
    +            On success, 0 is returned.
    +            On error, -1 is returned and errno is set to indicate the error.
    +
    +    Errors
    +            See strtoi(3) and strtou(3).
    +
    +    Examples
    +            if (a2s(pid_t, &pid, s, NULL, 10, 1, _Maxof(pid_t)) == -1)
    +                    goto err;
    +
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## lib/atoi/a2i/a2i.h ##
3:  6aff20e8c = 3:  7fcb598b7 lib/atoi/, */: Move all a2i() macros to the same file
4:  7c1c836cf = 4:  b21aef77d lib/atoi/, */: Move all str2i() macros together with a2i()
v6h
  • Improve documentation.
$ git rd 
1:  1003c38b4 = 1:  1003c38b4 lib/typetraits.h: QChar_of(): Add macro
2:  4b0417785 ! 2:  c19daedbf lib/atoi/: a2i(): Re-implement with a statement expression
    @@ Commit message
                 by 's' to an integer of base 'base', ensure that the number is
                 in the range [min, max], and store it in *n.
     
    -            It is similar to NetBSD's strtoi(3) and strtou(3), which itself
    -            is similar to strtol(3) and strtoul(3).
    +            It is similar to NetBSD's strtoi(3) and strtou(3), which
    +            themselves are similar to strtol(3) and strtoul(3).
     
         Arguments
                 T
    @@ Commit message
                 max
                         See strtoi(3) and strtou(3).
     
    +                    An important difference with NetBSD's strtou(3) is that
    +                    a2i() (with an unsigned type T) doesn't intepret some
    +                    negative numbers as if they were large positive numbers.
    +                    a2i() respects the limits [min, max] as one would
    +                    intuitively expect.
    +
         Return value
                 On success, 0 is returned.
                 On error, -1 is returned and errno is set to indicate the error.
    @@ Commit message
                 See strtoi(3) and strtou(3).
     
         Examples
    -            if (a2s(pid_t, &pid, s, NULL, 10, 1, _Maxof(pid_t)) == -1)
    +            if (a2i(pid_t, &pid, s, &s, 10, 1, _Maxof(pid_t)) == -1)
                         goto err;
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
3:  7fcb598b7 = 3:  bdbd80d90 lib/atoi/, */: Move all a2i() macros to the same file
4:  b21aef77d = 4:  175345c64 lib/atoi/, */: Move all str2i() macros together with a2i()
v6i
  • wfix
$ git rd 
1:  1003c38b4 = 1:  1003c38b4 lib/typetraits.h: QChar_of(): Add macro
2:  c19daedbf ! 2:  3fb0b26d5 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ Commit message
                         See strtoi(3) and strtou(3).
     
                         An important difference with NetBSD's strtou(3) is that
    -                    a2i() (with an unsigned type T) doesn't intepret some
    +                    a2i() (with an unsigned type T) doesn't intepret any
                         negative numbers as if they were large positive numbers.
                         a2i() respects the limits [min, max] as one would
                         intuitively expect.
3:  bdbd80d90 = 3:  e50f0beb9 lib/atoi/, */: Move all a2i() macros to the same file
4:  175345c64 = 4:  d878587c4 lib/atoi/, */: Move all str2i() macros together with a2i()
v6j
  • Rebase
$ git rd 
1:  1003c38b4 = 1:  3429dbb96 lib/typetraits.h: QChar_of(): Add macro
2:  3fb0b26d5 = 2:  7e5cd1071 lib/atoi/: a2i(): Re-implement with a statement expression
3:  e50f0beb9 = 3:  1724f015d lib/atoi/, */: Move all a2i() macros to the same file
4:  d878587c4 = 4:  20cbff42b lib/atoi/, */: Move all str2i() macros together with a2i()
v6k
  • Rebase
$ git rd 
1:  3429dbb96 = 1:  6be81bc45 lib/typetraits.h: QChar_of(): Add macro
2:  7e5cd1071 = 2:  fdacd88d0 lib/atoi/: a2i(): Re-implement with a statement expression
3:  1724f015d = 3:  5f5324c62 lib/atoi/, */: Move all a2i() macros to the same file
4:  20cbff42b = 4:  df391491d lib/atoi/, */: Move all str2i() macros together with a2i()
v7
  • Merge two macros into one.
  • Remove comment pointing to my server. I've copied the manual page in the commit message.
$ git rd 
1:  6be81bc45 = 1:  6be81bc45 lib/typetraits.h: QChar_of(): Add macro
2:  fdacd88d0 ! 2:  0ad81369c lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +#include "typetraits.h"
      
      
    - /*
    -@@
    -  */
    - 
    - 
    +-/*
    +- * See the manual of these macros in liba2i's documentation:
    +- * <http://www.alejandro-colomar.es/share/dist/liba2i/git/HEAD/liba2i-HEAD.pdf>
    +- */
    +-
    +-
     -#define a2i(TYPE, n, s, ...)                                                  \
     -(                                                                             \
     -  _Generic((void (*)(TYPE, typeof(s))) 0,                               \
    @@ lib/atoi/a2i/a2i.h
     -          void (*)(unsigned long long, void *):        a2ull_nc         \
     -  )(n, s, __VA_ARGS__)                                                  \
     -)
    -+#define a2i_(T, QChar, n, s, endp, base, min, max)                    \
    ++// a2i - alpha to integer
    ++#define a2i(T, n, s, endp, base, min, max)                            \
     +({                                                                    \
    -+  T      *n_ = n;                                               \
    -+  QChar  **endp_ = endp;                                        \
    -+  T      min_ = min;                                            \
    -+  T      max_ = max;                                            \
    ++  T            *n_ = n;                                         \
    ++  QChar_of(s)  **endp_ = endp;                                  \
    ++  T            min_ = min;                                      \
    ++  T            max_ = max;                                      \
     +                                                                      \
     +  int  status;                                                  \
     +                                                                      \
    @@ lib/atoi/a2i/a2i.h
     +          errno = status;                                       \
     +  -!!status;                                                    \
     +})
    -+
    -+
    -+// a2i - alpha to integer
    -+#define a2i(T, n, s, ...)  a2i_(T, QChar_of(s), n, s, __VA_ARGS__)
      
      
      #endif  // include guard
3:  5f5324c62 ! 3:  ee0acaf40 lib/atoi/, */: Move all a2i() macros to the same file
    @@ lib/atoi/a2i/a2i.h => lib/atoi/a2i.h
      
      #include "config.h"
     @@
    - // a2i - alpha to integer
    - #define a2i(T, n, s, ...)  a2i_(T, QChar_of(s), n, s, __VA_ARGS__)
    + })
    + 
      
     +#define a2sh(...)   a2i(short, __VA_ARGS__)
     +#define a2si(...)   a2i(int, __VA_ARGS__)
    @@ lib/atoi/a2i/a2i.h => lib/atoi/a2i.h
     +#define a2ul(...)   a2i(unsigned long, __VA_ARGS__)
     +#define a2ull(...)  a2i(unsigned long long, __VA_ARGS__)
     +
    - 
    ++
      #endif  // include guard
     
      ## lib/atoi/a2i/a2i.c (deleted) ##
4:  df391491d = 4:  38185539b lib/atoi/, */: Move all str2i() macros together with a2i()
v8
  • Use a compound literal to avoid a cast.
$ git rd 
1:  6be81bc45 = 1:  6be81bc45 lib/typetraits.h: QChar_of(): Add macro
2:  0ad81369c ! 2:  0af12f0a1 lib/atoi/: a2i(): Re-implement with a statement expression
    @@ lib/atoi/a2i/a2i.h
     +                                                                      \
     +  int  status;                                                  \
     +                                                                      \
    -+  *n_ = _Generic((T) 0,                                         \
    ++  *n_ = _Generic((T){},                                         \
     +          short:              strtoi_,                          \
     +          int:                strtoi_,                          \
     +          long:               strtoi_,                          \
3:  ee0acaf40 = 3:  676bbc54e lib/atoi/, */: Move all a2i() macros to the same file
4:  38185539b = 4:  a2d85a73b lib/atoi/, */: Move all str2i() macros together with a2i()
v8b
  • Rebase
$ git rd 
1:  6be81bc45 = 1:  81833b4c7 lib/typetraits.h: QChar_of(): Add macro
2:  0af12f0a1 = 2:  41e5b903f lib/atoi/: a2i(): Re-implement with a statement expression
3:  676bbc54e = 3:  6ebcd22c5 lib/atoi/, */: Move all a2i() macros to the same file
4:  a2d85a73b = 4:  d6a827d5c lib/atoi/, */: Move all str2i() macros together with a2i()
v9
  • Use an empty compound literal. We don't need to specify a value to get its type.
$ git rd 
1:  81833b4c7 ! 1:  17ca8ba29 lib/typetraits.h: QChar_of(): Add macro
    @@ lib/typetraits.h
     +#define QChar_of(s)  typeof                                           \
     +(                                                                     \
     +  _Generic(s,                                                   \
    -+          const char *:  (const char){1},                       \
    -+          const void *:  (const char){1},                       \
    -+          char *:        (char){1},                             \
    -+          void *:        (char){1}                              \
    ++          const char *:  (const char){},                        \
    ++          const void *:  (const char){},                        \
    ++          char *:        (char){},                              \
    ++          void *:        (char){}                               \
     +  )                                                             \
     +)
     +
2:  41e5b903f = 2:  5ced4f1ed lib/atoi/: a2i(): Re-implement with a statement expression
3:  6ebcd22c5 = 3:  f44e4a88f lib/atoi/, */: Move all a2i() macros to the same file
4:  d6a827d5c = 4:  f20144bf8 lib/atoi/, */: Move all str2i() macros together with a2i()

@alejandro-colomar alejandro-colomar changed the title a2i(): Simplify, a lot a2i(): Simplify, a lot; really! Oct 18, 2025
@alejandro-colomar alejandro-colomar force-pushed the funclit branch 3 times, most recently from cb1c2f7 to b9472c3 Compare October 18, 2025 07:17
@alejandro-colomar alejandro-colomar force-pushed the funclit branch 5 times, most recently from 0f7e3f7 to d83e5e2 Compare October 18, 2025 07:36
@alejandro-colomar alejandro-colomar changed the title a2i(): Simplify, a lot; really! a2i(): Minimal minimalism Oct 18, 2025
@alejandro-colomar alejandro-colomar changed the title a2i(): Minimal minimalism a2i(): Maximal minimalism Oct 18, 2025
@alejandro-colomar alejandro-colomar marked this pull request as ready for review October 18, 2025 08:24
@alejandro-colomar alejandro-colomar force-pushed the funclit branch 6 times, most recently from 5dd7f58 to ff66739 Compare October 18, 2025 18:38
@alejandro-colomar alejandro-colomar force-pushed the funclit branch 3 times, most recently from a036cfb to 8f89996 Compare October 19, 2025 08:22
@alejandro-colomar alejandro-colomar force-pushed the funclit branch 2 times, most recently from cf9fab5 to f9bb612 Compare November 1, 2025 19:55
@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Nov 2, 2025

@ikerexxe , @hallyn , please review this PR when you have some time.

@alejandro-colomar alejandro-colomar force-pushed the funclit branch 7 times, most recently from df39149 to 3818553 Compare November 10, 2025 12:15
@alejandro-colomar alejandro-colomar force-pushed the funclit branch 2 times, most recently from a2d85a7 to d6a827d Compare November 19, 2025 21:35
This macro is useful to implement QChar versions of functions.
See ISO C23 for a description of what QChar is.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Synopsis
	int a2i(typename T, T *restrict n, QChar *s,
	        QChar **_Nullable restrict endp, int base,
	        T min, T max);

Description
	This macro converts the initial portion of the string pointed to
	by 's' to an integer of base 'base', ensure that the number is
	in the range [min, max], and store it in *n.

	It is similar to NetBSD's strtoi(3) and strtou(3), which
	themselves are similar to strtol(3) and strtoul(3).

Arguments
	T
		The integer type used for the number.
	n
		A pointer to an integer.  The parsed number will be
		stored there.
	s
		See strtol(3).
	endp
		See strtol(3).  A difference with strtol(3) is that this
		macro is const-correct.  If 's' has type 'const char *',
		then 'endp' must have type 'const char **', whereas if
		's' has type 'char *', 'endp' must have type 'char **'.
	base
		See strtol(3).
	min
	max
		See strtoi(3) and strtou(3).

		An important difference with NetBSD's strtou(3) is that
		a2i() (with an unsigned type T) doesn't intepret any
		negative numbers as if they were large positive numbers.
		a2i() respects the limits [min, max] as one would
		intuitively expect.

Return value
	On success, 0 is returned.
	On error, -1 is returned and errno is set to indicate the error.

Errors
	See strtoi(3) and strtou(3).

Examples
	if (a2i(pid_t, &pid, s, &s, 10, 1, _Maxof(pid_t)) == -1)
		goto err;

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
@hallyn hallyn merged commit 38499ca into shadow-maint:master Nov 28, 2025
11 checks passed
@alejandro-colomar alejandro-colomar deleted the funclit branch November 28, 2025 12:34
@alejandro-colomar alejandro-colomar self-assigned this Dec 27, 2025
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.

2 participants