Skip to content

Commit

Permalink
👷 Smarter Flags<N> templates (#27309)
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkyhead authored Jul 25, 2024
1 parent bcbdac6 commit 2d32f7c
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Marlin/src/core/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@

// Macros for bit masks
#undef _BV
#define _BV(n) (1<<(n))
#define _BV(b) (1 << (b))
#define TEST(n,b) (!!((n)&_BV(b)))
#define SET_BIT_TO(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0)
#ifndef SBI
Expand Down
88 changes: 71 additions & 17 deletions Marlin/src/core/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,36 +159,90 @@ template <class L, class R> struct IF<true, L, R> { typedef L type; };
#define FI FORCE_INLINE

// Define types based on largest bit width stored value required
#define bits_t(W) typename IF<((W)> 16), uint32_t, typename IF<((W)> 8), uint16_t, uint8_t>::type>::type
#define bits_t(W) typename IF<((W)> 32), uint64_t, typename IF<((W)> 16), uint32_t, typename IF<((W)>8), uint16_t, uint8_t>::type>::type>::type
#define uvalue_t(V) typename IF<((V)>65535), uint32_t, typename IF<((V)>255), uint16_t, uint8_t>::type>::type
#define value_t(V) typename IF<((V)>32767), int32_t, typename IF<((V)>127), int16_t, int8_t>::type>::type

// General Flags for some number of states
// Define a template for a bit field of N bits, using the smallest type that can hold N bits
template<size_t N, bool UseArray = (N > 64)>
struct Flags;

// Flag bits for <= 64 states
template<size_t N>
struct Flags {
struct Flags<N, false> {
typedef bits_t(N) flagbits_t;
typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } N8;
typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1, b9:1, b10:1, b11:1, b12:1, b13:1, b14:1, b15:1; } N16;
typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1, b9:1, b10:1, b11:1, b12:1, b13:1, b14:1, b15:1,
b16:1, b17:1, b18:1, b19:1, b20:1, b21:1, b22:1, b23:1, b24:1, b25:1, b26:1, b27:1, b28:1, b29:1, b30:1, b31:1; } N32;
union {
flagbits_t b;
typename IF<(N>16), N32, typename IF<(N>8), N16, N8>::type>::type flag;
flagbits_t b;

class BitProxy {
public:
BitProxy(flagbits_t& data, int bit) : data_(data), bit_(bit) {}

BitProxy& operator=(const bool value) {
if (value)
data_ |= (flagbits_t(1) << bit_);
else
data_ &= ~(flagbits_t(1) << bit_);
return *this;
}

operator bool() const { return bool(data_ & (flagbits_t(1) << bit_)); }

private:
flagbits_t& data_;
uint8_t bit_;
};

FI void reset() { b = 0; }
FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); }
FI void set(const int n) { b |= (flagbits_t)_BV(n); }
FI void clear(const int n) { b &= ~(flagbits_t)_BV(n); }
FI bool test(const int n) const { return TEST(b, n); }
FI bool operator[](const int n) { return test(n); }
FI void set(const int n) { b |= (flagbits_t(1) << n); }
FI void clear(const int n) { b &= ~(flagbits_t(1) << n); }
FI bool test(const int n) const { return bool(b & (flagbits_t(1) << n)); }
FI BitProxy operator[](const int n) { return BitProxy(b, n); }
FI bool operator[](const int n) const { return test(n); }
FI int size() const { return sizeof(b); }
FI operator bool() const { return b; }
FI operator bool() const { return b != 0; }
};

// Flag bits for more than 64 states
template<size_t N>
struct Flags<N, true> {
uint8_t bitmask[(N+7)>>3];
// Proxy class for handling bit assignment
class BitProxy {
public:
BitProxy(uint8_t data[], int n) : data_(data[n >> 3]), bit_(n & 7) {}

// Assignment operator
BitProxy& operator=(const bool value) {
if (value)
data_ |= _BV(bit_);
else
data_ &= ~_BV(bit_);
return *this;
}

// Conversion operator to bool
operator bool() const { return TEST(data_, bit_); }

private:
uint8_t& data_;
uint8_t bit_;
};

FI void reset() { for (uint8_t b = 0; b < sizeof(bitmask); ++b) bitmask[b] = 0; }
FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); }
FI void set(const int n) { bitmask[n >> 3] |= _BV(n & 7); }
FI void clear(const int n) { bitmask[n >> 3] &= ~_BV(n & 7); }
FI bool test(const int n) const { return TEST(bitmask[n >> 3], n & 7); }
FI BitProxy operator[](const int n) { return BitProxy(bitmask, n); }
FI bool operator[](const int n) const { return test(n); }
FI int size() const { return sizeof(bitmask); }
FI operator bool() const { for (uint8_t b : bitmask) if (b) return true; return false; }
};

// Specialization for a single bool flag
template<>
struct Flags<1> {
struct Flags<1, false> {
bool b;
FI void reset() { b = false; }
FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); }
Expand Down Expand Up @@ -218,7 +272,7 @@ typedef struct {
FI bool operator[](const int n) { return flags[n]; }
FI bool operator[](const int n) const { return flags[n]; }
FI int size() const { return sizeof(flags); }
FI operator bool() const { return flags; }
FI operator bool() const { return (bool)flags; }
} AxisFlags;

//
Expand Down
53 changes: 53 additions & 0 deletions Marlin/tests/core/test_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,59 @@ MARLIN_TEST(types, Flags_32) {
TEST_ASSERT_EQUAL(4, flags.size());
}

MARLIN_TEST(types, Flags_64) {
Flags<64> flags;

flags.reset();
TEST_ASSERT_EQUAL(0, flags.b);

flags.set(0, true);
flags.set(63, true);
TEST_ASSERT_EQUAL(9223372036854775809ULL, flags.b);

flags.clear(0);
flags.clear(63);
TEST_ASSERT_EQUAL(0, flags.b);

flags.set(0, true);
flags.set(63, true);
TEST_ASSERT_EQUAL(true, flags.test(0));
TEST_ASSERT_EQUAL(true, flags.test(63));
TEST_ASSERT_EQUAL(false, flags.test(1));
TEST_ASSERT_EQUAL(false, flags.test(62));

TEST_ASSERT_EQUAL(true, flags[0]);
TEST_ASSERT_EQUAL(true, flags[63]);
TEST_ASSERT_EQUAL(false, flags[1]);
TEST_ASSERT_EQUAL(false, flags[62]);

TEST_ASSERT_EQUAL(8, flags.size());
}

MARLIN_TEST(types, Flags_302) {
Flags<302> flags;

flags.reset();
TEST_ASSERT_EQUAL(false, (bool)flags);

flags.set(0, true);
flags.set(301, true);

TEST_ASSERT_EQUAL(true, (bool)flags);

TEST_ASSERT_EQUAL(true, flags.test(0));
TEST_ASSERT_EQUAL(true, flags.test(301));
TEST_ASSERT_EQUAL(false, flags.test(1));
TEST_ASSERT_EQUAL(false, flags.test(300));

TEST_ASSERT_EQUAL(true, flags[0]);
TEST_ASSERT_EQUAL(true, flags[301]);
TEST_ASSERT_EQUAL(false, flags[1]);
TEST_ASSERT_EQUAL(false, flags[300]);

TEST_ASSERT_EQUAL(38, flags.size());
}

MARLIN_TEST(types, AxisFlags_const_as_bools) {
const AxisFlags axis_flags_const_false = {0};
TEST_ASSERT_FALSE(axis_flags_const_false);
Expand Down

0 comments on commit 2d32f7c

Please sign in to comment.