I have a system call like the following one:
int transfer(int handle, int direction, unsigned char *data, int length);
I have written the following two functions:
int input(int handle, void* data, int length)
{
return transfer(handle, 1, static_cast<unsigned char*>(data), length);
}
int output(int handle, const void* data, int length)
{
return transfer(handle, 0, static_cast<unsigned char*>(const_cast<void*>(data)), length);
}
I don't like the nested const_cast
inside the static_cast
, is there a way to perform the conversion from const void*
to unsigned char*
in a single step?
Using a C-style cast will generate identical assembly. As seen in Compiler Explorer:
//Source #1
int transfer(int handle, int direction, unsigned char *data, int length);
int input(int handle, void* data, int length)
{
return transfer(handle, 1, static_cast<unsigned char*>(data), length);
}
int output(int handle, const void* data, int length)
{
return transfer(handle, 0, static_cast<unsigned char*>(const_cast<void*>(data)), length);
}
//Source #2
int transfer(int handle, int direction, unsigned char *data, int length);
int input(int handle, void* data, int length)
{
return transfer(handle, 1, (unsigned char*)data, length);
}
int output(int handle, const void* data, int length)
{
return transfer(handle, 0, (unsigned char*)data, length);
}
//Assembly (both)
input(int, void*, int):
mov ecx, edx
mov rdx, rsi
mov esi, 1
jmp transfer(int, int, unsigned char*, int)
output(int, void const*, int):
mov ecx, edx
mov rdx, rsi
xor esi, esi
jmp transfer(int, int, unsigned char*, int)
So it's clear that simply using a C-style cast will solve your problem.
The reason for the verboseness of the C++ casts is to ensure that you aren't making mistakes. When a maintainer sees your code, it's important that they see the const_cast
and the static_cast
, because writing the code this way informs the reader that casting away the const
-ness of the pointer is intentional and desired behavior. A code maintainer should see these casts, and presume that there was intent behind the code, instead of having to guess whether you knew that casting directly from const void*
to unsigned char*
would involve risking Undefined Behavior. Your example might not contain UB (since you specified that the contract of transfer
is to treat data
as read-only when direction
is 0), but it's important that anyone else who needs to make changes to your code understands the deliberateness of your coding practices.