diff mbox

[v2] New mechanism for declaring const-covariant string functions.

Message ID 284c07af-361d-ae0d-e2bf-84f11bcac7f6@panix.com
State New
Headers show

Commit Message

Zack Weinberg Nov. 21, 2016, 5:46 p.m. UTC
Several functions in string.h, strings.h, and wchar.h return a pointer
into a buffer passed as their first argument.  That pointer logically
ought to inherit the 'const'-ness of the original buffer.  There's no
way to express that in C, so all these functions are specified with a
prototype that, as a side-effect, strips the 'const', potentially
leading to bugs:

    char *strfoo(const char *buf, ...);

In C++, however, you can get it exactly right via function overloads:

    char *strfoo(char *buf, ...);
    const char *strfoo(const char *buf, ...);

and the C++ standard (21.7 [c.strings]) requires the library to do
just that.  We were implementing this requirement via a repetitive and
error-prone mess of #ifdefs for each and every one of the affected
functions.

This patch absorbs the entire mess into a pair of macros,
__CONST_COV_PROTO and __CONST_COV_BUILTIN, which are far less
error-prone to use, and which make the public header files much nicer
to read, too.  They needed some black magic to _implement_, but it's
hiding in a bits header and hopefully nobody need ever change that
file again.

Tested on x86-64-linux-gnu.  OK?

(The only change from the previous iteration of this patch is to give
wchar.h the same treatment, as suggested by Joseph.)

zw

	* string/bits/const-covariance.h: New file.
        * string/Makefile (headers): Add it.
	* string/string.h: Use __CONST_COV_PROTO and __CONST_COV_BUILTIN to
	declare memchr, rawmemchr, memrchr, strchr, strrchr, strchrnul,
	strpbrk, strstr, strcasestr, memmem, and basename.
	* string/strings.h: Likewise for index and rindex.
	* wcsmbs/wchar.h: Likewise for wcschr, wcsrchr, wcschrnul,
	wcspbrk, wcsstr, wcswcs, and wmemchr.
	* include/bits/const-covariance.h: New wrapper.
---
 include/bits/const-covariance.h |   1 +
 string/Makefile                 |   3 +-
 string/bits/const-covariance.h  | 130 ++++++++++++++++++++
 string/string.h                 | 266
+++++++++++-----------------------------
 string/strings.h                |  71 +++--------
 wcsmbs/wchar.h                  | 131 +++++++++-----------
 6 files changed, 281 insertions(+), 321 deletions(-)
 create mode 100644 include/bits/const-covariance.h
 create mode 100644 string/bits/const-covariance.h

__THROW __attribute_malloc__;
 #endif
  __BEGIN_NAMESPACE_STD
-/* Find the first occurrence of WC in WCS.  */
-#ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
-extern "C++" wchar_t *wcschr (wchar_t *__wcs, wchar_t __wc)
-     __THROW __asm ("wcschr") __attribute_pure__;
-extern "C++" const wchar_t *wcschr (const wchar_t *__wcs, wchar_t __wc)
-     __THROW __asm ("wcschr") __attribute_pure__;
-#else
-extern wchar_t *wcschr (const wchar_t *__wcs, wchar_t __wc)
-     __THROW __attribute_pure__;
-#endif
-/* Find the last occurrence of WC in WCS.  */
-#ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
-extern "C++" wchar_t *wcsrchr (wchar_t *__wcs, wchar_t __wc)
-     __THROW __asm ("wcsrchr") __attribute_pure__;
-extern "C++" const wchar_t *wcsrchr (const wchar_t *__wcs, wchar_t __wc)
-     __THROW __asm ("wcsrchr") __attribute_pure__;
-#else
-extern wchar_t *wcsrchr (const wchar_t *__wcs, wchar_t __wc)
-     __THROW __attribute_pure__;
-#endif
+/* Find the first occurrence of WC in WCS.
+   [C]   extern wchar_t *wcschr (const wchar_t *wcs, wchar_t wc);
+   [C++] extern wchar_t *wcschr (wchar_t *wcs, wchar_t wc);
+         extern const wchar_t *wcschr (const wchar_t *wcs, wchar_t wc); */
+__CONST_COV_PROTO (wcschr, __attribute_pure__,
+                   wchar_t *, __wcs, wchar_t, __wc);
+
+/* Find the last occurrence of WC in WCS.
+   [C]   extern wchar_t *wcsrchr (const wchar_t *wcs, wchar_t wc);
+   [C++] extern wchar_t *wcsrchr (wchar_t *wcs, wchar_t wc);
+         extern const wchar_t *wcsrchr (const wchar_t *wcs, wchar_t wc); */
+__CONST_COV_PROTO (wcsrchr, __attribute_pure__,
+                   wchar_t *, __wcs, wchar_t, __wc);
 __END_NAMESPACE_STD
  #ifdef __USE_GNU
 /* This function is similar to `wcschr'.  But it returns a pointer to
-   the closing NUL wide character in case C is not found in S.  */
-extern wchar_t *wcschrnul (const wchar_t *__s, wchar_t __wc)
-     __THROW __attribute_pure__;
+   the closing NUL wide character in case C is not found in S.
+   [C]   extern wchar_t *wcschrnul (const wchar_t *wcs, wchar_t wc);
+   [C++] extern wchar_t *wcschrnul (wchar_t *wcs, wchar_t wc);
+         extern const wchar_t *wcschrnul (const wchar_t *wcs, wchar_t
wc); */
+__CONST_COV_PROTO (wcschrnul, __attribute_pure__,
+                   wchar_t *, __wcs, wchar_t, __wc);
 #endif
  __BEGIN_NAMESPACE_STD
@@ -259,28 +256,23 @@ extern size_t wcscspn (const wchar_t *__wcs, const
wchar_t *__reject)
    consists entirely of wide characters in  ACCEPT.  */
 extern size_t wcsspn (const wchar_t *__wcs, const wchar_t *__accept)
      __THROW __attribute_pure__;
-/* Find the first occurrence in WCS of any character in ACCEPT.  */
-#ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
-extern "C++" wchar_t *wcspbrk (wchar_t *__wcs, const wchar_t *__accept)
-     __THROW __asm ("wcspbrk") __attribute_pure__;
-extern "C++" const wchar_t *wcspbrk (const wchar_t *__wcs,
-				     const wchar_t *__accept)
-     __THROW __asm ("wcspbrk") __attribute_pure__;
-#else
-extern wchar_t *wcspbrk (const wchar_t *__wcs, const wchar_t *__accept)
-     __THROW __attribute_pure__;
-#endif
-/* Find the first occurrence of NEEDLE in HAYSTACK.  */
-#ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
-extern "C++" wchar_t *wcsstr (wchar_t *__haystack, const wchar_t *__needle)
-     __THROW __asm ("wcsstr") __attribute_pure__;
-extern "C++" const wchar_t *wcsstr (const wchar_t *__haystack,
-				    const wchar_t *__needle)
-     __THROW __asm ("wcsstr") __attribute_pure__;
-#else
-extern wchar_t *wcsstr (const wchar_t *__haystack, const wchar_t *__needle)
-     __THROW __attribute_pure__;
-#endif
+/* Find the first occurrence in WCS of any character in ACCEPT.
+   [C]   extern wchar_t *wcspbrk (const wchar_t *wcs, const wchar_t
*accept);
+   [C++] extern wchar_t *wcspbrk (wchar_t *wcs, const wchar_t *accept);
+         extern const wchar_t *wcspbrk (const wchar_t *wcs,
+                                        const wchar_t *accept); */
+__CONST_COV_PROTO (wcspbrk, __attribute_pure__,
+                   wchar_t *, __wcs, const wchar_t *, __accept);
+
+/* Find the first occurrence of NEEDLE in HAYSTACK.
+   [C]   extern wchar_t *wcsstr (const wchar_t *haystack,
+                                 const wchar_t *needle);
+   [C++] extern wchar_t *wcsstr (wchar_t *haystack,
+                                 const wchar_t *needle);
+         extern const wchar_t *wcsstr (const wchar_t *haystack,
+                                       const wchar_t *needle); */
+__CONST_COV_PROTO (wcsstr, __attribute_pure__,
+                   wchar_t *, __haystack, const wchar_t *, __needle);
  /* Divide WCS into tokens separated by characters in DELIM.  */
 extern wchar_t *wcstok (wchar_t *__restrict __s,
@@ -292,17 +284,15 @@ extern size_t wcslen (const wchar_t *__s) __THROW
__attribute_pure__;
 __END_NAMESPACE_STD
  #ifdef __USE_XOPEN
-/* Another name for `wcsstr' from XPG4.  */
-# ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
-extern "C++" wchar_t *wcswcs (wchar_t *__haystack, const wchar_t *__needle)
-     __THROW __asm ("wcswcs") __attribute_pure__;
-extern "C++" const wchar_t *wcswcs (const wchar_t *__haystack,
-				    const wchar_t *__needle)
-     __THROW __asm ("wcswcs") __attribute_pure__;
-# else
-extern wchar_t *wcswcs (const wchar_t *__haystack, const wchar_t *__needle)
-     __THROW __attribute_pure__;
-# endif
+/* Another name for `wcsstr' from XPG4.
+   [C]   extern wchar_t *wcswcs (const wchar_t *haystack,
+                                 const wchar_t *needle);
+   [C++] extern wchar_t *wcswcs (wchar_t *haystack,
+                                 const wchar_t *needle);
+         extern const wchar_t *wcswcs (const wchar_t *haystack,
+                                       const wchar_t *needle); */
+__CONST_COV_PROTO (wcswcs, __attribute_pure__,
+                   wchar_t *, __haystack, const wchar_t *, __needle);
 #endif
  #ifdef __USE_XOPEN2K8
@@ -311,19 +301,14 @@ extern size_t wcsnlen (const wchar_t *__s, size_t
__maxlen)
      __THROW __attribute_pure__;
 #endif
 -
 __BEGIN_NAMESPACE_STD
-/* Search N wide characters of S for C.  */
-#ifdef __CORRECT_ISO_CPP_WCHAR_H_PROTO
-extern "C++" wchar_t *wmemchr (wchar_t *__s, wchar_t __c, size_t __n)
-     __THROW __asm ("wmemchr") __attribute_pure__;
-extern "C++" const wchar_t *wmemchr (const wchar_t *__s, wchar_t __c,
-				     size_t __n)
-     __THROW __asm ("wmemchr") __attribute_pure__;
-#else
-extern wchar_t *wmemchr (const wchar_t *__s, wchar_t __c, size_t __n)
-     __THROW __attribute_pure__;
-#endif
+/* Search N wide characters of S for C.
+   [C]   extern wchar_t *wmemchr (const wchar_t *s, wchar_t wc, size_t n);
+   [C++] extern wchar_t *wmemchr (wchar_t *s, wchar_t wc, size_t n);
+         extern const wchar_t *wmemchr (const wchar_t *s, wchar_t wc,
+                                        size_t n); */
+__CONST_COV_PROTO (wmemchr, __attribute_pure__,
+                   wchar_t *, __s, wchar_t, __c, size_t, __n);
  /* Compare N wide characters of S1 and S2.  */
 extern int wmemcmp (const wchar_t *__s1, const wchar_t *__s2, size_t __n)

Comments

Paul Eggert Nov. 22, 2016, 6:06 a.m. UTC | #1
Zack Weinberg wrote:
> This patch absorbs the entire mess into a pair of macros,
> __CONST_COV_PROTO and __CONST_COV_BUILTIN, which are far less
> error-prone to use, and which make the public header files much nicer
> to read, too.

That is a impressive use of the C preprocessor.

Question: if I get the types wrong with a C or C++ program, what do the 
resulting diagnostics look like? For this invalid program:

   #include <string.h>
   char *
   foo (int *x, int p)
   {
     return strchr (x, p);
   }

I currently get a diagnostic that ends like this:

   /usr/include/string.h:231:14: note: expected ‘const char *’ but argument is 
of type ‘int *’
    extern char *strchr (const char *__s, int __c)
                 ^

and this is is reasonably easy to follow in isolation. Would it be as easy to 
follow with the proposed patch? (Similarly for C++.)
Florian Weimer Nov. 22, 2016, 9:05 a.m. UTC | #2
On 11/21/2016 06:46 PM, Zack Weinberg wrote:

> diff --git a/string/bits/const-covariance.h b/string/bits/const-covariance.h
> +#define __CC_VA_NARGS(...)  __CC_VA_NARGS_(__VA_ARGS__, __CC_RSEQ)
> +#define __CC_VA_NARGS_(...) __CC_VA_NARGS__(__VA_ARGS__)
> +#define __CC_VA_NARGS__(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
> +#define __CC_RSEQ 8, 7, 6, 5, 4, 3, 2, 1

It's not clear to me what ensures that variadic macros are available at 
this point.  <bits/const-covariance.h> seems to be included 
unconditionally, not just for GNU or C++11.  I don't think you can use 
variadic macros for this.

Florian
Zack Weinberg Nov. 23, 2016, 12:56 p.m. UTC | #3
On Tue, Nov 22, 2016 at 1:06 AM, Paul Eggert <eggert@cs.ucla.edu> wrote:
> Zack Weinberg wrote:
>>
>> This patch absorbs the entire mess into a pair of macros,
>> __CONST_COV_PROTO and __CONST_COV_BUILTIN, which are far less
>> error-prone to use, and which make the public header files much nicer
>> to read, too.
>
> Question: if I get the types wrong with a C or C++ program, what do the
> resulting diagnostics look like?

The diagnostics are unfortunately not as easy to read.  For the same
program you posted, I get this with gcc 6:

strchr-wrong-types.c: In function ‘foo’:
strchr-wrong-types.c:5:17: error: passing argument 1 of ‘strchr’ from
incompatible pointer type [-Werror=incompatible-pointer-types]
  return strchr (x, p);
                 ^
In file included from ../include/bits/const-covariance.h:1:0,
                 from ../string/string.h:39,
                 from ../include/string.h:56,
                 from strchr-wrong-types.c:1:
../string/string.h:181:22: note: expected ‘const char *’ but argument
is of type ‘int *’
 __CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
                      ^
../string/bits/const-covariance.h:90:17: note: in definition of macro
‘__CONST_COV_PROTO’
    extern rtype name __CC_XPROTO(const rtype, __VA_ARGS__) __THROW attrs
                 ^~~~
../string/string.h:181:1: note: in expansion of macro ‘__CONST_COV_BUILTIN’
 __CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
 ^~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

... and this with g++ 6 ...

strchr-wrong-types.c: In function ‘char* foo(int*, int)’:
strchr-wrong-types.c:5:21: error: no matching function for call to
‘strchr(int*&, int&)’
  return strchr (x, p);
                     ^
In file included from ../include/bits/const-covariance.h:1:0,
                 from ../string/string.h:39,
                 from ../include/string.h:56,
                 from strchr-wrong-types.c:1:
../string/string.h:181:22: note: candidate: char* strchr(char*, int)
 __CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
                      ^
../string/bits/const-covariance.h:119:5: note: in definition of macro
‘__CONST_COV_BUILTIN’
     name __CC_XPROTO (rtype, __VA_ARGS__) __THROW   \
     ^~~~
../string/string.h:181:22: note:   no known conversion for argument 1
from ‘int*’ to ‘char*’
 __CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
                      ^
../string/bits/const-covariance.h:119:5: note: in definition of macro
‘__CONST_COV_BUILTIN’
     name __CC_XPROTO (rtype, __VA_ARGS__) __THROW   \
     ^~~~
../string/string.h:181:22: note: candidate: const char* strchr(const char*, int)
 __CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
                      ^
../string/bits/const-covariance.h:122:5: note: in definition of macro
‘__CONST_COV_BUILTIN’
     name __CC_XPROTO (const rtype, __VA_ARGS__) __THROW   \
     ^~~~
../string/string.h:181:22: note:   no known conversion for argument 1
from ‘int*’ to ‘const char*’
 __CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
                      ^
../string/bits/const-covariance.h:122:5: note: in definition of macro
‘__CONST_COV_BUILTIN’
     name __CC_XPROTO (const rtype, __VA_ARGS__) __THROW   \
     ^~~~
cc1plus: all warnings being treated as errors


clang 3.9 doesn't flood the screen with a bunch of macro expansions,
but what you do get is still a bit confusing:

strchr-wrong-types.c:5:17: error: incompatible pointer types passing 'int *' to
      parameter of type 'const char *' [-Werror,-Wincompatible-pointer-types]
        return strchr (x, p);
                       ^
../string/string.h:182:28: note: passing argument to parameter '__s' here
                   char *, __s, int, __c);
                           ^

or in C++ mode

strchr-wrong-types.c:5:9: error: no matching function for call to 'strchr'
        return strchr (x, p);
               ^~~~~~
../string/string.h:181:22: note: candidate function not viable: no known
      conversion from 'int *' to 'const char *' for 1st argument
__CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
                     ^
../string/bits/const-covariance.h:92:54: note: expanded from macro
      '__CONST_COV_BUILTIN'
# define __CONST_COV_BUILTIN(...) __CONST_COV_PROTO (__VA_ARGS__)
                                                     ^
../string/bits/const-covariance.h:90:17: note: expanded from macro
      '__CONST_COV_PROTO'
   extern rtype name __CC_XPROTO(const rtype, __VA_ARGS__) __THROW attrs
                ^

(Note: if testing against an uninstalled build tree you will need to
pass both -D_ISOMAC and -D__NO_STRING_INLINES to reproduce these
errors.)
Zack Weinberg Nov. 23, 2016, 1:06 p.m. UTC | #4
On Tue, Nov 22, 2016 at 4:05 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 11/21/2016 06:46 PM, Zack Weinberg wrote:
>> diff --git a/string/bits/const-covariance.h
>> b/string/bits/const-covariance.h
>> +#define __CC_VA_NARGS(...)  __CC_VA_NARGS_(__VA_ARGS__, __CC_RSEQ)
>> +#define __CC_VA_NARGS_(...) __CC_VA_NARGS__(__VA_ARGS__)
>> +#define __CC_VA_NARGS__(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
>> +#define __CC_RSEQ 8, 7, 6, 5, 4, 3, 2, 1
>
> It's not clear to me what ensures that variadic macros are available at this
> point.  <bits/const-covariance.h> seems to be included unconditionally, not
> just for GNU or C++11.  I don't think you can use variadic macros for this.

This is a good point.  I had been blithely assuming that all supported
compilers provide variadic macros as an extension even in C89 mode,
but maybe I shouldn't do that.

To avoid variadic macros we would need to encode the number of
arguments in the macro name, e.g.

    __CONST_COV_PROTO_2 (strchr, char *, __s, int, __c);
    __CONST_COV_PROTO_3 (memchr, void *, __s, int, __c, size_t, __n);

Do you think we could live with that?  I won't have time to rework the
patch till the weekend.

zw
Florian Weimer Nov. 23, 2016, 1:29 p.m. UTC | #5
On 11/23/2016 02:06 PM, Zack Weinberg wrote:
> On Tue, Nov 22, 2016 at 4:05 AM, Florian Weimer <fweimer@redhat.com> wrote:
>> On 11/21/2016 06:46 PM, Zack Weinberg wrote:
>>> diff --git a/string/bits/const-covariance.h
>>> b/string/bits/const-covariance.h
>>> +#define __CC_VA_NARGS(...)  __CC_VA_NARGS_(__VA_ARGS__, __CC_RSEQ)
>>> +#define __CC_VA_NARGS_(...) __CC_VA_NARGS__(__VA_ARGS__)
>>> +#define __CC_VA_NARGS__(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
>>> +#define __CC_RSEQ 8, 7, 6, 5, 4, 3, 2, 1
>>
>> It's not clear to me what ensures that variadic macros are available at this
>> point.  <bits/const-covariance.h> seems to be included unconditionally, not
>> just for GNU or C++11.  I don't think you can use variadic macros for this.
>
> This is a good point.  I had been blithely assuming that all supported
> compilers provide variadic macros as an extension even in C89 mode,
> but maybe I shouldn't do that.
>
> To avoid variadic macros we would need to encode the number of
> arguments in the macro name, e.g.
>
>     __CONST_COV_PROTO_2 (strchr, char *, __s, int, __c);
>     __CONST_COV_PROTO_3 (memchr, void *, __s, int, __c, size_t, __n);
>
> Do you think we could live with that?  I won't have time to rework the
> patch till the weekend.

I think the diagnostics issue is the show-stopper here.  This looks 
increasingly like an application of Joseph's idea of generated installed 
headers.

Thanks,
Florian
Zack Weinberg Nov. 28, 2016, 12:33 p.m. UTC | #6
On Wed, Nov 23, 2016 at 8:29 AM, Florian Weimer <fweimer@redhat.com> wrote:
>
> I think the diagnostics issue is the show-stopper here.  This looks
> increasingly like an application of Joseph's idea of generated installed
> headers.

Yeah.  I'm withdrawing this patch for the time being.

zw
diff mbox

Patch

diff --git a/include/bits/const-covariance.h
b/include/bits/const-covariance.h
new file mode 100644
index 0000000..564d730
--- /dev/null
+++ b/include/bits/const-covariance.h
@@ -0,0 +1 @@ 
+#include <string/bits/const-covariance.h>
diff --git a/string/Makefile b/string/Makefile
index 69d3f80..c009872 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -24,7 +24,8 @@  include ../Makeconfig
  headers	:= string.h strings.h memory.h endian.h bits/endian.h \
 	   argz.h envz.h byteswap.h bits/byteswap.h bits/byteswap-16.h \
-	   bits/string.h bits/string2.h bits/string3.h
+	   bits/string.h bits/string2.h bits/string3.h \
+	   bits/const-covariance.h
  routines	:= strcat strchr strcmp strcoll strcpy strcspn		\
 		   strverscmp strdup strndup				\
diff --git a/string/bits/const-covariance.h b/string/bits/const-covariance.h
new file mode 100644
index 0000000..6378438
--- /dev/null
+++ b/string/bits/const-covariance.h
@@ -0,0 +1,130 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _BITS_CONST_COVARIANCE_H
+#define _BITS_CONST_COVARIANCE_H 1
+
+#if !defined _STRING_H && !defined _STRINGS_H && !defined _WCHAR_H
+# error "Never use <bits/const-covariance.h> directly; include
<string.h>, <strings.h>, or <wchar.h> instead."
+#endif
+
+/* This header defines internal-use macros that expand a C prototype
+   declaration like
+
+       extern void *memchr (const void *, int, size_t) attrs;
+
+   to a pair of C++ overloaded function declarations with improved
+   const-correctness:
+
+       extern void *memchr (void *, int, size_t) attrs;
+       extern const void *memchr (const void *, int, size_t) attrs;
+
+   You use them like this:
+
+       __CONST_COV_PROTO (memchr, attrs,
+                          void *, __s, int, __c, size_t, __n);
+
+    where the arguments after 'attrs' are the function's arguments,
+    alternating with argument names.  The first of these will be used
+    as the const-covariant return type.  It should be written without
+    a 'const' qualifier.
+
+    If the compiler has intrinsic knowledge of the function, use
+    __CONST_COV_BUILTIN instead of __CONST_COV_PROTO.  In C++ mode,
+    this will also generate inline functions of the form
+
+        __extern_always_inline [const] void *
+        memchr (void *__s, int __c, size_t __n) attrs
+        {
+          return __builtin_memchr (__s, __c, __n);
+        }
+
+    Due to limitations in the preprocessor, these macros support no
+    more than four arguments to any function.  This is all that
+    string.h/strings.h currently require.
+
+    Because g++ only accepts throw(), __asm("..."), and
+    __attribute__((whatever)) annotations in a specific order, all
+    functions declared with __CONST_COV_PROTO or defined with
+    __CONST_COV_BUILTIN are automatically marked __THROW.  Do not put
+    __THROW in 'attrs'.  */
+
+#define __CC_VA_NARGS(...)  __CC_VA_NARGS_(__VA_ARGS__, __CC_RSEQ)
+#define __CC_VA_NARGS_(...) __CC_VA_NARGS__(__VA_ARGS__)
+#define __CC_VA_NARGS__(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
+#define __CC_RSEQ 8, 7, 6, 5, 4, 3, 2, 1
+
+#define __CC_2FOR2(op, a, b)	  op (a, b)
+#define __CC_2FOR4(op, a, b, ...) op (a, b), __CC_2FOR2 (op, __VA_ARGS__)
+#define __CC_2FOR6(op, a, b, ...) op (a, b), __CC_2FOR4 (op, __VA_ARGS__)
+#define __CC_2FOR8(op, a, b, ...) op (a, b), __CC_2FOR6 (op, __VA_ARGS__)
+
+#define __CC_2FOR(op, ...) \
+ __CC_2FOR_ (__CC_VA_NARGS (__VA_ARGS__)) (op, __VA_ARGS__)
+#define __CC_2FOR_(n) __CC_2FOR__ (n)
+#define __CC_2FOR__(n) __CC_2FOR##n
+
+#define __CC_XPROTO(...) (__CC_2FOR (__CC_XPROTO_, __VA_ARGS__))
+#define __CC_XPROTO_(type, name) type name
+
+#define __CC_XCALL(...) (__CC_2FOR (__CC_XCALL_, __VA_ARGS__))
+#define __CC_XCALL_(type, name) name
+
+#if !defined __cplusplus || !__GNUC_PREREQ (4, 4)
+
+# define __CONST_COV_PROTO(name, attrs, rtype, ...)			\
+   extern rtype name __CC_XPROTO(const rtype, __VA_ARGS__) __THROW attrs
+
+# define __CONST_COV_BUILTIN(...) __CONST_COV_PROTO (__VA_ARGS__)
+
+#else
+
+# define __CONST_COV_PROTO_BODY(name, attrs, rtype, ...)		\
+  extern rtype name __CC_XPROTO(rtype, __VA_ARGS__)			\
+    __THROW __asm (#name) attrs;					\
+  extern const rtype name __CC_XPROTO(const rtype, __VA_ARGS__)		\
+    __THROW __asm (#name) attrs;
+
+# define __CONST_COV_PROTO(...)						\
+   extern "C++"								\
+   {									\
+     __CONST_COV_PROTO_BODY(__VA_ARGS__)				\
+   }									\
+   struct __require_semicolon
+
+# ifndef __OPTIMIZE__
+#  define __CONST_COV_BUILTIN(...) __CONST_COV_PROTO (__VA_ARGS__)
+
+# else
+
+#  define __CONST_COV_BUILTIN(name, attrs, rtype, ...)			\
+  extern "C++"								\
+  {									\
+    __CONST_COV_PROTO_BODY (name, attrs, rtype, __VA_ARGS__)		\
+    __extern_always_inline rtype					\
+    name __CC_XPROTO (rtype, __VA_ARGS__) __THROW			\
+    { return __builtin_##name __CC_XCALL (rtype, __VA_ARGS__); }	\
+    __extern_always_inline const rtype					\
+    name __CC_XPROTO (const rtype, __VA_ARGS__) __THROW			\
+    { return __builtin_##name __CC_XCALL (rtype, __VA_ARGS__); }	\
+  }									\
+  struct __require_semicolon
+
+# endif /* __OPTIMIZE__ */
+#endif /* C++ and GCC >=4.4 */
+
+#endif /* const-covariance.h */
diff --git a/string/string.h b/string/string.h
index ca22cd0..601025b 100644
--- a/string/string.h
+++ b/string/string.h
@@ -36,7 +36,7 @@  __BEGIN_DECLS
 #if defined __cplusplus && __GNUC_PREREQ (4, 4)
 # define __CORRECT_ISO_CPP_STRING_H_PROTO
 #endif
-
+#include <bits/const-covariance.h>
  __BEGIN_NAMESPACE_STD
 /* Copy N bytes of SRC to DEST.  */
@@ -66,61 +66,31 @@  extern void *memset (void *__s, int __c, size_t __n)
__THROW __nonnull ((1));
 extern int memcmp (const void *__s1, const void *__s2, size_t __n)
      __THROW __attribute_pure__ __nonnull ((1, 2));
 -/* Search N bytes of S for C.  */
-#ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++"
-{
-extern void *memchr (void *__s, int __c, size_t __n)
-      __THROW __asm ("memchr") __attribute_pure__ __nonnull ((1));
-extern const void *memchr (const void *__s, int __c, size_t __n)
-      __THROW __asm ("memchr") __attribute_pure__ __nonnull ((1));
-
-# ifdef __OPTIMIZE__
-__extern_always_inline void *
-memchr (void *__s, int __c, size_t __n) __THROW
-{
-  return __builtin_memchr (__s, __c, __n);
-}
-
-__extern_always_inline const void *
-memchr (const void *__s, int __c, size_t __n) __THROW
-{
-  return __builtin_memchr (__s, __c, __n);
-}
-# endif
-}
-#else
-extern void *memchr (const void *__s, int __c, size_t __n)
-      __THROW __attribute_pure__ __nonnull ((1));
-#endif
+/* Search N bytes of S for C.
+   [C]    extern void *memchr (const void *s, int c, size_t n);
+   [C++]  extern void *memchr (void *s, int c, size_t n);
+          extern const void *memchr (const void *s, int c, size_t n);  */
+__CONST_COV_BUILTIN (memchr, __attribute_pure__ __nonnull ((1)),
+                     void *, __s, int, __c, size_t, __n);
 __END_NAMESPACE_STD
  #ifdef __USE_GNU
-/* Search in S for C.  This is similar to `memchr' but there is no
-   length limit.  */
-# ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++" void *rawmemchr (void *__s, int __c)
-     __THROW __asm ("rawmemchr") __attribute_pure__ __nonnull ((1));
-extern "C++" const void *rawmemchr (const void *__s, int __c)
-     __THROW __asm ("rawmemchr") __attribute_pure__ __nonnull ((1));
-# else
-extern void *rawmemchr (const void *__s, int __c)
-     __THROW __attribute_pure__ __nonnull ((1));
-# endif
+/* Search in S for C.  This is similar to 'memchr' but there is no
+   length limit.
+   [C]    extern void *rawmemchr (const void *s, int c);
+   [C++]  extern void *rawmemchr (void *s, int c);
+          extern const void *rawmemchr (const void *s, int c);  */
+__CONST_COV_PROTO (rawmemchr, __attribute_pure__ __nonnull ((1)),
+                   void *, __s, int, __c);
 -/* Search N bytes of S for the final occurrence of C.  */
-# ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++" void *memrchr (void *__s, int __c, size_t __n)
-      __THROW __asm ("memrchr") __attribute_pure__ __nonnull ((1));
-extern "C++" const void *memrchr (const void *__s, int __c, size_t __n)
-      __THROW __asm ("memrchr") __attribute_pure__ __nonnull ((1));
-# else
-extern void *memrchr (const void *__s, int __c, size_t __n)
-      __THROW __attribute_pure__ __nonnull ((1));
-# endif
+/* Search N bytes of S for the final occurrence of C.
+   [C]    extern void *memrchr (const void *s, int c, size_t n);
+   [C++]  extern void *memrchr (void *s, int c, size_t n);
+          extern const void *memrchr (const void *s, int c, size_t n);  */
+__CONST_COV_PROTO (memrchr, __attribute_pure__ __nonnull ((1)),
+                   void *, __s, int, __c, size_t, __n);
 #endif
 -
 __BEGIN_NAMESPACE_STD
 /* Copy SRC to DEST.  */
 extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
@@ -204,74 +174,30 @@  extern char *strndup (const char *__string, size_t
__n)
 #endif
  __BEGIN_NAMESPACE_STD
-/* Find the first occurrence of C in S.  */
-#ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++"
-{
-extern char *strchr (char *__s, int __c)
-     __THROW __asm ("strchr") __attribute_pure__ __nonnull ((1));
-extern const char *strchr (const char *__s, int __c)
-     __THROW __asm ("strchr") __attribute_pure__ __nonnull ((1));
+/* Find the first occurrence of C in S.
+   [C]    extern char *strchr (const char *s, int c);
+   [C++]  extern char *strchr (char *s, int c);
+          extern const char *strchr (const char *s, int c);  */
+__CONST_COV_BUILTIN (strchr, __attribute_pure__ __nonnull ((1)),
+                   char *, __s, int, __c);
 -# ifdef __OPTIMIZE__
-__extern_always_inline char *
-strchr (char *__s, int __c) __THROW
-{
-  return __builtin_strchr (__s, __c);
-}
+/* Find the last occurrence of C in S.
+   [C]    extern char *strrchr (const char *s, int c);
+   [C++]  extern char *strrchr (char *s, int c);
+          extern const char *strrchr (const char *s, int c);  */
+__CONST_COV_BUILTIN (strrchr, __attribute_pure__ __nonnull ((1)),
+                     char *, __s, int, __c);
 -__extern_always_inline const char *
-strchr (const char *__s, int __c) __THROW
-{
-  return __builtin_strchr (__s, __c);
-}
-# endif
-}
-#else
-extern char *strchr (const char *__s, int __c)
-     __THROW __attribute_pure__ __nonnull ((1));
-#endif
-/* Find the last occurrence of C in S.  */
-#ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++"
-{
-extern char *strrchr (char *__s, int __c)
-     __THROW __asm ("strrchr") __attribute_pure__ __nonnull ((1));
-extern const char *strrchr (const char *__s, int __c)
-     __THROW __asm ("strrchr") __attribute_pure__ __nonnull ((1));
-
-# ifdef __OPTIMIZE__
-__extern_always_inline char *
-strrchr (char *__s, int __c) __THROW
-{
-  return __builtin_strrchr (__s, __c);
-}
-
-__extern_always_inline const char *
-strrchr (const char *__s, int __c) __THROW
-{
-  return __builtin_strrchr (__s, __c);
-}
-# endif
-}
-#else
-extern char *strrchr (const char *__s, int __c)
-     __THROW __attribute_pure__ __nonnull ((1));
-#endif
 __END_NAMESPACE_STD
  #ifdef __USE_GNU
 /* This function is similar to `strchr'.  But it returns a pointer to
-   the closing NUL byte in case C is not found in S.  */
-# ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++" char *strchrnul (char *__s, int __c)
-     __THROW __asm ("strchrnul") __attribute_pure__ __nonnull ((1));
-extern "C++" const char *strchrnul (const char *__s, int __c)
-     __THROW __asm ("strchrnul") __attribute_pure__ __nonnull ((1));
-# else
-extern char *strchrnul (const char *__s, int __c)
-     __THROW __attribute_pure__ __nonnull ((1));
-# endif
+   the closing NUL byte in case C is not found in S.
+   [C]    extern char *strchrnul (const char *s, int c);
+   [C++]  extern char *strchrnul (char *s, int c);
+          extern const char *strchrnul (const char *s, int c);  */
+__CONST_COV_PROTO (strchrnul, __attribute_pure__ __nonnull ((1)),
+                   char *, __s, int, __c);
 #endif
  __BEGIN_NAMESPACE_STD
@@ -279,65 +205,26 @@  __BEGIN_NAMESPACE_STD
    consists entirely of characters not in REJECT.  */
 extern size_t strcspn (const char *__s, const char *__reject)
      __THROW __attribute_pure__ __nonnull ((1, 2));
+
 /* Return the length of the initial segment of S which
    consists entirely of characters in ACCEPT.  */
 extern size_t strspn (const char *__s, const char *__accept)
      __THROW __attribute_pure__ __nonnull ((1, 2));
-/* Find the first occurrence in S of any character in ACCEPT.  */
-#ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++"
-{
-extern char *strpbrk (char *__s, const char *__accept)
-     __THROW __asm ("strpbrk") __attribute_pure__ __nonnull ((1, 2));
-extern const char *strpbrk (const char *__s, const char *__accept)
-     __THROW __asm ("strpbrk") __attribute_pure__ __nonnull ((1, 2));
 -# ifdef __OPTIMIZE__
-__extern_always_inline char *
-strpbrk (char *__s, const char *__accept) __THROW
-{
-  return __builtin_strpbrk (__s, __accept);
-}
-
-__extern_always_inline const char *
-strpbrk (const char *__s, const char *__accept) __THROW
-{
-  return __builtin_strpbrk (__s, __accept);
-}
-# endif
-}
-#else
-extern char *strpbrk (const char *__s, const char *__accept)
-     __THROW __attribute_pure__ __nonnull ((1, 2));
-#endif
-/* Find the first occurrence of NEEDLE in HAYSTACK.  */
-#ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++"
-{
-extern char *strstr (char *__haystack, const char *__needle)
-     __THROW __asm ("strstr") __attribute_pure__ __nonnull ((1, 2));
-extern const char *strstr (const char *__haystack, const char *__needle)
-     __THROW __asm ("strstr") __attribute_pure__ __nonnull ((1, 2));
-
-# ifdef __OPTIMIZE__
-__extern_always_inline char *
-strstr (char *__haystack, const char *__needle) __THROW
-{
-  return __builtin_strstr (__haystack, __needle);
-}
-
-__extern_always_inline const char *
-strstr (const char *__haystack, const char *__needle) __THROW
-{
-  return __builtin_strstr (__haystack, __needle);
-}
-# endif
-}
-#else
-extern char *strstr (const char *__haystack, const char *__needle)
-     __THROW __attribute_pure__ __nonnull ((1, 2));
-#endif
+/* Find the first occurrence in S of any character in ACCEPT.
+   [C]   extern char *strpbrk (const char *s, const char *accept);
+   [C++] extern char *strpbrk (char *s, const char *accept);
+         extern const char *strpbrk (const char *s, const char *accept); */
+__CONST_COV_BUILTIN (strpbrk, __attribute_pure__ __nonnull ((1, 2)),
+                     char *, __s, const char *, __accept);
 +/* Find the first occurrence of NEEDLE in HAYSTACK.
+   [C]   extern char *strstr (const char *haystack, const char *needle);
+   [C++] extern char *strstr (char *haystack, const char *needle);
+         extern const char *strstr (const char *haystack, const char
*needle);
+ */
+__CONST_COV_BUILTIN (strstr, __attribute_pure__ __nonnull ((1, 2)),
+                     char *, __haystack, const char *, __needle);
  /* Divide S into tokens separated by characters in DELIM.  */
 extern char *strtok (char *__restrict __s, const char *__restrict __delim)
@@ -357,26 +244,26 @@  extern char *strtok_r (char *__restrict __s, const
char *__restrict __delim,
 #endif
  #ifdef __USE_GNU
-/* Similar to `strstr' but this function ignores the case of both
strings.  */
-# ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++" char *strcasestr (char *__haystack, const char *__needle)
-     __THROW __asm ("strcasestr") __attribute_pure__ __nonnull ((1, 2));
-extern "C++" const char *strcasestr (const char *__haystack,
-				     const char *__needle)
-     __THROW __asm ("strcasestr") __attribute_pure__ __nonnull ((1, 2));
-# else
-extern char *strcasestr (const char *__haystack, const char *__needle)
-     __THROW __attribute_pure__ __nonnull ((1, 2));
-# endif
-#endif
+/* Similar to 'strstr', but ignores the case of both strings.
+   [C]   extern char *strcasestr (const char *haystack, const char
*needle);
+   [C++] extern char *strcasestr (char *haystack, const char *needle);
+         extern const char *strcasestr (const char *haystack,
+                                        const char *needle);  */
+__CONST_COV_PROTO (strcasestr, __attribute_pure__ __nonnull ((1, 2)),
+                   char *, __haystack, const char *, __needle);
 -#ifdef __USE_GNU
 /* Find the first occurrence of NEEDLE in HAYSTACK.
    NEEDLE is NEEDLELEN bytes long;
-   HAYSTACK is HAYSTACKLEN bytes long.  */
-extern void *memmem (const void *__haystack, size_t __haystacklen,
-		     const void *__needle, size_t __needlelen)
-     __THROW __attribute_pure__ __nonnull ((1, 3));
+   HAYSTACK is HAYSTACKLEN bytes long.
+   [C]   extern void *memmem (const void *haystack, size_t haystacklen,
+                              const void *needle, size_t needlelen);
+   [C++] extern void *memmem (void *haystack, size_t haystacklen,
+                              const void *needle, size_t needlelen);
+         extern const void *memmem (const void *haystack, size_t
haystacklen,
+                                    const void *needle, size_t
needlelen);  */
+__CONST_COV_PROTO (memmem, __attribute_pure__ __nonnull ((1, 3)),
+                   void *, __haystack, size_t, __haystacklen,
+                   const void *, __needle, size_t, __needlelen);
  /* Copy N bytes of SRC to DEST, return pointer to bytes after the
    last written byte.  */
@@ -487,15 +374,12 @@  extern void *memfrob (void *__s, size_t __n)
__THROW __nonnull ((1));
 /* Return the file name within directory of FILENAME.  We don't
    declare the function if the `basename' macro is available (defined
    in <libgen.h>) which makes the XPG version of this function
-   available.  */
-#  ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
-extern "C++" char *basename (char *__filename)
-     __THROW __asm ("basename") __nonnull ((1));
-extern "C++" const char *basename (const char *__filename)
-     __THROW __asm ("basename") __nonnull ((1));
-#  else
-extern char *basename (const char *__filename) __THROW __nonnull ((1));
-#  endif
+   available.
+   [C]   char *basename (const char *filename);
+   [C++] char *basename (char *filename);
+         const char *basename (const char *filename);  */
+__CONST_COV_PROTO (basename, __nonnull ((1)),
+                   char *, __filename);
 # endif
 #endif
 diff --git a/string/strings.h b/string/strings.h
index f78eca5..a72530a 100644
--- a/string/strings.h
+++ b/string/strings.h
@@ -26,6 +26,7 @@ 
 #if defined __cplusplus && __GNUC_PREREQ (4, 4)
 # define __CORRECT_ISO_CPP_STRINGS_H_PROTO
 #endif
+#include <bits/const-covariance.h>
  __BEGIN_DECLS
 @@ -41,61 +42,19 @@ extern void bcopy (const void *__src, void *__dest,
size_t __n)
 /* Set N bytes of S to 0.  */
 extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
 -/* Find the first occurrence of C in S (same as strchr).  */
-# ifdef __CORRECT_ISO_CPP_STRINGS_H_PROTO
-extern "C++"
-{
-extern char *index (char *__s, int __c)
-     __THROW __asm ("index") __attribute_pure__ __nonnull ((1));
-extern const char *index (const char *__s, int __c)
-     __THROW __asm ("index") __attribute_pure__ __nonnull ((1));
+/* Find the first occurrence of C in S (same as strchr).
+   [C]    extern char *index (const char *s, int c);
+   [C++]  extern char *index (char *s, int c);
+          extern const char *index (const char *s, int c);  */
+__CONST_COV_BUILTIN (index, __attribute_pure__ __nonnull ((1)),
+                     char *, __s, int, __c);
 -#  if defined __OPTIMIZE__
-__extern_always_inline char *
-index (char *__s, int __c) __THROW
-{
-  return __builtin_index (__s, __c);
-}
-
-__extern_always_inline const char *
-index (const char *__s, int __c) __THROW
-{
-  return __builtin_index (__s, __c);
-}
-#  endif
-}
-# else
-extern char *index (const char *__s, int __c)
-     __THROW __attribute_pure__ __nonnull ((1));
-# endif
-
-/* Find the last occurrence of C in S (same as strrchr).  */
-# ifdef __CORRECT_ISO_CPP_STRINGS_H_PROTO
-extern "C++"
-{
-extern char *rindex (char *__s, int __c)
-     __THROW __asm ("rindex") __attribute_pure__ __nonnull ((1));
-extern const char *rindex (const char *__s, int __c)
-     __THROW __asm ("rindex") __attribute_pure__ __nonnull ((1));
-
-#  if defined __OPTIMIZE__
-__extern_always_inline char *
-rindex (char *__s, int __c) __THROW
-{
-  return __builtin_rindex (__s, __c);
-}
-
-__extern_always_inline const char *
-rindex (const char *__s, int __c) __THROW
-{
-  return __builtin_rindex (__s, __c);
-}
-#  endif
-}
-# else
-extern char *rindex (const char *__s, int __c)
-     __THROW __attribute_pure__ __nonnull ((1));
-# endif
+/* Find the last occurrence of C in S (same as strrchr).
+   [C]    extern char *rindex (const char *s, int c);
+   [C++]  extern char *rindex (char *s, int c);
+          extern const char *rindex (const char *s, int c);  */
+__CONST_COV_BUILTIN (rindex, __attribute_pure__ __nonnull ((1)),
+                     char *, __s, int, __c);
 #endif
  #if defined __USE_MISC || !defined __USE_XOPEN2K8 || defined
__USE_XOPEN2K8XSI
@@ -106,11 +65,11 @@  extern int ffs (int __i) __THROW __attribute_const__;
  /* The following two functions are non-standard but necessary for
non-32 bit
    platforms.  */
-# ifdef	__USE_GNU
+#ifdef	__USE_GNU
 extern int ffsl (long int __l) __THROW __attribute_const__;
 __extension__ extern int ffsll (long long int __ll)
      __THROW __attribute_const__;
-# endif
+#endif
  /* Compare S1 and S2, ignoring case.  */
 extern int strcasecmp (const char *__s1, const char *__s2)
diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
index 9686fcd..9d4d373 100644
--- a/wcsmbs/wchar.h
+++ b/wcsmbs/wchar.h
@@ -70,11 +70,6 @@  typedef __WINT_TYPE__ wint_t;
 __END_NAMESPACE_STD
 #  endif
 # endif
-
-/* Tell the caller that we provide correct C++ prototypes.  */
-# if defined __cplusplus && __GNUC_PREREQ (4, 4)
-#  define __CORRECT_ISO_CPP_WCHAR_H_PROTO
-# endif
 #endif
  #if (defined _WCHAR_H || defined __need_mbstate_t) && !defined
____mbstate_t_defined
@@ -101,6 +96,12 @@  typedef struct
    defined.  */
 #ifdef _WCHAR_H
 +/* Tell the caller that we provide correct C++ prototypes.  */
+# if defined __cplusplus && __GNUC_PREREQ (4, 4)
+#  define __CORRECT_ISO_CPP_WCHAR_H_PROTO
+# endif
+# include <bits/const-covariance.h>
+
 # ifndef __mbstate_t_defined
 __BEGIN_NAMESPACE_C99
 /* Public type.  */
@@ -221,33 +222,29 @@  extern wchar_t *wcsdup (const wchar_t *__s)