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 need to test this in liba2i first. I'll keep as a draft here until I test it thoroughly. (Done.)

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

Edit: Testing in liba2i says all's good.


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 <[email protected]>
     
      ## 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 <[email protected]>
     
    - ## 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 <[email protected]>
    -+// 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()

@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
lib/atoi/a2i.h Outdated
Comment on lines 27 to 89
#define QChar(s) typeof \
( \
_Generic(s, \
const char *: *(const char *) "", \
const void *: *(const char *) "", \
char *: *(char *) "", \
void *: *(char *) "" \
) \
)


// helper for a2i()
#define a2i_T(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; \
\
int status; \
\
*n_ = _Generic(T, \
short: strtoi_, \
int: strtoi_, \
long: strtoi_, \
long long: strtoi_, \
unsigned short: strtou_noneg, \
unsigned int: strtou_noneg, \
unsigned long: strtou_noneg, \
unsigned long long: strtou_noneg \
)(s_, const_cast(char **, endp_), base_, min_, max_, &status);\
\
if (status != 0) \
errno = status; \
status == 0 ? 0 : -1; \
})


// a2i - alpha to integer
#define a2i(T, n, s, ...) a2i_T(T, QChar(s), n, s, __VA_ARGS__)

#define a2sh(...) a2i(short, __VA_ARGS__)
#define a2si(...) a2i(int, __VA_ARGS__)
#define a2sl(...) a2i(long, __VA_ARGS__)
#define a2sll(...) a2i(long long, __VA_ARGS__)

#define a2uh(...) a2i(unsigned short, __VA_ARGS__)
#define a2ui(...) a2i(unsigned int, __VA_ARGS__)
#define a2ul(...) a2i(unsigned long, __VA_ARGS__)
#define a2ull(...) a2i(unsigned long long, __VA_ARGS__)

#define str2i(T, ...) a2i(T, __VA_ARGS__, NULL, 0, type_min(T), type_max(T))

#define str2sh(...) str2i(short, __VA_ARGS__)
#define str2si(...) str2i(int, __VA_ARGS__)
#define str2sl(...) str2i(long, __VA_ARGS__)
#define str2sll(...) str2i(long long, __VA_ARGS__)

#define str2uh(...) str2i(unsigned short, __VA_ARGS__)
#define str2ui(...) str2i(unsigned int, __VA_ARGS__)
#define str2ul(...) str2i(unsigned long, __VA_ARGS__)
#define str2ull(...) str2i(unsigned long long, __VA_ARGS__)
Copy link
Collaborator Author

@alejandro-colomar alejandro-colomar Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've finally removed the need to read many layers of files, macros, and functions to understand what these APIs do!

Finally, this API set is much more readable now. :)

Cc: @ikerexxe , @thesamesam

Copy link
Collaborator Author

@alejandro-colomar alejandro-colomar Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(There's still strtoi_() and strtou_noneg(), but those are really necessary.)

@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
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 <[email protected]>
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