Skip to content

Instantly share code, notes, and snippets.

@salkinium
Last active August 29, 2015 14:10
Show Gist options
  • Save salkinium/766dbcea1a700a83f1d8 to your computer and use it in GitHub Desktop.
Save salkinium/766dbcea1a700a83f1d8 to your computer and use it in GitHub Desktop.
Concept for typesafe bit operations for register mappings
// this is part of a xpcc unittest:
// https://github.com/roboterclubaachen/xpcc/blob/develop/src/xpcc/architecture/peripheral/test/register_test.hpp#L20
// more operations can be seen here:
// https://github.com/roboterclubaachen/xpcc/blob/develop/src/xpcc/architecture/peripheral/test/register_test.cpp#L16
// complete macro definitions are available here:
// https://github.com/roboterclubaachen/xpcc/blob/develop/src/xpcc/architecture/peripheral/register.hpp#L55
// see the type-safe registers in action here:
// https://github.com/roboterclubaachen/xpcc/blob/develop/src/xpcc/driver/inertial/lis302.hpp#L61
// These are the register bit mappings
enum class
Test
{
A = 0x01,
B = 0x02,
C = 0x04,
D = 0x08,
E = 0x10,
F = 0x20,
G = 0x40,
H = 0x80,
Mask = 0xff,
};
REGISTER8(Test);
// these are bit mappings for a different register
enum class
Test2
{
a = 0x01,
b = 0x02,
c = 0x04,
d = 0x08,
e = 0x10,
f = 0x20,
g = 0x40,
h = 0x80,
Mask = 0xff,
};
REGISTER8(Test2);
REGISTER8_GROUP(Common, Test, Test2);
// all typesafe
Test_t vD = Test::A | Test::B;
vD = vD | Test::C;
vD = Test::D | vD;
Test_t vI = Test::E;
vD = vD | vI;
// but still simple access of raw value without casting
uint8_t raw = vD.value;
// but Test2 and Test are not compatible!!
Test2_t e = Test::A | Test2::a; // NOT POSSIBLE
e = e | Test::A; // NOT possible either
e = e | vD; // NOT possible either
// possible
Common_t common = Test::A;
common = Test2::a;
// These are the register bit mappings
enum class
Test : uint8_t
{
A = 0x01,
B = 0x02,
C = 0x04,
D = 0x08,
E = 0x10,
F = 0x20,
G = 0x40,
H = 0x80,
Mask = 0xff,
};
// this is the register "type"
struct
Test_t : public ::xpcc::Register8
{
constexpr Test_t() {}
// explicit conversion constructor, ie. for static_cast<Type_t>(0x12)
explicit constexpr Test_t(uint8_t value)
: Register8(value) {}
// convert from enum class to type
constexpr Test_t(Test value)
: Register8(static_cast<uint8_t>(value)) {}
// Test | Test_t ~> Test_t
friend constexpr Test_t operator|(Test const &lhs, Test_t const &rhs)
{
return Test_t(static_cast<Test>(static_cast<uint8_t>(lhs) | rhs.value));
}
// Test_t | Test ~> Test_t
friend constexpr Test_t operator|(Test_t const &lhs, Test const &rhs)
{
return Test_t(static_cast<Test>(static_cast<uint8_t>(rhs) | lhs.value));
}
// Test_t | Test_t ~> Test_t
friend constexpr Test_t operator|(Test_t const &lhs, Test_t const &rhs)
{
return Test_t(static_cast<Test>(lhs.value | rhs.value));
}
};
// Test | Test ~> Test_t
constexpr Test_t operator|(Test const &lhs, Test const &rhs)
{
return Test_t(static_cast<Test>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs)));
}
// Declare Test2 exactly the same (abreviated)
... Test2
... Test2_t
// common register group
struct Common_t : public ::xpcc::Register8
{
constexpr Common_t() {}
explicit constexpr Common_t(uint8_t value)
: Register8(value) {}
constexpr Common_t(Test value) \
: Register(static_cast<uint8_t>(value)) {} \
constexpr Common_t(Test_t value) \
: Register(value.value) {}
constexpr Common_t(Test2 value) \
: Register(static_cast<uint8_t>(value)) {} \
constexpr Common_t(Test2_t value) \
: Register(value.value) {}
};
// all typesafe
Test_t vD = Test::A | Test::B;
vD = vD | Test::C;
vD = Test::D | vD;
Test_t vI = Test::E;
vD = vD | vI;
// but still simple access of raw value without casting
uint8_t raw = vD.value;
// but Test2 and Test are not compatible!!
Test2_t e = Test::A | Test2::a; // NOT POSSIBLE
e = e | Test::A; // NOT possible either
e = e | vD; // NOT possible either
// possible
Common_t common = Test::A;
common = Test2::a;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment