* _Generic.3: EXAMPLES: C++'s static_cast() in C
@ 2022-11-11 14:53 Alejandro Colomar
2022-11-11 15:11 ` Alejandro Colomar
0 siblings, 1 reply; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-11 14:53 UTC (permalink / raw)
To: linux-man; +Cc: Andrew Clayton
[-- Attachment #1.1: Type: text/plain, Size: 9188 bytes --]
Dear readers,
I've been developing a cast macro for C that is similar to C++'s static_cast(),
which allows to:
- Cast between a limited set of types (actually, pointers to those types).
- Can add const, but not remove it.
- Easily grep for all casts.
But forbids:
- Cast between unrelated types.
- Removing const.
This improves type safety in C, where casts allow programmers to commit all
kinds of crimes by disabling most compiler warnings.
The macro I originally wrote, allows to convert between u_char and char, for a
code base that uses u_char internally but has to interface libc and syscalls.
That one is unnecessary for most code bases which just use char (and that's how
it should be for strings; if you want unsigned 'char's, you should use
'-funsigned-char').
But there are cases where the kernel or libc forces us to use casts, like for
example in bind(2). That's for what the following macro is, and I plan to add
it to the EXAMPLES section in _Generic(3), replacing the previous program.
The macro itself is quite huge, which might discourage some, but it is not so
complex. I'll release it to the public domain. Could you please review it?
Cheers,
Alex
---
#define sockaddr_cast(t, p) \
_Generic(&*(p), \
struct sockaddr *: \
_Generic((t) NULL, \
struct sockaddr_in *: (t) (p), \
const struct sickaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
const struct sickaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
const struct sickaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
const struct sickaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr **: \
_Generic((t) NULL, \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr *: \
_Generic((t) NULL, \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in *: \
_Generic((t) NULL, \
struct sockaddr *: (t) (p), \
const struct sickaddr *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
const struct sickaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
const struct sickaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
const struct sickaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in6 *: \
_Generic((t) NULL, \
struct sockaddr *: (t) (p), \
const struct sickaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
const struct sickaddr_in *: (t) (p), \
struct sockaddr_un *: (t) (p), \
const struct sickaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
const struct sickaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in6 **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in6 *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_un *: \
_Generic((t) NULL, \
struct sockaddr *: (t) (p), \
const struct sickaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
const struct sickaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
const struct sickaddr_in6 *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
const struct sickaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_un **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_un *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_storage *: \
_Generic((t) NULL, \
struct sockaddr *: (t) (p), \
const struct sickaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
const struct sickaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
const struct sickaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
const struct sickaddr_un *: (t) (p), \
default: (p)), \
struct sockaddr_storage **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
default: (p)), \
const struct sockaddr_storage *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
default: (p)), \
\
default: \
(p) \
)
--
<http://www.alejandro-colomar.es/>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: _Generic.3: EXAMPLES: C++'s static_cast() in C
2022-11-11 14:53 _Generic.3: EXAMPLES: C++'s static_cast() in C Alejandro Colomar
@ 2022-11-11 15:11 ` Alejandro Colomar
2022-11-11 16:54 ` Alejandro Colomar
0 siblings, 1 reply; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-11 15:11 UTC (permalink / raw)
To: linux-man; +Cc: Andrew Clayton
[-- Attachment #1.1: Type: text/plain, Size: 8223 bytes --]
On 11/11/22 15:53, Alejandro Colomar wrote:
> Dear readers,
>
> I've been developing a cast macro for C that is similar to C++'s static_cast(),
> which allows to:
>
> - Cast between a limited set of types (actually, pointers to those types).
> - Can add const, but not remove it.
> - Easily grep for all casts.
>
> But forbids:
>
> - Cast between unrelated types.
> - Removing const.
>
> This improves type safety in C, where casts allow programmers to commit all
> kinds of crimes by disabling most compiler warnings.
>
> The macro I originally wrote, allows to convert between u_char and char, for a
> code base that uses u_char internally but has to interface libc and syscalls.
> That one is unnecessary for most code bases which just use char (and that's how
> it should be for strings; if you want unsigned 'char's, you should use
> '-funsigned-char').
>
> But there are cases where the kernel or libc forces us to use casts, like for
> example in bind(2). That's for what the following macro is, and I plan to add
> it to the EXAMPLES section in _Generic(3), replacing the previous program.
>
> The macro itself is quite huge, which might discourage some, but it is not so
> complex. I'll release it to the public domain. Could you please review it?
>
> Cheers,
>
> Alex
>
With C2x's typeof_unqual(), it can be made a bit smaller, and better (it now
supports volatile).
#define sockaddr_cast(t, p) \
_Generic(&*(p), \
struct sockaddr *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr *: \
_Generic((t) NULL, \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in6 *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
struct sockaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in6 **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in6 *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_un *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_un **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_un *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_storage *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
default: (p)), \
struct sockaddr_storage **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
default: (p)), \
const struct sockaddr_storage *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
default: (p)), \
\
default: \
(p) \
)
--
<http://www.alejandro-colomar.es/>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: _Generic.3: EXAMPLES: C++'s static_cast() in C
2022-11-11 15:11 ` Alejandro Colomar
@ 2022-11-11 16:54 ` Alejandro Colomar
2022-11-11 17:08 ` Alejandro Colomar
0 siblings, 1 reply; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-11 16:54 UTC (permalink / raw)
To: linux-man; +Cc: Andrew Clayton
[-- Attachment #1.1: Type: text/plain, Size: 7378 bytes --]
On 11/11/22 16:11, Alejandro Colomar wrote:
> On 11/11/22 15:53, Alejandro Colomar wrote:
>> Dear readers,
>>
>> I've been developing a cast macro for C that is similar to C++'s
>> static_cast(), which allows to:
>>
>> - Cast between a limited set of types (actually, pointers to those types).
>> - Can add const, but not remove it.
>> - Easily grep for all casts.
>>
>> But forbids:
>>
>> - Cast between unrelated types.
>> - Removing const.
>>
>> This improves type safety in C, where casts allow programmers to commit all
>> kinds of crimes by disabling most compiler warnings.
>>
>> The macro I originally wrote, allows to convert between u_char and char, for a
>> code base that uses u_char internally but has to interface libc and syscalls.
>> That one is unnecessary for most code bases which just use char (and that's
>> how it should be for strings; if you want unsigned 'char's, you should use
>> '-funsigned-char').
>>
>> But there are cases where the kernel or libc forces us to use casts, like for
>> example in bind(2). That's for what the following macro is, and I plan to add
>> it to the EXAMPLES section in _Generic(3), replacing the previous program.
>>
>> The macro itself is quite huge, which might discourage some, but it is not so
>> complex. I'll release it to the public domain. Could you please review it?
>>
>> Cheers,
>>
>> Alex
>>
>
> With C2x's typeof_unqual(), it can be made a bit smaller, and better (it now
> supports volatile).
And since it makes no sense to convert between some of the derivative types
(e.g., sa_in to sa_un), it can be further simplified:
#define sockaddr_cast(t, p) \
_Generic(&*(p), \
struct sockaddr *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr **: \
_Generic((t) NULL, \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr *: \
_Generic((t) NULL, \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in6 *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in6 **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in6 *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_un *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_un **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_un *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_storage *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
default: (p)), \
struct sockaddr_storage **: \
_Generic((t) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
default: (p)), \
const struct sockaddr_storage *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
default: (p)), \
\
default: \
(p) \
)
--
<http://www.alejandro-colomar.es/>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: _Generic.3: EXAMPLES: C++'s static_cast() in C
2022-11-11 16:54 ` Alejandro Colomar
@ 2022-11-11 17:08 ` Alejandro Colomar
2022-11-11 21:40 ` Alejandro Colomar
0 siblings, 1 reply; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-11 17:08 UTC (permalink / raw)
To: linux-man; +Cc: Andrew Clayton
[-- Attachment #1.1: Type: text/plain, Size: 7595 bytes --]
On 11/11/22 17:54, Alejandro Colomar wrote:
>
>
> On 11/11/22 16:11, Alejandro Colomar wrote:
>> On 11/11/22 15:53, Alejandro Colomar wrote:
>>> Dear readers,
>>>
>>> I've been developing a cast macro for C that is similar to C++'s
>>> static_cast(), which allows to:
>>>
>>> - Cast between a limited set of types (actually, pointers to those types).
>>> - Can add const, but not remove it.
>>> - Easily grep for all casts.
>>>
>>> But forbids:
>>>
>>> - Cast between unrelated types.
>>> - Removing const.
>>>
>>> This improves type safety in C, where casts allow programmers to commit all
>>> kinds of crimes by disabling most compiler warnings.
>>>
>>> The macro I originally wrote, allows to convert between u_char and char, for
>>> a code base that uses u_char internally but has to interface libc and
>>> syscalls. That one is unnecessary for most code bases which just use char
>>> (and that's how it should be for strings; if you want unsigned 'char's, you
>>> should use '-funsigned-char').
>>>
>>> But there are cases where the kernel or libc forces us to use casts, like for
>>> example in bind(2). That's for what the following macro is, and I plan to
>>> add it to the EXAMPLES section in _Generic(3), replacing the previous program.
>>>
>>> The macro itself is quite huge, which might discourage some, but it is not so
>>> complex. I'll release it to the public domain. Could you please review it?
>>>
>>> Cheers,
>>>
>>> Alex
>>>
>>
>> With C2x's typeof_unqual(), it can be made a bit smaller, and better (it now
>> supports volatile).
>
>
> And since it makes no sense to convert between some of the derivative types
> (e.g., sa_in to sa_un), it can be further simplified:
>
v4:
- Reintroduce some typeof_unqual() that were accidentally removed in v3.
- Align casts for readability.
#define sockaddr_cast(t, p) \
_Generic(&*(p), \
struct sockaddr *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr *: \
_Generic((t) NULL, \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_in6 *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_in6 **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_in6 *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_un *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_storage *: (t) (p), \
default: (p)), \
struct sockaddr_un **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_storage **: (t) (p), \
default: (p)), \
const struct sockaddr_un *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_storage *: (t) (p), \
default: (p)), \
\
struct sockaddr_storage *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
default: (p)), \
struct sockaddr_storage **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
default: (p)), \
const struct sockaddr_storage *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
default: (p)), \
\
default: \
(p) \
)
--
<http://www.alejandro-colomar.es/>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: _Generic.3: EXAMPLES: C++'s static_cast() in C
2022-11-11 17:08 ` Alejandro Colomar
@ 2022-11-11 21:40 ` Alejandro Colomar
2022-11-11 23:06 ` [PATCH v6] _Generic.3: EXAMPLES: Add sockaddr_cast() macro Alejandro Colomar
0 siblings, 1 reply; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-11 21:40 UTC (permalink / raw)
To: linux-man; +Cc: Andrew Clayton
[-- Attachment #1.1: Type: text/plain, Size: 7080 bytes --]
On 11/11/22 18:08, Alejandro Colomar wrote:>>>> Dear readers,
>>>>
>>>> I've been developing a cast macro for C that is similar to C++'s
>>>> static_cast(), which allows to:
>>>>
>>>> - Cast between a limited set of types (actually, pointers to those types).
>>>> - Can add const, but not remove it.
>>>> - Easily grep for all casts.
>>>>
>>>> But forbids:
>>>>
>>>> - Cast between unrelated types.
>>>> - Removing const.
>>>>
>>>> This improves type safety in C, where casts allow programmers to commit all
>>>> kinds of crimes by disabling most compiler warnings.
>>>>
>>>> The macro I originally wrote, allows to convert between u_char and char, for
>>>> a code base that uses u_char internally but has to interface libc and
>>>> syscalls. That one is unnecessary for most code bases which just use char
>>>> (and that's how it should be for strings; if you want unsigned 'char's, you
>>>> should use '-funsigned-char').
>>>>
>>>> But there are cases where the kernel or libc forces us to use casts, like
>>>> for example in bind(2). That's for what the following macro is, and I plan
>>>> to add it to the EXAMPLES section in _Generic(3), replacing the previous
>>>> program.
>>>>
>>>> The macro itself is quite huge, which might discourage some, but it is not
>>>> so complex. I'll release it to the public domain. Could you please review it?
>>>>
>>>> Cheers,
>>>>
>>>> Alex
>>>>
>>>
>>> With C2x's typeof_unqual(), it can be made a bit smaller, and better (it now
>>> supports volatile).
>>
>>
>> And since it makes no sense to convert between some of the derivative types
>> (e.g., sa_in to sa_un), it can be further simplified:
>>
> v4:
>
> - Reintroduce some typeof_unqual() that were accidentally removed in v3.
> - Align casts for readability.
>
v5:
- Casting to sockaddr_storage is useless. Only casting from it makes sense.
I'll send shortly after an v6, if I manage to simplify after this change, but
I'll post this v5 to not have a sudden jump with an unreadably interdiff.
#define sockaddr_cast(t, p) \
_Generic(&*(p), \
struct sockaddr *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
default: (p)), \
struct sockaddr **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
default: (p)), \
const struct sockaddr *: \
_Generic((t) NULL, \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
default: (p)), \
\
struct sockaddr_in *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
default: (p)), \
struct sockaddr_in **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
default: (p)), \
const struct sockaddr_in *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
default: (p)), \
\
struct sockaddr_in6 *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
default: (p)), \
struct sockaddr_in6 **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
default: (p)), \
const struct sockaddr_in6 *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
default: (p)), \
\
struct sockaddr_un *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
default: (p)), \
struct sockaddr_un **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
default: (p)), \
const struct sockaddr_un *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
default: (p)), \
\
struct sockaddr_storage *: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr *: (t) (p), \
struct sockaddr_in *: (t) (p), \
struct sockaddr_in6 *: (t) (p), \
struct sockaddr_un *: (t) (p), \
default: (p)), \
struct sockaddr_storage **: \
_Generic((typeof_unqual(t)) NULL, \
struct sockaddr **: (t) (p), \
struct sockaddr_in **: (t) (p), \
struct sockaddr_in6 **: (t) (p), \
struct sockaddr_un **: (t) (p), \
default: (p)), \
const struct sockaddr_storage *: \
_Generic((t) NULL, \
const struct sockaddr *: (t) (p), \
const struct sockaddr_in *: (t) (p), \
const struct sockaddr_in6 *: (t) (p), \
const struct sockaddr_un *: (t) (p), \
default: (p)), \
\
default: \
(p) \
)
--
<http://www.alejandro-colomar.es/>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6] _Generic.3: EXAMPLES: Add sockaddr_cast() macro
2022-11-11 21:40 ` Alejandro Colomar
@ 2022-11-11 23:06 ` Alejandro Colomar
2022-11-12 14:15 ` [PATCH v7] " Alejandro Colomar
0 siblings, 1 reply; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-11 23:06 UTC (permalink / raw)
To: linux-man; +Cc: Alejandro Colomar, Andrew Clayton
This macro is an example of how C++-style casts can be implemented in C.
They are better than C's casts because they only allow certain
conversions, while disallowing most. This adds considerable type
safety. They also make code more greppable.
A macro similar to const_cast() can also be implemented in a similar
manner:
/* This code is in the public domain. */
#define qual_cast(t, p) \
_Generic(typeof_unqual(&*(p)), \
typeof_unqual(t): \
_Generic(&*(p), \
const t: (t) (p), \
volatile t: (t) (p), \
const volatile t: (t) (p), \
default: (p)) \
default: \
(p) \
)
However, qual_cast() is less useful in quality code, since it breaks const
correctness. It's only useful to interface dubious APIs.
Note that typeof_unqual() is yet unsupported by GCC and Clang, and will
be added to C23. Similar behavior can be achieved by combining GNU
builtins.
Cc: Andrew Clayton <andrew@digital-domain.net>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man3/_Generic.3 | 113 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 1 deletion(-)
diff --git a/man3/_Generic.3 b/man3/_Generic.3
index ddee5f6c4..740b7b358 100644
--- a/man3/_Generic.3
+++ b/man3/_Generic.3
@@ -27,12 +27,123 @@ .SH DESCRIPTION
.SH STANDARDS
C11 and later.
.SH EXAMPLES
+The following code demonstrates how to write
+a macro similar to C++'s
+.BR \%static_cast (),
+which will allow casting safely between a limited set of types.
+It is useful for example when calling
+system calls or library functions that use compatible structures,
+like for example
+.BR bind (2)
+with
+.BR \%sockaddr (3type).
+.IP
+.EX
+/* This code is in the public domain. */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define sockaddr_cast(t, p) \e
+ _Generic(&*(p), \e
+ struct sockaddr *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr_in *: (t) (p), \e
+ struct sockaddr_in6 *: (t) (p), \e
+ struct sockaddr_un *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr_in **: (t) (p), \e
+ struct sockaddr_in6 **: (t) (p), \e
+ struct sockaddr_un **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr_in *: (t) (p), \e
+ const struct sockaddr_in6 *: (t) (p), \e
+ const struct sockaddr_un *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_in *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_in **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_in *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_in6 *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_in6 **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_in6 *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_un *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_un **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_un *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_storage *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ struct sockaddr_in *: (t) (p), \e
+ struct sockaddr_in6 *: (t) (p), \e
+ struct sockaddr_un *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_storage **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ struct sockaddr_in **: (t) (p), \e
+ struct sockaddr_in6 **: (t) (p), \e
+ struct sockaddr_un **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_storage *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ const struct sockaddr_in *: (t) (p), \e
+ const struct sockaddr_in6 *: (t) (p), \e
+ const struct sockaddr_un *: (t) (p), \e
+ default: (p)), \e
+ \e
+ default: \e
+ (p) \e
+ )
+
+socklen_t slen;
+struct sockaddr_storage ss;
+
+slen = sizeof(ss);
+getsockname(sfd, sockaddr_cast(struct sockaddr *, &ss), &slen);
+.EE
+.PP
The following program demonstrates how to write
a replacement for the standard
.BR imaxabs (3)
function, which being a function can't really provide what it promises:
seamlessly upgrading to the widest available type.
-.PP
+.IP
.\" SRC BEGIN (_Generic.c)
.EX
#include <stdint.h>
--
2.38.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7] _Generic.3: EXAMPLES: Add sockaddr_cast() macro
2022-11-11 23:06 ` [PATCH v6] _Generic.3: EXAMPLES: Add sockaddr_cast() macro Alejandro Colomar
@ 2022-11-12 14:15 ` Alejandro Colomar
0 siblings, 0 replies; 7+ messages in thread
From: Alejandro Colomar @ 2022-11-12 14:15 UTC (permalink / raw)
To: linux-man; +Cc: Alejandro Colomar, Andrew Clayton
This macro is an example of how C++-style casts can be implemented in C.
They are better than C's casts because they only allow certain
conversions, while disallowing most. This adds considerable type
safety. They also make code more greppable.
A macro similar to const_cast() can also be implemented in a similar
manner:
/* This code is in the public domain. */
#define qual_cast(t, p) \
_Generic(typeof_unqual(&*(p)), \
typeof_unqual(t): \
_Generic(&*(p), \
const t: (t) (p), \
volatile t: (t) (p), \
const volatile t: (t) (p), \
default: (p)) \
default: \
(p) \
)
Note that typeof_unqual() is yet unsupported by GCC and Clang, and will
be added to C23. Similar behavior can be achieved by combining GNU
builtins.
Cc: Andrew Clayton <andrew@digital-domain.net>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
v7:
- Casting from sockaddr_storage is useless. It violates aliasing rules,
since something declared that type cannot be accessed (it doesn't have
useful members) except aliased to u_char. So, the type is only useful
as argument to sizeof() and in unions.
- Fix the call to getsockname(2) to use a sockaddr_un instead, to avoid
violating aliasing rules.
man3/_Generic.3 | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 90 insertions(+), 1 deletion(-)
diff --git a/man3/_Generic.3 b/man3/_Generic.3
index ddee5f6c4..6ff5e24ea 100644
--- a/man3/_Generic.3
+++ b/man3/_Generic.3
@@ -27,12 +27,101 @@ .SH DESCRIPTION
.SH STANDARDS
C11 and later.
.SH EXAMPLES
+The following code demonstrates how to write
+a macro similar to C++'s
+.BR \%static_cast (),
+which will allow casting safely between a limited set of types.
+It is useful for example when calling
+system calls or library functions that use compatible structures,
+like for example
+.BR bind (2)
+with
+.BR \%sockaddr (3type).
+.IP
+.EX
+/* This code is in the public domain. */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define sockaddr_cast(t, p) \e
+ _Generic(&*(p), \e
+ struct sockaddr *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr_in *: (t) (p), \e
+ struct sockaddr_in6 *: (t) (p), \e
+ struct sockaddr_un *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr_in **: (t) (p), \e
+ struct sockaddr_in6 **: (t) (p), \e
+ struct sockaddr_un **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr_in *: (t) (p), \e
+ const struct sockaddr_in6 *: (t) (p), \e
+ const struct sockaddr_un *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_in *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_in **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_in *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_in6 *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_in6 **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_in6 *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ \e
+ struct sockaddr_un *: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ struct sockaddr_un **: \e
+ _Generic((typeof_unqual(t)) NULL, \e
+ struct sockaddr **: (t) (p), \e
+ default: (p)), \e
+ const struct sockaddr_un *: \e
+ _Generic((t) NULL, \e
+ const struct sockaddr *: (t) (p), \e
+ default: (p)), \e
+ \e
+ default: \e
+ (p) \e
+ )
+
+socklen_t slen;
+struct sockaddr_un sun;
+
+slen = sizeof(ss);
+getsockname(sfd, sockaddr_cast(struct sockaddr *, &sun), &slen);
+.EE
+.PP
The following program demonstrates how to write
a replacement for the standard
.BR imaxabs (3)
function, which being a function can't really provide what it promises:
seamlessly upgrading to the widest available type.
-.PP
+.IP
.\" SRC BEGIN (_Generic.c)
.EX
#include <stdint.h>
--
2.38.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-11-12 14:18 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-11-11 14:53 _Generic.3: EXAMPLES: C++'s static_cast() in C Alejandro Colomar
2022-11-11 15:11 ` Alejandro Colomar
2022-11-11 16:54 ` Alejandro Colomar
2022-11-11 17:08 ` Alejandro Colomar
2022-11-11 21:40 ` Alejandro Colomar
2022-11-11 23:06 ` [PATCH v6] _Generic.3: EXAMPLES: Add sockaddr_cast() macro Alejandro Colomar
2022-11-12 14:15 ` [PATCH v7] " Alejandro Colomar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox