What is the correct form of a type-cast which should be used for non-typesafe binary data operations involving pointer casts which cause data to be reinterpreted as a different type?
For example, if I were to read some data from a network socket using recv
into a buffer, I may then want to "re-interpret" that data as some particular type. The data is initially read using a void*
type.
For example, for a uint64_t
type, we may have something like:
std::vector<uint8_t> buffer(1024); // some length
const auto byte_count = recv(
peer_fd,
&(*buffer.end()),
1024 - buffer.size(),
0
);
const auto p_length = &buffer[some_offset];
const auto p_const = const_cast<const uint8_t* const>(&buffer[some_offset]);
const auto p_length = &p_const[8];
const auto p_length_as_uint64_t = static_cast<const uint64_t* const>(p_length);
const uint64_t length = *p_length_as_uint64_t;
I have now fixed this example to address the const
cast issue, and memory address alignment issue.
- Since
std::vector<uint8_t> buffer(1024)
will usenew
as the default allocator, there is no issue with memory alignment, sincenew
is required to return a memory address which is correctly aligned for all types, regardless of the template typeT
. (In this case,T=uint8_t
.) Since the starting address is correctly aligned, and all offsets are also correctly aligned, this resolves any allignment issue.
This code does not compile. The error message is
Error: Invalid type conversion
and relates to the use of static_cast
.
It is a while since I have done C++ regularly. I reached for static_cast
by default, and was surprised that this did not work.
A few questions.
- Should I be using
static_cast
here? - If not, which is the most appropriate form of type cast to use here?
I thought about just changing this to reinterpret_cast
, because this works. However, is that the right form to use? I am beginning to think a "plain" or "C-style" cast is the most sensible thing to use?
const uint64_t* const p_length_as_uint64_t = (const uint64_t* const)p_length;
Edit: Still somewhat confused, after aggregating information in comments and answers.
Am I to take it that static_cast
still will not work, and reinterpret_cast
is the correct solution? (Elsewhere std::bitcast
was suggested, but does it seem strange to require using something from the standard library to do this?)
What is the correct form of a type-cast which should be used for non-typesafe binary data operations involving pointer casts which cause data to be reinterpreted as a different type?
For example, if I were to read some data from a network socket using recv
into a buffer, I may then want to "re-interpret" that data as some particular type. The data is initially read using a void*
type.
For example, for a uint64_t
type, we may have something like:
std::vector<uint8_t> buffer(1024); // some length
const auto byte_count = recv(
peer_fd,
&(*buffer.end()),
1024 - buffer.size(),
0
);
const auto p_length = &buffer[some_offset];
const auto p_const = const_cast<const uint8_t* const>(&buffer[some_offset]);
const auto p_length = &p_const[8];
const auto p_length_as_uint64_t = static_cast<const uint64_t* const>(p_length);
const uint64_t length = *p_length_as_uint64_t;
I have now fixed this example to address the const
cast issue, and memory address alignment issue.
- Since
std::vector<uint8_t> buffer(1024)
will usenew
as the default allocator, there is no issue with memory alignment, sincenew
is required to return a memory address which is correctly aligned for all types, regardless of the template typeT
. (In this case,T=uint8_t
.) Since the starting address is correctly aligned, and all offsets are also correctly aligned, this resolves any allignment issue.
This code does not compile. The error message is
Error: Invalid type conversion
and relates to the use of static_cast
.
It is a while since I have done C++ regularly. I reached for static_cast
by default, and was surprised that this did not work.
A few questions.
- Should I be using
static_cast
here? - If not, which is the most appropriate form of type cast to use here?
I thought about just changing this to reinterpret_cast
, because this works. However, is that the right form to use? I am beginning to think a "plain" or "C-style" cast is the most sensible thing to use?
const uint64_t* const p_length_as_uint64_t = (const uint64_t* const)p_length;
Edit: Still somewhat confused, after aggregating information in comments and answers.
Am I to take it that static_cast
still will not work, and reinterpret_cast
is the correct solution? (Elsewhere std::bitcast
was suggested, but does it seem strange to require using something from the standard library to do this?)
3 Answers
Reset to default 2There is no correct cast. The correct action is to memcpy
(or equivalent) from your uint8_t[]
into an appropriately sized uint64_t[]
.
If the only thing being sent is a uint64_t[]
, then recv
can go directly to the destination, it acts like memcpy
for trivially copyable types.
static_cast
is incorrect, you need reinterpret_cast
. However unless you want to deal with misaligned accesses and C/C++ possibly counting them as UB, then you need to ensure your buffer is 8-byte aligned.
The easiest way to do that is to do the cast the other way:
std::vector<uint64_t> buffer;
const uint8_t* buffer_u8 = reinterpret_cast<const uint8_t*>(buffer.data());
...
reinterpret_cast
is the correct cast to use for non-typesafe pointer conversion. Your "plain" or "C-style" cast would do the same implicitly, however, kind of masking the type conversion.
Your specific compiler error, however, is more likely the result of casting constness. For those situations use const_cast
(see reference).
std::bit_cast
orstd::start_lifetime_as
(I'm not sure which at this point) – Oersted Commented Feb 6 at 9:19