-4
\$\begingroup\$

This is a class that acts as an unsigned integer with a variable amount of bits.

#include <iostream>
#include <cstdint>

// any amount of bits inteter
// note: stores with size of uint64_t
template <uint8_t bits_g>
class uintx_t {
private:
    uint64_t Val;
    uint8_t bits;

    constexpr inline uint64_t _formatBits(
        const uint64_t& val,
        const uint16_t& _bits
    ) {return val & UINT64_MAX >> (64-_bits);}

public:
    uintx_t(const int64_t& val) {
        bits = bits_g;
        if      (bits > 64) bits = 64;
        else if (bits < 1 ) bits = 1;
        Val = _formatBits(val, bits);
    }
    
    void operator= (uintx_t& val) {
        Val = _formatBits(val.Val, bits);
    }
    void operator= (const int64_t& val) {
        Val = _formatBits(val, bits);
    }

    bool operator== (const int64_t& val) {
        return (Val == val);
    }
    bool operator!= (const int64_t& val) {
        return (Val != val);
    }
    bool operator> (const int64_t& val) {
        return (Val > val);
    }
    bool operator< (const int64_t& val) {
        return (Val < val);
    }
    bool operator>= (const int64_t& val) {
        return (Val >= val);
    }
    bool operator<= (const int64_t& val) {
        return (Val <= val);
    }

    int64_t operator+ (const int64_t& val) {
        return (_formatBits(Val + val, bits));
    }
    int64_t operator- (const int64_t& val) {
        return (_formatBits(Val - val, bits));
    }
    int64_t operator* (const int64_t& val) {
        return (_formatBits(Val * val, bits));
    }
    int64_t operator/ (const int64_t& val) {
        return (_formatBits(Val / val, bits));
    }
    int64_t operator% (const int64_t& val) {
        return (_formatBits(Val % val, bits));
    }
    int64_t operator| (const int64_t& val) {
        return (_formatBits(Val | val, bits));
    }
    int64_t operator& (const int64_t& val) {
        return (_formatBits(Val & val, bits));
    }
    int64_t operator^ (const int64_t& val) {
        return (_formatBits(Val ^ val, bits));
    }
    int64_t operator<< (const int64_t& val) {
        return (_formatBits(Val << val, bits));
    }
    int64_t operator>> (const int64_t& val) {
        return (_formatBits(Val >> val, bits));
    }

    uintx_t& operator++ () {
        Val = _formatBits(++Val, bits);
        return *this;
    }
    uintx_t& operator-- () {
        Val = _formatBits(--Val, bits);
        return *this;
    }
    uintx_t operator++ (int) {
        Val = _formatBits(++Val, bits);
        return *this;
    }
    uintx_t operator-- (int) {
        Val = _formatBits(--Val, bits);
        return *this;
    }

    void operator+= (const int64_t& val) {
        Val = _formatBits(Val+val, bits);
    }
    void operator-= (const int64_t& val) {
        Val = _formatBits(Val-val, bits);
    }
    void operator*= (const int64_t& val) {
        Val = _formatBits(Val*val, bits);
    }
    void operator/= (const int64_t& val) {
        Val = _formatBits(Val/val, bits);
    }
    void operator%= (const int64_t& val) {
        Val = _formatBits(Val%val, bits);
    }
    void operator|= (const int64_t& val) {
        Val = _formatBits(Val|val, bits);
    }
    void operator&= (const int64_t& val) {
        Val = _formatBits(Val&val, bits);
    }
    void operator^= (const int64_t& val) {
        Val = _formatBits(Val^val, bits);
    }
    void operator<<= (const int64_t& val) {
        Val = _formatBits(Val<<val, bits);
    }
    void operator>>= (const int64_t& val) {
        Val = _formatBits(Val>>val, bits);
    }

    friend std::ostream& operator<< (
        std::ostream& os,
        uintx_t& UX
    ) {
        os << std::to_string(UX.Val);
        return os;
    }

    template <typename T>
    explicit operator T() const {
        return (T)Val;
    }
};

The issue I have with this right now is that overloading an operator with another uintx_t doesn't work without having it casted to another type. I tried to overload the operators specifically for uintx_t but since 2 objects could have differing amount of bits, I couldn't figure out how to get it to work.

\$\endgroup\$
1

1 Answer 1

1
\$\begingroup\$

Code Style

Your coding style changes throughout your code, you should try to stick to one. Also, you should never name things starting with an _, this is reserved for the standard library and compilers.

Some rules I like (based on the standard library) are for example:

  • name your templates in PascalCase,
  • name your members in snake_case.

Bits between 1 and 64

I feel like it is misleading to allow the user to use bit values less than 1 or bigger than 64, and silently put it in that range. I would recommend doing the following instead:

template <std::uint8_t bits>
requires(bits >= 1 && bits <= 64)
struct uintx_t {

This way, if the user tries to have a number of bits higher than 64 or less than 1, the code will not compile.

bits member

Why do you have a member uint8_t bits? If the only reason is the one above, then you can remove it. If you absolutely want to keep it you should mark it static constexpr, this will avoid having a copy on all your objects. Also, thanks to memory alignment, having the uint8_t makes the struct 128 bits on my computer, so it doubles the size of the struct.

const uint64_t& val

You should not pass integers by const &, they are small enough that a copy does not matter (I mean that it is the same to copy a 64 bits pointer and a 64 bits integer), it removes an indirection, and it makes the code cleaner.

Format bits

For someone just reading the code, it is unclear what the priorities are in the format bit function, so I would recommend adding parenthesis.

You can also add the static qualifier. I would argue you can even move the function out of the class, this would make it more testable.

With all the above comments taken into account as well, this gives:

constexpr static inline uint64_t formatBits(uint64_t val, uint16_t bits) {
    return val & (UINT64_MAX >> (64 - bits));
}

Operators

Not really feedback here, but more a question. Why do the operators return an int64_t and not a uintx_t ?

Regarding your issue with overloading your operators for uintx_t, you can just template your operators: template <uint8_t OtherBits>. Should you require more assistance, I would recommend heading over to StackOverflow.

\$\endgroup\$
1
  • \$\begingroup\$ We generally don't answer questions where the code isn't working as intended. \$\endgroup\$
    – pacmaninbw
    Commented Mar 28, 2023 at 1:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.