最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - What is the correct type of cast to use for non-typesafe pointer conversions for binary data reinterpret operations? - Sta

programmeradmin1浏览0评论

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 use new as the default allocator, there is no issue with memory alignment, since new is required to return a memory address which is correctly aligned for all types, regardless of the template type T. (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 use new as the default allocator, there is no issue with memory alignment, since new is required to return a memory address which is correctly aligned for all types, regardless of the template type T. (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?)

Share Improve this question edited Feb 8 at 22:38 user2138149 asked Feb 6 at 9:11 user2138149user2138149 16.8k30 gold badges145 silver badges287 bronze badges 22
  • 3 Please try to create a proper minimal reproducible example, and copy-paste the full ande complete build-log. Error messages often have informational notes that could be useful to see. – Some programmer dude Commented Feb 6 at 9:15
  • 4 you may need std::bit_cast or std::start_lifetime_as (I'm not sure which at this point) – Oersted Commented Feb 6 at 9:19
  • 1 Instead of using raw sockets + raw data why don't you use a serialization library like flattbuffers/protobuf + sockets. Or and RPC mechanism like gRPC? Those will solve a lot of your problems for you assuming you don't have to be very high-performance. There is a price you pay for trying to go too low level in your own code – Pepijn Kramer Commented Feb 6 at 10:10
  • 2 Your example currently also makes a few assumptions you're not aware of. You assume the sender and receiver : Both have the same endianess, have the same memory alignment and the compiler settings are such that the resulting memory layout is portable too. So it might well be there is no correct cast. (That's what I mean by binary serialization is hard) – Pepijn Kramer Commented Feb 6 at 10:14
  • 1 Because you don't describe the sender, or specify if this is a particular protocol. I didn't say it was an issue, I said it was a potential issue. – Caleth Commented Feb 6 at 10:36
 |  Show 17 more comments

3 Answers 3

Reset to default 2

There 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).

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论