2
\$\begingroup\$

The purpose for this class is to pack two 4 bit unsigned integer into 8 bit unsigned integer, e.g. uint8_t.

It is made in a way, so the class to resemble array of two contiguous elements.

Once this work correctly, one may program continuous array of 4 bit integers and to use it for example for counting bloom filter.

A "special" proxy class is used in a way similar to std::vector, so you can do:

 UINT4 x;
 // set parts
 x[0] = 1; // set first part, OK
 x[1] = 20; // set second part, OK
 // convert to uint8_t
 uint8_t i = x[0]; // OK
 // arithmetic mostly works.
 ++x[0]; // OK
 x[0] += 10; // OK
 --x[1]; // OK
 x[1] -= 10; // OK
 // not implemented yet, mostly because I don't need it
 // x[1] = x[0] + 10;

#include <cstdint>
//#include <type_traits>
//#include <cstdio>
class UINT4{
 uint8_t _0 : 4;
 uint8_t _1 : 4;
 class Proxy;
public:
 constexpr explicit UINT4() : UINT4{ 0, 0 }{
 }
 constexpr UINT4(uint8_t _0, uint8_t _1) : _0{ _0 }, _1{ _1 }{
 }
 constexpr
 Proxy operator[](std::size_t i);
 constexpr uint8_t operator[](std::size_t i) const{
 if (i == 0)
 return _0;
 else
 return _1;
 }
};
static_assert(sizeof(UINT4) == 1);
class UINT4::Proxy{
 UINT4 &d;
 uint8_t i;
public:
 // c-tor
 constexpr Proxy(UINT4 &d, std::size_t i) :
 d(d ),
 i(i == 0 ? 0 : 1 ){
 }
 // convert
 constexpr operator uint8_t() const{
 // we can use UINT4::operator[](size_t) const
 if (i == 0)
 return d._0;
 else
 return d._1;
 }
 // compare
 constexpr bool operator==(uint8_t x) const{
 if (i == 0)
 return d._0 == x;
 else
 return d._1 == x;
 }
 constexpr bool operator!=(uint8_t x) const{
 return !( *this == x );
 }
 constexpr bool operator<(uint8_t x) const{
 if (i == 0)
 return d._0 < x;
 else
 return d._1 < x;
 }
 constexpr bool operator<=(uint8_t x) const{
 if (i == 0)
 return d._0 <= x;
 else
 return d._1 <= x;
 }
 constexpr bool operator>(uint8_t x) const{
 return !( *this <= x );
 }
 constexpr bool operator>=(uint8_t x) const{
 return !( *this < x );
 }
 // assign
 constexpr
 auto &operator=(uint8_t x){
 if (i == 0)
 d._0 = x;
 else
 d._1 = x;
 return *this;
 }
 // +++
 constexpr
 auto &operator+=(uint8_t x){
 if (i == 0)
 d._0 += x;
 else
 d._1 += x;
 return *this;
 }
 constexpr
 auto &operator++(){
 return *this += uint8_t{ 1 };
 }
 // can not be done easily, because there are references
 // auto operator++(int);
 // ---
 constexpr
 auto &operator-=(uint8_t x){
 return *this += - x;
 }
 constexpr
 auto &operator--(){
 return *this -= uint8_t{ 1 };
 }
 // can not be done easily, because there are references
 // auto operator--(int);
 // todo add multiplication and division
};
constexpr auto UINT4::operator[](std::size_t i) -> Proxy{
 return Proxy{ *this, i };
}
#include <cstdio>
int main(){
 UINT4 x{ 15, 0 };
 x[1] = 2;
 uint8_t a = x[0];
 printf("%u\n", a);
 printf("%u %u\n", uint16_t{ x[0] }, uint16_t{ x[1] } );
 x[0] -= 10;
 x[1] += 10;
 printf("%u %u\n", uint16_t{ x[0] }, uint16_t{ x[1] } );
 ++x[0];
 --x[1];
 printf("%u %u\n", uint16_t{ x[0] }, uint16_t{ x[1] } );
}
asked Mar 28, 2023 at 12:31
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Make it work like std::vector<bool>

Your class shares similarities with std::vector<bool>. I recommend that you make it work more like that. In particular, instead of just providing two 4-bit integers, make a class that acts like a vector of them. You already mentioned that the use case is for contiguous arrays, but if you have to write:

std::vector<UINT4> values;

Then it actually becomes very awkward to access the n-th 4-bit integer, because you'd have to write something like:

std::size_t n = ...;
auto value = values[n / 2][n & 1];

Instead it would be much nicer to be able to write:

vector_uint4 values;
...
std::size_t n = ...
auto value = values[n];

std::vector<bool>::reference uses a proxy class just like your class. It is a bit simpler since it only needs to deal with operations that make sense for booleans. But note that it not only allows bool arguments, it also has overloads that take other std::vector<bool>::references as parameters. This allows you to, say, assign the value of one reference to another. You might want to allow something similar in your class, so you can add one 4-bit value of the vector to another one. Of course, if you are sure you are not going to need it, I would not implement that, but if you wanted to make a generic library that can be used for other purposes than your bloom filter, this is something you might want to think about.

answered Mar 28, 2023 at 14:37
\$\endgroup\$
1
  • \$\begingroup\$ Yes, I already have class similar to vector. I like this one values[n / 2][n & 1] \$\endgroup\$ Commented Mar 28, 2023 at 15:03

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.