On 2026-08-24 15:01, Bruno Haible wrote:
Why not#ifdef __cplusplus # define _LIBGETTEXT_FUNCAST(type, expr) static_cast<type> (expr)
Sounds good, thanks. I installed the attached to do that, and to try to simplify the code a bit.
From 4ab1951805cf092e079f0aa05afb3b0779e075d2 Mon Sep 17 00:00:00 2001 From: Paul Eggert <[email protected]> Date: Mon, 24 Aug 2026 22:57:18 -0700 Subject: [PATCH] gettext: more-consistent no-op macros (improved) Problem and idea for fix reported by Bruno Haible in: https://lists.gnu.org/r/bug-gnulib/2026-08/msg00278.html https://lists.gnu.org/r/bug-gnulib/2026-08/msg00282.html Modify previous commit so that it does the following instead: Update comments about side effects. The following changes occur only if !ENABLE_NLS && ! (defined __GNUC__ && !defined __clang__ && !defined __cplusplus). (_LIBGETTEXT_FUNCAST): New macro, so that code is more consistent, when possible, about better type checking than plain casts. (gettext, dgettext, dcgettext, ngettext, dngettext, dcngettext): Use it systematically. Coalesce now-identical implementations. Always evaluate all arguments, to be more compatible with the function, and to pacify clang -Wunused-parameter. --- ChangeLog | 16 ++++--- lib/gettext.h | 113 +++++++++++++++++++++----------------------------- 2 files changed, 58 insertions(+), 71 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6690d65c97..802b45cab2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,17 @@ 2026-08-24 Paul Eggert <[email protected]> gettext: more-consistent no-op macros - * lib/gettext.h (gettext, dgettext, dcgettext): When defining - no-op macros, be more consistent about always "using" the - arguments; this pacifies clang -Wunused-parameter when - --disable-nls is used. Also, be more consistent about using - compound literals rather than casts, for better type checking. + Idea for fix reported by Bruno Haible in: + https://lists.gnu.org/r/bug-gnulib/2026-08/msg00282.html + Update comments about side effects. + The following changes occur only if !ENABLE_NLS && ! (defined + __GNUC__ && !defined __clang__ && !defined __cplusplus). + (_LIBGETTEXT_FUNCAST): New macro, so that code is more consistent, + when possible, about better type checking than plain casts. + (gettext, dgettext, dcgettext, ngettext, dngettext, dcngettext): + Use it systematically. Coalesce now-identical implementations. + Always evaluate all arguments, to be more compatible with the + function, and to pacify clang -Wunused-parameter. 2026-08-22 Paul Eggert <[email protected]> diff --git a/lib/gettext.h b/lib/gettext.h index 8ae3885fd7..d404c0c649 100644 --- a/lib/gettext.h +++ b/lib/gettext.h @@ -65,17 +65,11 @@ warnings that would occur at every invocation of a *gettext function in a *printf format string position. Do this with inline functions when possible, namely for gettext, dgettext, - dcgettext, which are known to gcc as "external built-ins". - It is not ideal to ignore the possible side effects done in the - Domainname and Category arguments, but it's better than to have a - warning at every invocation in a format string position. */ + dcgettext, which are known to gcc as "external built-ins". */ /* When clang is used with option -Wformat=2, we need to silence "warning: format string is not a string literal [-Wformat-nonliteral]" warnings that would occur at every invocation of a *gettext function - in a *printf format string position. - It is not ideal to ignore the possible side effects done in the - Domainname and Category arguments, but it's better than to have a - warning at every invocation in a format string position. */ + in a *printf format string position. */ /* These warnings would not occur with enabled NLS. */ /* A test case: ================================ foo.c ================================ @@ -160,83 +154,70 @@ dcgettext (const char *domain, const char *msgid, int category) # if __GNUC__ >= 9 # pragma GCC diagnostic pop # endif -# elif defined __clang__ -# undef gettext -# define gettext(Msgid) ((const char *) {(Msgid)}) -# undef dgettext -# define dgettext(Domainname, Msgid) \ - ((void) (const char *) {(Domainname)}, gettext (Msgid)) -# undef dcgettext -# define dcgettext(Domainname, Msgid, Category) \ - ((void) (int) {(Category)}, dgettext (Domainname, Msgid)) -# else -/* The conversions to 'const char *' via compound literals serve the purpose - of producing warnings for invalid uses of the value returned from these - functions and for invalid-typed Msgid arguments. */ -# undef gettext -# define gettext(Msgid) ((const char *) {(Msgid)}) -/* The conversions via compound literals serve the purpose of producing warnings - for invalid-typed arguments. */ -# undef dgettext -# define dgettext(Domainname, Msgid) \ - ((void) (const char *) {(Domainname)}, gettext (Msgid)) -# undef dcgettext -# define dcgettext(Domainname, Msgid, Category) \ - ((void) (int) {(Category)}, dgettext (Domainname, Msgid)) -# endif - -# if (defined __GNUC__ && defined __cplusplus) || defined __clang__ -# undef ngettext -# define ngettext(Msgid1, Msgid2, N) \ - ((N) == 1 ? (const char *) {(Msgid1)} : (const char *) {(Msgid2)}) -# undef dngettext -# define dngettext(Domainname, Msgid1, Msgid2, N) \ - ((void) (const char *) {(Domainname)}, ngettext (Msgid1, Msgid2, N)) -# undef dcngettext -# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ - ((void) (int) {(Category)}, dngettext (Domainname, Msgid1, Msgid2, N)) -# elif defined __GNUC__ && !defined __cplusplus /* Silence -Wuseless-cast warnings. */ # if __GNUC__ >= 14 # pragma GCC diagnostic ignored "-Wuseless-cast" # endif +/* Use plain casts to pacify -Wformat-nonliteral. + This ngettext implementation does not evaluate all arguments, + as doing so would trigger -Wformat-nonliteral false positives. */ # undef ngettext # define ngettext(Msgid1, Msgid2, N) \ - ((N) == 1 ? (const char *) {(Msgid1)} : (const char *) {(Msgid2)}) + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # undef dngettext # define dngettext(Domainname, Msgid1, Msgid2, N) \ - ((void) (const char *) {(Domainname)}, ngettext (Msgid1, Msgid2, N)) + ((void) (const char *) (Domainname), ngettext (Msgid1, Msgid2, N)) # undef dcngettext # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ - ((void) (int) {(Category)}, dngettext (Domainname, Msgid1, Msgid2, N)) + ((void) (int) (Category), dngettext (Domainname, Msgid1, Msgid2, N)) + # else -/* The conversions to 'const char *' via compound literals serve the purpose - of producing warnings for invalid uses of the value returned from these - functions and for invalid-typed Msgid1 and Msgid2 arguments. */ + +/* Like the C cast ((type) (expr)), but do only conversions that an + ordinary assignment would do. This can diagnose invalid arguments + better than a cast would. */ +# ifdef __cplusplus +# define _LIBGETTEXT_FUNCAST(type, expr) static_cast <type> (expr) +# else +# define _LIBGETTEXT_FUNCAST(type, expr) (type) {(expr)} +# endif + +# undef gettext +# define gettext(Msgid) _LIBGETTEXT_FUNCAST (const char *, Msgid) +# undef dgettext +# define dgettext(Domainname, Msgid) \ + ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), gettext (Msgid)) +# undef dcgettext +# define dcgettext(Domainname, Msgid, Category) \ + ((void) _LIBGETTEXT_FUNCAST (int, Category), \ + dgettext (Domainname, Msgid)) # undef ngettext # define ngettext(Msgid1, Msgid2, N) \ ((N) == 1 \ - ? ((void) (Msgid2), (const char *) {(Msgid1)}) \ - : ((void) (Msgid1), (const char *) {(Msgid2)})) -/* The conversions via compound literals serve the purpose of producing warnings - for invalid-typed arguments. */ + ? ((void) _LIBGETTEXT_FUNCAST (const char *, Msgid2), \ + _LIBGETTEXT_FUNCAST (const char *, Msgid1)) \ + : ((void) _LIBGETTEXT_FUNCAST (const char *, Msgid1), \ + _LIBGETTEXT_FUNCAST (const char *, Msgid2))) # undef dngettext # define dngettext(Domainname, Msgid1, Msgid2, N) \ - ((void) (const char *) {(Domainname)}, ngettext (Msgid1, Msgid2, N)) + ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), \ + ngettext (Msgid1, Msgid2, N)) # undef dcngettext # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ - ((void) (int) {(Category)}, dngettext (Domainname, Msgid1, Msgid2, N)) -# endif - -# undef textdomain -# define textdomain(Domainname) ((const char *) {(Domainname)}) -# undef bindtextdomain -# define bindtextdomain(Domainname, Dirname) \ - ((void) (const char *) {(Domainname)}, (const char *) {(Dirname)}) -# undef bind_textdomain_codeset -# define bind_textdomain_codeset(Domainname, Codeset) \ - ((void) (const char *) {(Domainname)}, (const char *) {(Codeset)}) + ((void) _LIBGETTEXT_FUNCAST (int, Category), \ + dngettext (Domainname, Msgid1, Msgid2, N)) +# undef textdomain +# define textdomain(Domainname) _LIBGETTEXT_FUNCAST (const char *, Domainname) +# undef bindtextdomain +# define bindtextdomain(Domainname, Dirname) \ + ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), \ + _LIBGETTEXT_FUNCAST (const char *, Dirname)) +# undef bind_textdomain_codeset +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) _LIBGETTEXT_FUNCAT (const char *, Domainname), \ + _LIBGETTEXT_FUNCAT (const char *, Codeset)) +# endif #endif -- 2.55.0
