c++ computing compile time constants while preventing integral constant overflow - c++

I am sort of new to the language features of meta programming and I am trying to make a simple class with public static const variables that will set its values by compile time constants:
What I'm trying to achieve: I want to compute values that are the power of some exponent that are measured in number of bytes converted to number of bits with a base of 2. All calculations are in base 2.
Examples:
1 byte(s) = 8 bits: value = pow(2, 8) = 256;
2 byte(s) = 16 bits: value = pow(2, 16) = 65536
4 byte(s) = 32 bits: value = pow(2, 32) = 4294967296
8 byte(s) = 64 bits: value = pow(2, 64) = 18446744073709551616
I've tried writing a function to do the calculations to compute the values needed while trying to use constexpr or const, and I've tried using templates. I would like to use the const function, constexpr function or function template as such:
// constexpr function
constexpr std::uint64_t pow2( const std::uint32_t expInBytes, const std::uint32_t base = 2 ) {
const std::uint32_t expInBits = expInBytes * CHAR_BIT;
return static_cast<std::uint64_t>( expInBits == 0 ? 1 : base * pow2( base, expInBits - 1 ) );
}
// or function template
template<std::uint32_t expInbytes>
constexpr std::uint64_t pow2() {
const std::uint32_t base = 2;
const std::uint32_t expInBits = expInBytes * CHAR_BIT;
return (expInBits == 0 ? 1 : base * pow2<expInBytes-1>() );
}
template<>
constexpr std::uint64_t pow2<0>() {
return 0;
};
// template parameter T not used but needed to use the class as such:
// BitCombinations<>::static_member;
template<typename T = const std::uint32_t>
class BitCombinations {
public: // template // non template
static const std::uint64_t ONE_BYTE = pow2<1>(); // pow2( 1 );
static const std::uint64_t TWO_BYTES = pow2<2>(); // pow2( 2 );
static const std::uint64_t FOUR_BYTES = pow2<4>(); // pow2( 4 );
static const std::uint64_t EIGHT_BYTES = pow2<8>(); // pow2( 8 );
};
Through my efforts I've generated all sorts of compile time, run time errors ect. The latest attempt I was able to get the template version of the pow2<>() above to compile and run, however I'm not getting the correct results.
I'm not sure if my implementation of pow2 is wrong or if my syntax is wrong, or if I'm not using const or constexpr correctly and in some cases I kept getting the integral constant overflow as a compile time error from MS Visual Studio 2017 CE compiler.
I've been following these patterns for the pow2() function:
nullptr.me:C++11 constexpr : computing exp at compile time
prosepoetrycode.potterpcs.net : A simple constexpr power function (C++)
cppreference.com : math::exp2
reformatcode.com : c++ power of integer, template meta programming
I can not seem to wrap my mind around this and don't know what else to try.

Notice that your last case isn't possible currently. You can't store 2^64 in an 8 byte type, the maximum is 2^64 - 1. At least on mainstream architectures, don't know which one you are using.
I see two problems with your function template.
You multiply the result with base only once, but you decrement the bits count by 8 by doing expInBytes - 1. So, you need to multiply it eight times:
return (expInBits == 0 ? 1 : base * base * base * base * base * base * base * base * pow2<expInBytes-1>() );
The specialization for 0 returns 0, and any number multiplied with 0 is 0. :) If you think that you handled the case with expInBits == 0, think again: The only way for expInBits to be 0 is if expInBytes is 0, but that cannot be in the primary template because you have a specialization for when expInBytes is 0! That means that that branch is never taken, it literally has no effect.
Your function has the same problem described in 1), and in addition you are passing the wrong value to it when recursing (expInBits instead of expInBytes) and the order is wrong (base comes last).
In my opinion, a loop is easier to understand and less error-prone:
constexpr std::uint64_t pow2(const std::uint32_t expInBytes, const std::uint32_t base = 2) {
const std::uint32_t expInBits = expInBytes * CHAR_BIT;
std::uint64_t result = 1;
for (std::uint32_t i = 0; i < expInBits; ++i)
result *= base;
return result;
}

With the help of Rakete111 and his positive feed back; I was able to work through my problem as he pointed out a few mistakes. In return I was able to achieve some resemblance of what I wanted. To compute 2^n at compile time.
Here is the working code:
inline constexpr std::uint64_t powerOfBits( const std::uint64_t base, std::uint64_t const exponent ) {
return (exponent == 0) ? 1 : (base * powerOfBits( base, exponent - 1 ));
}
/*template<typename T = const std::uint32_t>*/
class BitCombinations {
public:
// Because I don't care for "magic numbers"
static const std::uint64_t binaryBase = std::uint64_t(2);
static const std::uint64_t eightBits = std::uint64_t( 8 );
static const std::uint64_t sixteenBits = std::uint64_t( 16 );
static const std::uint64_t thirtyTwoBits = std::uint64_t( 32 );
static const std::uint64_t sixtyFourBits = std::uint64_t( 64 );
// Now Generate Our Compile Time Constants
static const std::uint64_t ONE_BYTE = powerOfBits( binaryBase , eightBits ); //
static const std::uint64_t TWO_BYTES = powerOfBits( binaryBase , sixteenBits ); //
static const std::uint64_t FOUR_BYTES = powerOfBits( binaryBase , thirtyTwoBits ); //
// For 64bit int need to subtract 1 from the exponent otherwise you will have integral overflow
// To prevent this we just take 2^63, then in any output display we will have to append the
// string or characters `x 2` so that the user knows the value is double what they are seeing.
static const std::uint64_t EIGHT_BYTES = powerOfBits( binaryBase , sixtyFourBits - 1 );
};
int main() {
std::cout << BitCombinations::ONE_BYTE << std::endl;
std::cout << BitCombinations::TWO_BYTES << std::endl;
std::cout << BitCombinations::FOUR_BYTES << std::endl;
// Remember that 2^64 causes overflow: need to append characters to user.
std::cout << BitCombinations::EIGHT_BYTES << " x 2" << std::endl;
std::cout << std::endl;
std::cout << "\nPress any key and enter to quite." << std::endl;
char q;
std::cin >> q;
return 0;
}
Thank you so much for your help and pointing me in the right direction. I will accept your answer.

Related

C7595 error after reinstalling visual studio

I recently reinstalled visual studio, and apon reinstalling and attempting to compile some code of mine that had compiled completely fine up until that point, I was met with an error related to constant expressions inside of std::format strings.
I am using a compile time string encryption library known as xorstring and have not previously encountered this error. A minimum reproduceable example is provided below:
#include <iostream>
#include <string>
#include <format>
#include <array>
namespace strenc
{
constexpr auto time = __TIME__;
constexpr auto seed = static_cast<int>(time[7]) + static_cast<int>(time[6]) * 10 + static_cast<int>(time[4]) * 60 + static_cast<int>(time[3]) * 600 + static_cast<int>(time[1]) * 3600 + static_cast<int>(time[0]) * 36000;
// 1988, Stephen Park and Keith Miller
// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
// with 32-bit math and without division
template < int N >
struct RandomGenerator
{
private:
static constexpr unsigned a = 16807; // 7^5
static constexpr unsigned m = 2147483647; // 2^31 - 1
static constexpr unsigned s = RandomGenerator< N - 1 >::value;
static constexpr unsigned lo = a * (s & 0xFFFF); // Multiply lower 16 bits by 16807
static constexpr unsigned hi = a * (s >> 16); // Multiply higher 16 bits by 16807
static constexpr unsigned lo2 = lo + ((hi & 0x7FFF) << 16); // Combine lower 15 bits of hi with lo's upper bits
static constexpr unsigned hi2 = hi >> 15; // Discard lower 15 bits of hi
static constexpr unsigned lo3 = lo2 + hi;
public:
static constexpr unsigned max = m;
static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
};
template <>
struct RandomGenerator< 0 >
{
static constexpr unsigned value = seed;
};
template < int N, int M >
struct RandomInt
{
static constexpr auto value = RandomGenerator< N + 1 >::value % M;
};
template < int N >
struct RandomChar
{
static const char value = static_cast<char>(1 + RandomInt< N, 0x7F - 1 >::value);
};
template < size_t N, int K >
struct XorWString
{
private:
const wchar_t _key;
std::array< wchar_t, N + 1 > _encrypted;
bool decrypted = false;
constexpr wchar_t enc(wchar_t c) const
{
return c ^ _key;
}
wchar_t dec(wchar_t c) const
{
return c ^ _key;
}
public:
template < size_t... Is >
constexpr __forceinline XorWString(const wchar_t* str, std::index_sequence< Is... >) : _key(RandomChar< K >::value), _encrypted{ enc(str[Is])... }
{
}
__forceinline decltype(auto) decrypt(void)
{
if (!decrypted)
{
for (size_t i = 0; i < N; ++i)
{
_encrypted[i] = dec(_encrypted[i]);
}
_encrypted[N] = '\0';
decrypted = true;
}
return _encrypted.data();
}
};
}
#define xorws( s ) ( strenc::XorWString< sizeof( s ) - 1, __COUNTER__ >( s, std::make_index_sequence< sizeof( s ) - 1>() ).decrypt() )
int main()
{
auto str = std::format(xorws(L"this is a formatted string {}"), 1); // <- error here
}
You should get Severity Code Description Project File Line Suppression State
Error C7595 'std::_Basic_format_string<wchar_t,int>::_Basic_format_string': call to immediate function is not a constant expression
apon trying to run the program.
build log:
1>------ Build started: Project: test_app, Configuration: Release x64 ------
1>test_app.cpp
1>C:\Users\throw\source\repos\test_app\test_app\test_app.cpp(98,25): error C7595: 'std::_Basic_format_string<wchar_t,int>::_Basic_format_string': call to immediate function is not a constant expression
1>C:\Users\throw\source\repos\test_app\test_app\test_app.cpp(74,142): message : failure was caused by out of range index 30; allowed range is 0 <= index < 30
1>Done building project "test_app.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
compiler information:
visual studio 2019 latest version
windows sdk version 10.22000
platform toolset v142
language standard /std:c++20
if this is an actual error with my code, what can I do to fix it, and why wasn't I encountering this error before, and if it isn't, what can I do to remedy my MSVC install.
Thank you!
With the recent update to the Microsoft's STL, std::format now requires 100% constant values, but they have added std::vformat for runtime strings. I did not know this feature was added. If you have run into similar issues, try using std::vformat instead.
Met the same behavior (gcc & clang are ok).
Minimal example: https://gcc.godbolt.org/z/c7ejsM6eh
Filed bug #msvc-dev: https://developercommunity.visualstudio.com/t/C7595-error-on-valid-code/10150938
My impression - MSVC consteval support wants a huge lot to desired.

How to assign a 32-bit unsigned integer to a bit field containing 32 bits

I am trying to create a bit-field struct which has 32 total bits, but when I try to assign a 32-bit number to it, I get this error:
Implicit truncation from 'unsigned int' to bit-field changes value from 4278190080 to 0
Here is my struct and how I'm trying to use it
struct Color32 {
uint32_t a : 8;
uint32_t r : 8;
uint32_t g : 8;
uint32_t b : 8;
};
Color32 BLACK = {0xFF000000}; // this line has the compilation error
I see other questions around bit-field assignment, but they all seem to use bit-wise operations to set the individual fields.
There's also this reference which has the following sample, which seems to be the same way I'm using it, only mine won't compile:
#include <iostream>
struct S {
// three-bit unsigned field,
// allowed values are 0...7
unsigned int b : 3;
};
int main()
{
S s = {6};
++s.b; // store the value 7 in the bit field
std::cout << s.b << '\n';
++s.b; // the value 8 does not fit in this bit field
std::cout << s.b << '\n'; // formally implementation-defined, typically 0
}
You could use aggregate initialization here
Color32 BLACK = {0xFF, 0x00, 0x00, 0x00};
By the way, I would suggest modifying your Color32 struct to the following, which will have the same effect as specifying the bit field of your members
struct Color32 {
uint8_t a;
uint8_t r;
uint8_t g;
uint8_t b;
};
Something like this will sort of give you the best of both worlds:
struct Color32 {
union {
uint32_t color;
struct {
uint32_t b : 8;
uint32_t g : 8;
uint32_t r : 8;
uint32_t a : 8;
};
};
};
// will construct using single value
Color32 test{ 0x01020304 };
Color32 black{0xff000000 };
// can assign to individual fields
test.a = 0x04;
test.r = 0x03;
test.g = 0x02;
test.b = 0x01;
// can assign to the whole value like this.
test.color = 0xff000000;
test.color = black.color;
An issue with this is that the order of a, b, g, r in the struct may be dependent upon your specific compiler. For VS2017 compiling to windows target, the order shown will produce the expected results. I believe there may be a way to force the order somehow, but I am not familiar with how to do it.
Bitfield or not, your type has four members, not one.
You seem to be trying to treat it as a union.
Initialise each member individually as you would with any other type, or switch to a union (and then rely on type-punning as many do, with the usual caveats).
The counter-example you give is not the same, as it is a UDT with a single member and a single value in the initialiser; since the number of members given matches, everything is fine there.
After diving more into the topic, i've found that multiple bit fields aren't useful without bitwise operators and valid constructors, with that it depends a lot on the operating system.
The answer is tested on cygwin ( -Wno-unused-variable -O0 -ggdb flags ) on windows 7
Version 1: union
This is basic implementation without any bit fields, most common implementation of 4 byte colour space.
#include <iostream>
union c_space32{
uint32_t space;
uint8_t channels[4];
};
int main(){
{ // just a anonymous scope to keep things clear
union c_space32 temp = {0xff00fe32};
std::cout << "sizeof : " << sizeof( union c_space32 ) << "\n\n";
std::cout << (int)temp.channels[1] << "\t" << std::hex << temp.space << "\n";
++temp.channels[1];
std::cout << (int)temp.channels[1] << "\t" << std::hex << temp.space << "\n";
++temp.channels[1];
std::cout << (int)temp.channels[1] << "\t" << std::hex << temp.space << "\n";
}
return 0;}
The union behaves as normal color space, and every uint8_t part of the union behaves as unique byte, so overall change of value in c_space32.channels doesn't affect the value of c_space32.space outside of the scope of the byte. This is the output i am getting.
sizeof : 4
fe ff00fe32
ff ff00ff32
0 ff000032
Version 2: bit-fields
The issue with bit fields ( among lack of documentation in some cases ), is that they can easily change in size, and that endianness depends on OS so native logic behind structure of bit fields can escape our human logic. Let me give you some examples for future guys/gals who wish to endeavour into this topic.
#include <iostream>
#include <bitset>
struct temp1{
uint8_t a:1;
temp1(uint8_t val){ // just so i can assign it
this->a = (val & 0x1 ); // this is needed to avoid truncated warning
}
};
int main(){
struct temp1 t1 = 3;
uint8_t *ptr = (uint8_t *)&t1;
std::cout << sizeof(struct temp1) << std::endl; // size of 1 byte
std::cout << std::bitset<8>( *ptr ) << std::endl; // 0000-0001 position of our bitfield
return 0;}
So in this case sizeof(struct temp1) returns an size of 1 byte. With the position of our bit field as upmost right. And here is where documentation starts to go MIA.
#include <iostream>
#include <bitset>
struct temp2{
uint8_t a:1;
uint8_t b:1;
uint8_t c:1;
temp2(int VAL){ // just so i can assign it
this->a = (VAL & 0x1 );
this->b = 0;
this->c = (VAL >> 2 ) & 0x1;
}
};
int main(){
struct temp2 t1 = 0xf;
uint8_t *ptr = (uint8_t *)&t1;
std::cout << sizeof(struct temp2) << std::endl; // size of 1
std::cout << std::bitset<8>( *ptr ) << std::endl; // 0000-0101
return 0;}
In this case constructor is a must have, since computer doesn't know how you want to structure the data. Sure in our logic, if we line up bits it is logical that assigning them would be same as they are sharing the memory. But the issue is computer will not do bitwise operators for us. Sure those bits are in order and lined up naturally ,but the computer just grabs some bit and define it as an unique variable, what you choose to place in that variable it is up to you.
If we were to exceed the scope of unit memory size (byte), OS starts interfering in our work.
#include <iostream>
#include <bitset>
struct temp3{
bool b0:1;
bool b1:1;
bool b2:1;
bool b3:1;
bool b4:1;
bool b5:1;
bool b6:1;
bool b7:1;
temp3( int a ){
this->b0 = ( a & 0x1 );
this->b1 = ( a & 0x2 );
this->b2 = ( a & 0x4 );
this->b3 = ( a & 0x8 );
this->b4 = ( a & 0x10 );
this->b5 = ( a & 0x20 );
this->b6 = ( a & 0x40 );
this->b7 = ( a & 0x80 );
}
};
int main(){
struct temp3 t1 = 0xc3;
uint8_t *ptr = (uint8_t *)&t1;
std::cout << sizeof(struct temp3) << std::endl; // still size of 1
std::cout << std::bitset<8>( *ptr ) << std::endl; // 1100-0011
return 0;}
And when we exceed the byte size:
#include <iostream>
#include <bitset>
struct temp4{
bool b0:1;
bool b1:1;
bool b2:1;
bool b3:1;
bool b4:1;
bool b5:1;
bool b6:1;
bool b7:1;
bool b8:1;
temp4( int a ){
this->b0 = ( a & 0x1 );
this->b1 = ( a & 0x2 );
this->b2 = ( a & 0x4 );
this->b3 = ( a & 0x8 );
this->b4 = ( a & 0x10 );
this->b5 = ( a & 0x20 );
this->b6 = ( a & 0x40 );
this->b7 = ( a & 0x80 );
this->b8 = ( a & 0x100 );
}
};
int main(){
struct temp4 t1 = 0x1c3;
uint16_t *ptr = (uint16_t *)&t1;
std::cout << sizeof(struct temp4) << std::endl; // size of 2
std::cout << std::bitset<16>( *ptr ) << std::endl; // 0000-0000 1100-0011
std::cout << t1.b8 << std::endl; // still returns 1
std::cout << "\n\n";
union t_as{
uint16_t space;
temp4 data;
uint8_t bytes[2];
};
union t_as t2 = {0x1c3};
//11000011-00000001
std::cout << std::bitset<8>( t2.bytes[0] ) << "-" << std::bitset<8>( t2.bytes[1] ) << std::endl;
return 0;}
What happened here? Since we added another bool bit-field our struct grew for 1 byte ( since bool is 1 byte ), and our 16 bit pointer doesn't show the last b8 - but the union does. The issue is that OS took over, and in this case stuck the last bit behind our original memory - due to innate OS endianness. As you can see in the union , the byte is still read, but the order is different.
So when exceeding the byte size, normal OS rules apply.
CONCLUSION and ANSWER
struct half_opacity{
uint8_t alpha:4;
uint8_t red;
uint8_t green;
uint8_t blue;
half_opacity(int a){
this->alpha = ( a >> 24 )&0xf;
this->red = ( a >> 16 )&0xff;
this->green = ( a >> 8 )&0xff;
this->blue = ( a & 0xff );
}
operator uint32_t(){
return ( this->alpha << 24 )
| ( this->red << 16 )
| ( this->green << 8 )
| this->blue;
}
};
{
struct half_opacity c_space = 0xff00AABB;
std::cout << "size of : " << sizeof(struct half_opacity) << std::endl; //size of : 4
std::cout << std::hex << (uint32_t)c_space << std::endl; // 0x0f00AABB
}
So unless you plan to confide the original channel to some bit size, i would strongly suggest using union approach, since there isn't any added benefit into splitting the 32 bit integer into individual bytes with bit-fields. The major thing about bit fields is that you need to split them and build then back up as with any other integer field - bit shifts often circumvent the whole OS endianness thing.
The truncation warning you got, was due to multiple members in your struct, and struct naturally assigning the first one , and since you added more than the bit field could handle the compiler warned you that some data will be lost.

Is there a way I can use a 2-bit size type instead of an int, by just plugging in the new type name instead of int?

I have an application where I need to save as much of memory as possible. I need to store a large amount of data that can take exactly three possible values. So, I have been trying to use a 2 bit sized type.
One possibility is using bit fields. I could do
struct myType {
uint8_t twoBits : 2;
}
This is a suggestion from this thread.
However, everywhere where I have used int variables prior to this, I would need to change their usage by appending a .twoBits. I checked if I can create a bit field outside of a struct, such as
uint8_t twoBits : 2;
but this thread says it is not possible. However,that thread is specific to C, so I am not sure if it applied to C++.
Is there a clean way I can define a 2-bit type, so that by simply replacing int with my type, I can run the program correctly? Or is using bit fields the only possible way?
CPU, and thus the memory, the bus, and the compiler too, uses only bytes or groups of bytes. There's no way to store a 2-bits type without storing also the other 6 remaining bits.
What you can so is define a struct that only uses some bits. But we aware that it will not save memory.
You can pack several x-bits types in a struct, as you already know. Or you can do bits operations to pack/unpack them into a integer type.
Is there a clean way I can define a 2-bit type, so that by simply
replacing int with my type, I can run the program correctly? Or is
using bit fields the only possible way?
You can try to make the struct as transparent as possible by providing implicit conversion operators and constructors:
#include <cstdint>
#include <iostream>
template <std::size_t N, typename T = unsigned>
struct bit_field {
T rep : N;
operator T() { return rep; }
bit_field(T i) : rep{ i } { }
bit_field() = default;
};
using myType = bit_field<2, std::uint8_t>;
int main() {
myType mt;
mt = 3;
std::cout << mt << "\n";
}
So objects of type my_type somewhat behave like real 3-bit unsigned integers, despite having more than 3 bits.
Of course, the residual bits are unused, but as single bits are not addressable on most systems, this is the best way to go.
I'm not convinced that you will save anything with your existing structure, as the surrounding structure still gets rounded up to a whole number of bytes.
You can write the following to squeeze 4 2-bit counters into 1 byte, but as you say, you have to name them myInst.f0:
struct MyStruct
{
ubyte_t f0:2,
f1:2,
f2:2,
f3:2;
} myInst;
In c and c++98, you can declare this anonymous, but this usage is deprecated. You can now access the 4 values directly by name:
struct
{ // deprecated!
ubyte_t f0:2,
f1:2,
f2:2,
f3:2;
};
You could declare some sort of template that wraps a single instance with an operator int and operator =(int), and then define a union to put the 4 instances at the same location, but again anonymous unions are deprecated. However you could then declare references to your 4 values, but then you are paying for the references, which are bigger than the bytes you were trying to save!
template <class Size,int offset,int bits>
struct Bitz
{
Size ignore : offset,
value : bits;
operator Size()const { return value; }
Size operator = (Size val) { return (value = val); }
};
template <class Size,int bits>
struct Bitz0
{ // I know this can be done better
Size value : bits;
operator Size()const { return value; }
Size operator = (Size val) { return (value = val); }
};
static union
{ // Still deprecated!
Bitz0<char, 2> F0;
Bitz<char, 2, 2> F1;
Bitz<char, 4, 2> F2;
Bitz<char, 6, 2> F3;
};
union
{
Bitz0<char, 2> F0;
Bitz<char, 2, 2> F1;
Bitz<char, 4, 2> F2;
Bitz<char, 6, 2> F3;
} bitz;
Bitz0<char, 2>& F0 = bitz.F0; /// etc...
Alternatively, you could simply declare macros to replace the the dotted name with a simple name (how 1970s):
#define myF0 myInst.f0
Note that you can't pass bitfields by reference or pointer, as they don't have a byte address, only by value and assignment.
A very minimal example of a bit array with a proxy class that looks (for the most part) like you were dealing with an array of very small integers.
#include <cstdint>
#include <iostream>
#include <vector>
class proxy
{
uint8_t & byte;
unsigned int shift;
public:
proxy(uint8_t & byte,
unsigned int shift):
byte(byte),
shift(shift)
{
}
proxy(const proxy & src):
byte(src.byte),
shift(src.shift)
{
}
proxy & operator=(const proxy &) = delete;
proxy & operator=(unsigned int val)
{
if (val <=3)
{
uint8_t wipe = 3 << shift;
byte &= ~wipe;
byte |= val << shift;
}
// might want to throw std::out_of_range here
return *this;
}
operator int() const
{
return (byte >> shift) &0x03;
}
};
Proxy holds a reference to a byte and knows how to extract two specific bits and look like an int to anyone who uses it.
If we wrap an array of bits packed into bytes with a class that returns this proxy object wrapped around the appropriate byte, we now have something that looks a lot like an array of very small ints.
class bitarray
{
size_t size;
std::vector<uint8_t> data;
public:
bitarray(size_t size):
size(size),
data((size + 3) / 4)
{
}
proxy operator[](size_t index)
{
return proxy(data[index/4], (index % 4) * 2);
}
};
If you want to extend this and go the distance, Writing your own STL Container should help you make a fully armed and operational bit-packed array.
There's room for abuse here. The caller can hold onto a proxy and get up to whatever manner of evil this allows.
Use of this primitive example:
int main()
{
bitarray arr(10);
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 1;
arr[4] = 2;
arr[5] = 3;
arr[6] = 1;
arr[7] = 2;
arr[8] = 3;
arr[9] = 1;
std::cout << arr[0] << std::endl;
std::cout << arr[1] << std::endl;
std::cout << arr[2] << std::endl;
std::cout << arr[3] << std::endl;
std::cout << arr[4] << std::endl;
std::cout << arr[5] << std::endl;
std::cout << arr[6] << std::endl;
std::cout << arr[7] << std::endl;
std::cout << arr[8] << std::endl;
std::cout << arr[9] << std::endl;
}
Simply, build on top of bitset, something like:
#include<bitset>
#include<iostream>
using namespace std;
template<int N>
class mydoublebitset
{
public:
uint_least8_t operator[](size_t index)
{
return 2 * b[index * 2 + 1] + b[index * 2 ];
}
void set(size_t index, uint_least8_t store)
{
switch (store)
{
case 3:
b[index * 2] = 1;
b[index * 2 + 1] = 1;
break;
case 2:
b[index * 2] = 0;
b[index * 2 + 1] = 1;
break;
case 1:
b[index * 2] = 0;
b[index * 2 + 1] = 1;
break;
case 0:
b[index * 2] = 0;
b[index * 2 + 1] = 0;
break;
default:
throw exception();
}
}
private:
bitset<N * 2> b;
};
int main()
{
mydoublebitset<12> mydata;
mydata.set(0, 0);
mydata.set(1, 2);
mydata.set(2, 2);
cout << (unsigned int)mydata[0] << (unsigned int)mydata[1] << (unsigned int)mydata[2] << endl;
system("pause");
return 0;
}
Basically use a bitset with twice the size and index it accordingly. its simpler and memory efficient as is required by you.

C/C++ check if one bit is set in, i.e. int variable

int temp = 0x5E; // in binary 0b1011110.
Is there such a way to check if bit 3 in temp is 1 or 0 without bit shifting and masking.
Just want to know if there is some built in function for this, or am I forced to write one myself.
In C, if you want to hide bit manipulation, you can write a macro:
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
and use it this way to check the nth bit from the right end:
CHECK_BIT(temp, n - 1)
In C++, you can use std::bitset.
Check if bit N (starting from 0) is set:
temp & (1 << N)
There is no builtin function for this.
I would just use a std::bitset if it's C++. Simple. Straight-forward. No chance for stupid errors.
typedef std::bitset<sizeof(int)> IntBits;
bool is_set = IntBits(value).test(position);
or how about this silliness
template<unsigned int Exp>
struct pow_2 {
static const unsigned int value = 2 * pow_2<Exp-1>::value;
};
template<>
struct pow_2<0> {
static const unsigned int value = 1;
};
template<unsigned int Pos>
bool is_bit_set(unsigned int value)
{
return (value & pow_2<Pos>::value) != 0;
}
bool result = is_bit_set<2>(value);
What the selected answer is doing is actually wrong. The below function will return the bit position or 0 depending on if the bit is actually enabled. This is not what the poster was asking for.
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
Here is what the poster was originally looking for. The below function will return either a 1 or 0 if the bit is enabled and not the position.
#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1)
Yeah, I know I don't "have" to do it this way. But I usually write:
/* Return type (8/16/32/64 int size) is specified by argument size. */
template<class TYPE> inline TYPE BIT(const TYPE & x)
{ return TYPE(1) << x; }
template<class TYPE> inline bool IsBitSet(const TYPE & x, const TYPE & y)
{ return 0 != (x & y); }
E.g.:
IsBitSet( foo, BIT(3) | BIT(6) ); // Checks if Bit 3 OR 6 is set.
Amongst other things, this approach:
Accommodates 8/16/32/64 bit integers.
Detects IsBitSet(int32,int64) calls without my knowledge & consent.
Inlined Template, so no function calling overhead.
const& references, so nothing needs to be duplicated/copied. And we are guaranteed that the compiler will pick up any typo's that attempt to change the arguments.
0!= makes the code more clear & obvious. The primary point to writing code is always to communicate clearly and efficiently with other programmers, including those of lesser skill.
While not applicable to this particular case... In general, templated functions avoid the issue of evaluating arguments multiple times. A known problem with some #define macros. E.g.: #define ABS(X) (((X)<0) ? - (X) : (X)) ABS(i++);
According to this description of bit-fields, there is a method for defining and accessing fields directly. The example in this entry goes:
struct preferences {
unsigned int likes_ice_cream : 1;
unsigned int plays_golf : 1;
unsigned int watches_tv : 1;
unsigned int reads_books : 1;
};
struct preferences fred;
fred.likes_ice_cream = 1;
fred.plays_golf = 1;
fred.watches_tv = 1;
fred.reads_books = 0;
if (fred.likes_ice_cream == 1)
/* ... */
Also, there is a warning there:
However, bit members in structs have practical drawbacks. First, the ordering of bits in memory is architecture dependent and memory padding rules varies from compiler to compiler. In addition, many popular compilers generate inefficient code for reading and writing bit members, and there are potentially severe thread safety issues relating to bit fields (especially on multiprocessor systems) due to the fact that most machines cannot manipulate arbitrary sets of bits in memory, but must instead load and store whole words.
You can use a Bitset - http://www.cppreference.com/wiki/stl/bitset/start.
Use std::bitset
#include <bitset>
#include <iostream>
int main()
{
int temp = 0x5E;
std::bitset<sizeof(int)*CHAR_BITS> bits(temp);
// 0 -> bit 1
// 2 -> bit 3
std::cout << bits[2] << std::endl;
}
i was trying to read a 32-bit integer which defined the flags for an object in PDFs and this wasn't working for me
what fixed it was changing the define:
#define CHECK_BIT(var,pos) ((var & (1 << pos)) == (1 << pos))
the operand & returns an integer with the flags that both have in 1, and it wasn't casting properly into boolean, this did the trick
I use this:
#define CHECK_BIT(var,pos) ( (((var) & (pos)) > 0 ) ? (1) : (0) )
where "pos" is defined as 2^n (i.g. 1,2,4,8,16,32 ...)
Returns:
1 if true
0 if false
There is, namely the _bittest intrinsic instruction.
#define CHECK_BIT(var,pos) ((var>>pos) & 1)
pos - Bit position strarting from 0.
returns 0 or 1.
For the low-level x86 specific solution use the x86 TEST opcode.
Your compiler should turn _bittest into this though...
The precedent answers show you how to handle bit checks, but more often then not, it is all about flags encoded in an integer, which is not well defined in any of the precedent cases.
In a typical scenario, flags are defined as integers themselves, with a bit to 1 for the specific bit it refers to. In the example hereafter, you can check if the integer has ANY flag from a list of flags (multiple error flags concatenated) or if EVERY flag is in the integer (multiple success flags concatenated).
Following an example of how to handle flags in an integer.
Live example available here:
https://rextester.com/XIKE82408
//g++ 7.4.0
#include <iostream>
#include <stdint.h>
inline bool any_flag_present(unsigned int value, unsigned int flags) {
return bool(value & flags);
}
inline bool all_flags_present(unsigned int value, unsigned int flags) {
return (value & flags) == flags;
}
enum: unsigned int {
ERROR_1 = 1U,
ERROR_2 = 2U, // or 0b10
ERROR_3 = 4U, // or 0b100
SUCCESS_1 = 8U,
SUCCESS_2 = 16U,
OTHER_FLAG = 32U,
};
int main(void)
{
unsigned int value = 0b101011; // ERROR_1, ERROR_2, SUCCESS_1, OTHER_FLAG
unsigned int all_error_flags = ERROR_1 | ERROR_2 | ERROR_3;
unsigned int all_success_flags = SUCCESS_1 | SUCCESS_2;
std::cout << "Was there at least one error: " << any_flag_present(value, all_error_flags) << std::endl;
std::cout << "Are all success flags enabled: " << all_flags_present(value, all_success_flags) << std::endl;
std::cout << "Is the other flag enabled with eror 1: " << all_flags_present(value, ERROR_1 | OTHER_FLAG) << std::endl;
return 0;
}
Why all these bit shifting operations and need for library functions? If you have the value the OP posted: 1011110 and you want to know if the bit in the 3rd position from the right is set, just do:
int temp = 0b1011110;
if( temp & 4 ) /* or (temp & 0b0100) if that's how you roll */
DoSomething();
Or something a bit prettier that may be more easily interpreted by future readers of the code:
#include <stdbool.h>
int temp = 0b1011110;
bool bThirdBitIsSet = (temp & 4) ? true : false;
if( bThirdBitIsSet )
DoSomething();
Or, with no #include needed:
int temp = 0b1011110;
_Bool bThirdBitIsSet = (temp & 4) ? 1 : 0;
if( bThirdBitIsSet )
DoSomething();
You could "simulate" shifting and masking: if((0x5e/(2*2*2))%2) ...
One approach will be checking within the following condition:
if ( (mask >> bit ) & 1)
An explanation program will be:
#include <stdio.h>
unsigned int bitCheck(unsigned int mask, int pin);
int main(void){
unsigned int mask = 6; // 6 = 0110
int pin0 = 0;
int pin1 = 1;
int pin2 = 2;
int pin3 = 3;
unsigned int bit0= bitCheck( mask, pin0);
unsigned int bit1= bitCheck( mask, pin1);
unsigned int bit2= bitCheck( mask, pin2);
unsigned int bit3= bitCheck( mask, pin3);
printf("Mask = %d ==>> 0110\n", mask);
if ( bit0 == 1 ){
printf("Pin %d is Set\n", pin0);
}else{
printf("Pin %d is not Set\n", pin0);
}
if ( bit1 == 1 ){
printf("Pin %d is Set\n", pin1);
}else{
printf("Pin %d is not Set\n", pin1);
}
if ( bit2 == 1 ){
printf("Pin %d is Set\n", pin2);
}else{
printf("Pin %d is not Set\n", pin2);
}
if ( bit3 == 1 ){
printf("Pin %d is Set\n", pin3);
}else{
printf("Pin %d is not Set\n", pin3);
}
}
unsigned int bitCheck(unsigned int mask, int bit){
if ( (mask >> bit ) & 1){
return 1;
}else{
return 0;
}
}
Output:
Mask = 6 ==>> 0110
Pin 0 is not Set
Pin 1 is Set
Pin 2 is Set
Pin 3 is not Set
if you just want a real hard coded way:
#define IS_BIT3_SET(var) ( ((var) & 0x04) == 0x04 )
note this hw dependent and assumes this bit order 7654 3210 and var is 8 bit.
#include "stdafx.h"
#define IS_BIT3_SET(var) ( ((var) & 0x04) == 0x04 )
int _tmain(int argc, _TCHAR* argv[])
{
int temp =0x5E;
printf(" %d \n", IS_BIT3_SET(temp));
temp = 0x00;
printf(" %d \n", IS_BIT3_SET(temp));
temp = 0x04;
printf(" %d \n", IS_BIT3_SET(temp));
temp = 0xfb;
printf(" %d \n", IS_BIT3_SET(temp));
scanf("waitng %d",&temp);
return 0;
}
Results in:
1
0
1
0
While it is quite late to answer now, there is a simple way one could find if Nth bit is set or not, simply using POWER and MODULUS mathematical operators.
Let us say we want to know if 'temp' has Nth bit set or not. The following boolean expression will give true if bit is set, 0 otherwise.
( temp MODULUS 2^N+1 >= 2^N )
Consider the following example:
int temp = 0x5E; // in binary 0b1011110 // BIT 0 is LSB
If I want to know if 3rd bit is set or not, I get
(94 MODULUS 16) = 14 > 2^3
So expression returns true, indicating 3rd bit is set.
Why not use something as simple as this?
uint8_t status = 255;
cout << "binary: ";
for (int i=((sizeof(status)*8)-1); i>-1; i--)
{
if ((status & (1 << i)))
{
cout << "1";
}
else
{
cout << "0";
}
}
OUTPUT: binary: 11111111
I make this:
LATGbits.LATG0=((m&0x8)>0); //to check if bit-2 of m is 1
the fastest way seems to be a lookup table for masks

Binary literals?

In code, I sometimes see people specify constants in hex format like this:
const int has_nukes = 0x0001;
const int has_bio_weapons = 0x0002;
const int has_chem_weapons = 0x0004;
// ...
int arsenal = has_nukes | has_bio_weapons | has_chem_weapons; // all of them
if(arsenal &= has_bio_weapons){
std::cout << "BIO!!"
}
But it doesn't make sense to me to use the hex format here. Is there a way to do it directly in binary? Something like this:
const int has_nukes = 0b00000000000000000000000000000001;
const int has_bio_weapons = 0b00000000000000000000000000000010;
const int has_chem_weapons = 0b00000000000000000000000000000100;
// ...
I know the C/C++ compilers won't compile this, but there must be a workaround? Is it possible in other languages like Java?
In C++14 you will be able to use binary literals with the following syntax:
0b010101010 /* more zeros and ones */
This feature is already implemented in the latest clang and gcc. You can try it if you run those compilers with -std=c++1y option.
I'd use a bit shift operator:
const int has_nukes = 1<<0;
const int has_bio_weapons = 1<<1;
const int has_chem_weapons = 1<<2;
// ...
int dangerous_mask = has_nukes | has_bio_weapons | has_chem_weapons;
bool is_dangerous = (country->flags & dangerous_mask) == dangerous_mask;
It is even better than flood of 0's.
By the way, the next C++ version will support user defined literals. They are already included into the working draft. This allows that sort of stuff (let's hope i don't have too many errors in it):
template<char... digits>
constexpr int operator "" _b() {
return conv2bin<digits...>::value;
}
int main() {
int const v = 110110110_b;
}
conv2bin would be a template like this:
template<char... digits>
struct conv2bin;
template<char high, char... digits>
struct conv2bin<high, digits...> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0') * (1 << sizeof...(digits)) +
conv2bin<digits...>::value;
};
template<char high>
struct conv2bin<high> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0');
};
Well, what we get are binary literals that evaluate fully at compile time already, because of the "constexpr" above. The above uses a hard-coded int return type. I think one could even make it depend on the length of the binary string. It's using the following features, for anyone interested:
Generalized Constant Expressions.
Variadic Templates. A brief introduction can be found here
Static Assertions (static_assert)
User defined Literals
Actually, current GCC trunk already implements variadic templates and static assertions. Let's hope it will support the other two soon. I think C++1x will rock the house.
The C++ Standard Library is your friend:
#include <bitset>
const std::bitset <32> has_nukes( "00000000000000000000000000000001" );
GCC supports binary constants as an extension since 4.3. See the announcement (look at the section "New Languages and Language specific improvements").
You can use << if you like.
int hasNukes = 1;
int hasBioWeapons = 1 << 1;
int hasChemWeapons = 1 << 2;
This discussion may be interesting... Might have been, as the link is dead unfortunately. It described a template based approach similar to other answers here.
And also there is a thing called BOOST_BINARY.
The term you want is binary literals
Ruby has them with the syntax you give.
One alternative is to define helper macros to convert for you. I found the following code at http://bytes.com/groups/c/219656-literal-binary
/* Binary constant generator macro
* By Tom Torfs - donated to the public domain
*/
/* All macro's evaluate to compile-time constants */
/* *** helper macros *** */
/* turn a numeric literal into a hex constant
* (avoids problems with leading zeroes)
* 8-bit constants max value 0x11111111, always fits in unsigned long
*/
#define HEX_(n) 0x##n##LU
/* 8-bit conversion function */
#define B8_(x) ((x & 0x0000000FLU) ? 1:0) \
| ((x & 0x000000F0LU) ? 2:0) \
| ((x & 0x00000F00LU) ? 4:0) \
| ((x & 0x0000F000LU) ? 8:0) \
| ((x & 0x000F0000LU) ? 16:0) \
| ((x & 0x00F00000LU) ? 32:0) \
| ((x & 0x0F000000LU) ? 64:0) \
| ((x & 0xF0000000LU) ? 128:0)
/* *** user macros *** /
/* for upto 8-bit binary constants */
#define B8(d) ((unsigned char) B8_(HEX_(d)))
/* for upto 16-bit binary constants, MSB first */
#define B16(dmsb, dlsb) (((unsigned short) B8(dmsb) << 8) \
| B8(dlsb))
/* for upto 32-bit binary constants, MSB first */
#define B32(dmsb, db2, db3, dlsb) (((unsigned long) B8(dmsb) << 24) \
| ((unsigned long) B8( db2) << 16) \
| ((unsigned long) B8( db3) << 8) \
| B8(dlsb))
/* Sample usage:
* B8(01010101) = 85
* B16(10101010,01010101) = 43605
* B32(10000000,11111111,10101010,01010101) = 2164238933
*/
The next version of C++, C++0x, will introduce user defined literals. I'm not sure if binary numbers will be part of the standard but at the worst you'll be able to enable it yourself:
int operator "" _B(int i);
assert( 1010_B == 10);
I write binary literals like this:
const int has_nukes = 0x0001;
const int has_bio_weapons = 0x0002;
const int has_chem_weapons = 0x0004;
It's more compact than your suggested notation, and easier to read. For example:
const int upper_bit = 0b0001000000000000000;
versus:
const int upper_bit = 0x04000;
Did you notice that the binary version wasn't an even multiple of 4 bits? Did you think it was 0x10000?
With a little practice hex or octal are easier for a human than binary. And, in my opinion, easier to read that using shift operators. But I'll concede that my years of assembly language work may bias me on that point.
If you want to use bitset, auto, variadic templates, user-defined literals, static_assert, constexpr, and noexcept try this:
template<char... Bits>
struct __checkbits
{
static const bool valid = false;
};
template<char High, char... Bits>
struct __checkbits<High, Bits...>
{
static const bool valid = (High == '0' || High == '1')
&& __checkbits<Bits...>::valid;
};
template<char High>
struct __checkbits<High>
{
static const bool valid = (High == '0' || High == '1');
};
template<char... Bits>
inline constexpr std::bitset<sizeof...(Bits)>
operator"" bits() noexcept
{
static_assert(__checkbits<Bits...>::valid, "invalid digit in binary string");
return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
}
Use it like this:
int
main()
{
auto bits = 0101010101010101010101010101010101010101010101010101010101010101bits;
std::cout << bits << std::endl;
std::cout << "size = " << bits.size() << std::endl;
std::cout << "count = " << bits.count() << std::endl;
std::cout << "value = " << bits.to_ullong() << std::endl;
// This triggers the static_assert at compile-time.
auto badbits = 2101010101010101010101010101010101010101010101010101010101010101bits;
// This throws at run-time.
std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101bits");
}
Thanks to #johannes-schaub-litb
Java doesn't support binary literals either, unfortunately. However, it has enums which can be used with an EnumSet. An EnumSet represents enum values internally with bit fields, and presents a Set interface for manipulating these flags.
Alternatively, you could use bit offsets (in decimal) when defining your values:
const int HAS_NUKES = 0x1 << 0;
const int HAS_BIO_WEAPONS = 0x1 << 1;
const int HAS_CHEM_WEAPONS = 0x1 << 2;
There's no syntax for literal binary constants in C++ the way there is for hexadecimal and octal. The closest thing for what it looks like you're trying to do would probably be to learn and use bitset.
As an aside:
Especially if you're dealing with a large set, instead of going through the [minor] mental effort of writing a sequence of shift amounts, you can make each constant depend on the previously defined constant:
const int has_nukes = 1;
const int has_bio_weapons = has_nukes << 1;
const int has_chem_weapons = has_bio_weapons << 1;
const int has_nunchuks = has_chem_weapons << 1;
// ...
Looks a bit redundant, but it's less typo-prone. Also, you can simply insert a new constant in the middle without having to touch any other line except the one immediately following it:
const int has_nukes = 1;
const int has_gravity_gun = has_nukes << 1; // added
const int has_bio_weapons = has_gravity_gun << 1; // changed
const int has_chem_weapons = has_bio_weapons << 1; // unaffected from here on
const int has_nunchuks = has_chem_weapons << 1;
// ...
Compare to:
const int has_nukes = 1 << 0;
const int has_bio_weapons = 1 << 1;
const int has_chem_weapons = 1 << 2;
const int has_nunchuks = 1 << 3;
// ...
const int has_scimatar = 1 << 28;
const int has_rapier = 1 << 28; // good luck spotting this typo!
const int has_katana = 1 << 30;
And:
const int has_nukes = 1 << 0;
const int has_gravity_gun = 1 << 1; // added
const int has_bio_weapons = 1 << 2; // changed
const int has_chem_weapons = 1 << 3; // changed
const int has_nunchuks = 1 << 4; // changed
// ... // changed all the way
const int has_scimatar = 1 << 29; // changed *sigh*
const int has_rapier = 1 << 30; // changed *sigh*
const int has_katana = 1 << 31; // changed *sigh*
As an aside to my aside, it's probably equally hard to spot a typo like this:
const int has_nukes = 1;
const int has_gravity_gun = has_nukes << 1;
const int has_bio_weapons = has_gravity_gun << 1;
const int has_chem_weapons = has_gravity_gun << 1; // oops!
const int has_nunchuks = has_chem_weapons << 1;
So, I think the main advantage of this cascading syntax is when dealing with insertions and deletions of constants.
Another method:
template<unsigned int N>
class b
{
public:
static unsigned int const x = N;
typedef b_<0> _0000;
typedef b_<1> _0001;
typedef b_<2> _0010;
typedef b_<3> _0011;
typedef b_<4> _0100;
typedef b_<5> _0101;
typedef b_<6> _0110;
typedef b_<7> _0111;
typedef b_<8> _1000;
typedef b_<9> _1001;
typedef b_<10> _1010;
typedef b_<11> _1011;
typedef b_<12> _1100;
typedef b_<13> _1101;
typedef b_<14> _1110;
typedef b_<15> _1111;
private:
template<unsigned int N2>
struct b_: public b<N << 4 | N2> {};
};
typedef b<0> _0000;
typedef b<1> _0001;
typedef b<2> _0010;
typedef b<3> _0011;
typedef b<4> _0100;
typedef b<5> _0101;
typedef b<6> _0110;
typedef b<7> _0111;
typedef b<8> _1000;
typedef b<9> _1001;
typedef b<10> _1010;
typedef b<11> _1011;
typedef b<12> _1100;
typedef b<13> _1101;
typedef b<14> _1110;
typedef b<15> _1111;
Usage:
std::cout << _1101::_1001::_1101::_1101::x;
Implemented in CityLizard++ (citylizard/binary/b.hpp).
I agree that it's useful to have an option for binary literals, and they are present in many programming languages. In C, I've decided to use a macro like this:
#define bitseq(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
(a31|a30<< 1|a29<< 2|a28<< 3|a27<< 4|a26<< 5|a25<< 6|a24<< 7| \
a23<< 8|a22<< 9|a21<<10|a20<<11|a19<<12|a18<<13|a17<<14|a16<<15| \
a15<<16|a14<<17|a13<<18|a12<<19|a11<<20|a10<<21|a09<<22|a08<<23| \
a07<<24|a06<<25|a05<<26|a04<<27|a03<<28|a02<<29|a01<<30|(unsigned)a00<<31)
The usage is pretty much straightforward =)
One, slightly horrible way you could do it is by generating a .h file with lots of #defines...
#define b00000000 0
#define b00000001 1
#define b00000010 2
#define b00000011 3
#define b00000100 4
etc.
This might make sense for 8-bit numbers, but probably not for 16-bit or larger.
Alternatively, do this (similar to Zach Scrivena's answer):
#define bit(x) (1<<x)
int HAS_NUKES = bit(HAS_NUKES_OFFSET);
int HAS_BIO_WEAPONS = bit(HAS_BIO_WEAPONS_OFFSET);
Binary literals are part of the C++ language since C++14. It’s literals that start with 0b or 0B. Reference
Maybe less relevant to binary literals, but this just looks as if it can be solved better with a bit field.
struct DangerCollection : uint32_t {
bool has_nukes : 1;
bool has_bio_weapons : 1;
bool has_chem_weapons : 1;
// .....
};
DangerCollection arsenal{
.has_nukes = true,
.has_bio_weapons = true,
.has_chem_weapons = true,
// ...
};
if(arsenal.has_bio_weapons){
std::cout << "BIO!!"
}
You would still be able to fill it with binary data, since its binary footprint is just a uint32. This is often used in combination with a union, for compact binary serialisation:
union DangerCollectionUnion {
DangerCollection collection;
uint8_t data[sizeof(DangerCollection)];
};
DangerCollectionUnion dc;
std::memcpy(dc.data, bitsIGotFromSomewhere, sizeof(DangerCollection));
if (dc.collection.has_bio_weapons) {
// ....
In my experience less error prone and easy to understand what's going on.