Re: [PATCH] gettext: more-consistent no-op macros

Paul Eggert <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Organization UCLA Computer Science Department
Message-ID <[email protected]>
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.
0001-gettext-more-consistent-no-op-macros-improved.patch (text/x-patch, 9.1 KB)
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.