I wrote a library that requires little endian and bitfields to be ordered from low to high
I run similar code at runtime to check it. I was wondering if I could do this at compile time?
#include <cstring>
#include <cassert>
#include <cstdio>
#include <cstdint>
struct A {
uint64_t a : 4, b : 5, c:55;
A()=default;
//constexpr A(uint64_t value) { memcpy(this, &value, 8); }
A(uint64_t value) { memcpy(this, &value, 8); }
};
static_assert(sizeof(A) == 8);
int main() {
A test(0x3F3);
assert(test.a == 3);
assert(test.b == 0x1F);
assert(test.c == 1);
}
CodePudding user response:
You can replace the memcpy
with bit_cast
to make your constructor constexpr
:
#include <bit>
#include <cstdint>
struct A {
uint64_t a : 4, b : 5, c:55;
A()=default;
constexpr A(uint64_t value) { *this = std::bit_cast<A>(value); }
};
static_assert(A(0x3F3).a == 3);
static_assert(A(0x3F3).b == 0x1F);
static_assert(A(0x3F3).c == 1);
It should optimize to the same thing as memcpy
.
Note that older versions of some compilers don't support bit_cast
to types
with bit fields. Probably the best solution for these is to keep the memcpy
and use a build tool to check this before you compile.
CodePudding user response:
For endianess, just use the C 20 std::endian
check:
#include <bit>
struct A {
static_assert(std::endian::native == std::endian::little);
// ...
};
If this compiles, the target is using little endian.
For bit-fields:
The following properties of bit-fields are implementation-defined:
- The value that results from assigning or initializing a signed bit-field with a value out of range, or from incrementing a signed bit-field past its range.
- Everything about the actual allocation details of bit-fields within the class object
- For example, on some platforms, bit-fields don't straddle bytes, on others they do
- Also, on some platforms, bit-fields are packed left-to-right, on others right-to-left
CodePudding user response:
if I could do this at compile time?
No, you can't. Not only that, but your class is not portable and compiler and platform specific.
Rewrite your class to be using bit masks and accessors and getters for each field, and it will be portable and test-able staticallly.
#include <cstring>
#include <cassert>
#include <cstdio>
#include <cstdint>
#include <iostream>
struct A {
uint64_t value;
constexpr A(uint64_t value) : value(value) { }
constexpr uint64_t get(size_t offset, size_t len) const {
return (value >> offset) & ((1ull << len) - 1);
}
constexpr uint64_t a() const { return get(0, 4); }
constexpr uint64_t b() const { return get(4, 5); }
constexpr uint64_t c() const { return get(9, 55); }
};
static_assert(sizeof(A) == 8);
int main() {
static_assert(A(0x3F3).a() == 3);
static_assert(A(0x3F3).b() == 0x1f);
static_assert(A(0x3F3).c() == 1);
}