The latest draft of the upcoming C23 standard is:[...]
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
The latest draft of the upcoming C23 standard is:[...]
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
Just after I posted the above, I thought of a potential issue with
memchr() that just might affect real code.
In C17 and earlier, memchr() has this declaration:
void *memchr(const void *s, int c, size_t n);
Given the implicit conversions between void* and other object pointer
types, the first argument can be a pointer to any const object type.
This is something that might plausibly be used in practice, unlike
(I think) passing a void pointer to the str*() functions.
It's probably impractical to fix this, since it would require
the generic selection to cover all possible object pointer types.
Any code that depends on the current behavior would have to add
(void*) or (const void*) casts to ensure that the type actually
matches.
For example, this (contrived) program is valid in C17 and earlier:
#include <stdio.h>
#include <string.h>
int main(void) {
const unsigned u = 0x12345678;
printf("u = 0x%x", u);
unsigned char *p = memchr(&u, 0x34, sizeof u);
if (p != NULL) printf(", p points to 0x%x", *p);
putchar('\n');
}
The output is:
u = 0x12345678, p points to 0x34
(Conceivably p might be a null pointer if unsigned int has padding
bits that cause 0x34 not to be stored in a single byte.)
A call to memchr with a char* argument is, I suspect, more likely to
appear in real code.
The underlying issue is that the implicit conversions that happen with function arguments do not happen with operands of a generic selection.
(The generic functions in <tgmath.h> are defined in a way that this
isn't an issue, as far as I can tell.)
On 2023-06-02 07:18, Keith Thompson wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
The latest draft of the upcoming C23 standard is:[...]
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
Just after I posted the above, I thought of a potential issue with
memchr() that just might affect real code.
In C17 and earlier, memchr() has this declaration:
void *memchr(const void *s, int c, size_t n);
Given the implicit conversions between void* and other object
pointer
types, the first argument can be a pointer to any const object type.
This is something that might plausibly be used in practice, unlike
(I think) passing a void pointer to the str*() functions.
It's probably impractical to fix this, since it would require
the generic selection to cover all possible object pointer types.
Any code that depends on the current behavior would have to add
(void*) or (const void*) casts to ensure that the type actually
matches.
For example, this (contrived) program is valid in C17 and earlier:
#include <stdio.h>
#include <string.h>
int main(void) {
const unsigned u = 0x12345678;
printf("u = 0x%x", u);
unsigned char *p = memchr(&u, 0x34, sizeof u);
if (p != NULL) printf(", p points to 0x%x", *p);
putchar('\n');
}
The output is:
u = 0x12345678, p points to 0x34
(Conceivably p might be a null pointer if unsigned int has padding
bits that cause 0x34 not to be stored in a single byte.)
A call to memchr with a char* argument is, I suspect, more likely to
appear in real code.
The underlying issue is that the implicit conversions that happen
with
function arguments do not happen with operands of a generic selection.
(The generic functions in <tgmath.h> are defined in a way that this
isn't an issue, as far as I can tell.)
Would the ability of the (new) generic mechanism to choose among a
short prioritized list of types, combined with a rule that all the
argument promotion rules continue to apply to the selection solve the conundrum?
This is what typically happens with C++ overloads done for the same
purposes.
So if the generic declaration gives the priority list [char*, const
char* ], then non-const pointers compatible with char* formal argument
types will get selected first and return a non-const char*, while other pointers compatbile with const char* formal arguments will be selected
second and return a const char*. This would even work if the generic declaration also covered the wchar_t and related types, omitting
whichever of UTF-16/UCS-4 is equivalent to the implementation defined wchar_t* .
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
The latest draft of the upcoming C23 standard is:[...]
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
Just after I posted the above, I thought of a potential issue with
memchr() that just might affect real code.
In C17 and earlier, memchr() has this declaration:
void *memchr(const void *s, int c, size_t n);
Given the implicit conversions between void* and other object pointer
types, the first argument can be a pointer to any const object type.
This is something that might plausibly be used in practice, unlike
(I think) passing a void pointer to the str*() functions.
It's probably impractical to fix this, since it would require
the generic selection to cover all possible object pointer types.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:[snip]
The latest draft of the upcoming C23 standard is:[...]
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
Just after I posted the above, I thought of a potential issue with
memchr() that just might affect real code.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
The latest draft of the upcoming C23 standard is:[...]
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
It introduces several type-generic functions in <string.h>, replacing
normal functions of the same names: memchr, strchr, strpbrk, strrchr,
strstr.
I'll use strchr() as an example; the same applies to the other str*()
generic functions (but not to memchr()).
Just after I posted the above, I thought of a potential issue with
memchr() that just might affect real code.
In C17 and earlier, memchr() has this declaration:
void *memchr(const void *s, int c, size_t n);
Given the implicit conversions between void* and other object pointer
types, the first argument can be a pointer to any const object type.
This is something that might plausibly be used in practice, unlike
(I think) passing a void pointer to the str*() functions.
It's probably impractical to fix this, since it would require
the generic selection to cover all possible object pointer types.
There may be a way round that... This trick converts any object pointer
to a const void * or a void * depending on the qualifiers of the object pointer:
#include <stdio.h>
#ifndef T
#define T const int
#endif
int main(void)
{
T i;
puts(_Generic((1 ? &i : (void *)&(int){0}),
void *: "void *",
const void *: "const void *",
default: "other"));
}
(Compile with -DT=int for example to test the other case.)
Taking the address of (int){0} is simply a way to get a void * that is
not a null pointer constant. One could, in a macro taking pointer, just
use
(1 ? (p) (void *)(p))
but some compilers will warn that the cast discards the const even
though the overall effect of the expression is to keep it.
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:[SNIP]
I think you're right. I'll pass it on to the editors.
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 991 |
Nodes: | 10 (0 / 10) |
Uptime: | 81:44:21 |
Calls: | 12,949 |
Calls today: | 3 |
Files: | 186,574 |
Messages: | 3,264,673 |