Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping a structure to uint64_t

Tags:

c++

What is the right way to convert the structure below to uint64_t?

struct Data
{
    uint64_t sign : 1;
    uint64_t exp : 4;
    uint64_t man : 8;
};

static_assert(sizeof(Data) == sizeof(uint64_t));

the obvious one is

Data data;
uint64_t n = *(reinterpret_cast<const uint64_t*>(&data));

but it does not compile as constexpr and produces the following warnings in GCC:

dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

EDIT1:

The value of resulting uint64_t may be different with different compilers. But the value of Data structure should be the same when I convert it back from uint64_t.

So, saying more exactly I need:

Data data;
uint64_t n = convert(data);
Data data2 = convert_back(n);
static_assert(data == data2);
like image 390
Dmitriano Avatar asked Dec 17 '22 11:12

Dmitriano


2 Answers

You std::memcpy the structure to an uint64_t. std::memcpy is a legal way to perform type punning, it is allowed since your structure is trivially-copyable.


In C++20 there is also std::bit_cast.

Unlike std::memcpy it's constexpr, but to make it work at compile-time I had to add uint64_t : 51; at the end of the struct.

Interestingly, Clang (unlike GCC and MSVC) refused to perform it at compile-time:

note: constexpr bit_cast involving bit-field is not yet supported
like image 179
HolyBlackCat Avatar answered Dec 29 '22 09:12

HolyBlackCat


The conversion is not "obvious" because the exact layout of Data is implementation defined (see bit field). Moreover, your cast via pointers breaks strict aliasing, as the error suggests. There is no uint64_t stored at the adress of data.

You can convert it like this:

constexpr uint64_t Data_2_uint(const Data& d){
    return d.sign + (d.exp << 1) + (d.man << 5);
}
like image 24
463035818_is_not_a_number Avatar answered Dec 29 '22 07:12

463035818_is_not_a_number