Skip to content

Instantly share code, notes, and snippets.

@RMDarth
Last active February 2, 2024 02:47
Show Gist options
  • Save RMDarth/6357ddd6e09b4117efe84fc3347a732a to your computer and use it in GitHub Desktop.
Save RMDarth/6357ddd6e09b4117efe84fc3347a732a to your computer and use it in GitHub Desktop.
Compile-time size of the field in bit field struct (with C++11 restrictions)
// I've needed a constexpr way to get a size of the bit fields in struct (i.e. number of bits).
// Also, I needed it to work in Visual Studio 2015 (SP3), which is not yet fully support C++14,
// so constexpr functions could only use a single return statement and (almost) nothing more.
// After some time I've come up with this solution. It should work with any C++11 compiler.
// It has some limitations (it only works with unsigned fields).
// P.S.: With C++17 it could be done easier by using constexpr lambda expressions in macro.
#include <iostream>
#include <array>
// Helper constexpr functions
namespace BitFieldDetails
{
template<typename T>
constexpr auto getBitsCount(T data, size_t startBit = 0) -> typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
{
return (startBit == sizeof(T) * 8) ? 0 :
getBitsCount(data, startBit + 1) + ((data & (1ull << startBit)) ? 1 : 0);
}
// We should support unsigned enums too
template<typename T>
constexpr auto getBitsCount(T data, size_t startBit = 0) -> typename std::enable_if<std::is_enum<T>::value, size_t>::type
{
return (startBit == sizeof(T) * 8) ? 0 :
getBitsCount(data, startBit + 1) + ((static_cast<typename std::underlying_type<T>::type>(data) & (1ull << startBit)) ? 1 : 0);
}
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wbitfield-constant-conversion"
#endif
template<typename StructType, typename StructFieldsType, StructFieldsType ...initVals>
constexpr StructType getFilledStructImpl(StructType s, ...)
{
return s;
}
template<typename StructType, typename StructFieldsType, StructFieldsType ...initVals>
constexpr StructType getFilledStructImpl(StructType s, decltype(StructType{ initVals... }, int()) = 0)
{
// can you numeric_limits::max() here instead of -1, but it will require casting to and from underlying type for enum
return getFilledStructImpl<StructType, StructFieldsType, initVals...,
static_cast<StructFieldsType>(-1)> (StructType { initVals... }, 0);
}
template<typename StructType, typename StructFieldsType>
constexpr StructType getFilledStruct()
{
using UnderlyingStructType = typename std::conditional<
std::is_enum<StructFieldsType>::value,
std::underlying_type<StructFieldsType>,
std::remove_cv<StructFieldsType>>::type::type;
static_assert(std::is_unsigned<UnderlyingStructType>::value,
"Bit field calculation only works with unsigned values");
return getFilledStructImpl<StructType, StructFieldsType>(StructType{}, 0);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace BitFieldDetails
// Main macro
#define bitsizeof(structType, fieldName) \
::BitFieldDetails::getBitsCount(::BitFieldDetails::getFilledStruct<structType, decltype(structType{}.fieldName)>().fieldName)
// Structs to analyze
struct S1
{
uint16_t a : 2;
uint16_t b : 3;
uint16_t c : 7;
uint16_t d : 4;
};
enum class SomeEnum : uint32_t
{
Val1,
Val2,
Val3
};
struct S2
{
SomeEnum x : 20;
uint32_t y : 10;
uint32_t z : 2;
};
int main()
{
std::cout << bitsizeof(S1, c) << std::endl; // should output 7
std::cout << bitsizeof(S2, x) << std::endl; // should output 20
std::array<uint8_t, bitsizeof(S1, b)> arr{};
std::cout << "Array size = " << arr.size() << std::endl; // should output Array size = 3
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment