Last active
July 26, 2020 13:13
-
-
Save Crspy/afadfeafd588ce055a63154ad3ce923c to your computer and use it in GitHub Desktop.
C++11 templatized constexpr register wrapper class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stddef.h> // this is all we need | |
| namespace reg_wrapper | |
| { | |
| namespace detail | |
| { | |
| template <class T, T v> | |
| struct integral_constant | |
| { | |
| static constexpr T value = v; | |
| using value_type = T; | |
| using type = integral_constant; // using injected-class-name | |
| constexpr operator value_type() const noexcept { return value; } | |
| constexpr value_type operator()() const noexcept { return value; } //since c++14 | |
| }; | |
| using true_type = integral_constant<bool, true>; | |
| using false_type = integral_constant<bool, false>; | |
| template <class T, class U> | |
| struct is_same : false_type | |
| { | |
| }; | |
| template <class T> | |
| struct is_same<T, T> : true_type | |
| { | |
| }; | |
| template <class T> | |
| struct remove_volatile | |
| { | |
| typedef T type; | |
| }; | |
| template <class T> | |
| struct remove_volatile<volatile T> | |
| { | |
| typedef T type; | |
| }; | |
| } // namespace detail | |
| template <typename T> | |
| class TReg | |
| { | |
| template <bool...> | |
| struct bool_pack; | |
| template <bool... bs> | |
| using all_true = detail::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>; | |
| using folding_dummy = int[]; | |
| template <size_t... nbits> | |
| constexpr void AssertIndices() const | |
| { | |
| static_assert(all_true<(nbits < (sizeof(T) * 8))...>::value, | |
| "Bit index out of range"); | |
| }; | |
| public: | |
| // make a range of bits set to 1 and the rest is 0 | |
| template <size_t start_nbit, size_t end_nbit> | |
| inline typename detail::remove_volatile<T>::type make_range() | |
| { | |
| AssertIndices<start_nbit, end_nbit>(); | |
| return (((1 << start_nbit) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| template <size_t start_nbit, size_t end_nbit> | |
| constexpr void set_range(T ®) | |
| { | |
| AssertIndices<start_nbit, end_nbit>(); | |
| reg |= (((1 << start_nbit) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| template <size_t start_nbit, size_t end_nbit> | |
| constexpr void clear_range(T ®) | |
| { | |
| AssertIndices<start_nbit, end_nbit>(); | |
| reg &= ~(((1 << start_nbit) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| template <size_t start_nbit, size_t end_nbit> | |
| constexpr void toggle_range(T ®) | |
| { | |
| AssertIndices<start_nbit, end_nbit>(); | |
| reg ^= (((1 << start_nbit) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| // make a variable with the specified bits set to 1 and the rest is zero | |
| template <size_t... nbits> | |
| inline typename detail::remove_volatile<T>::type make_bits() | |
| { | |
| AssertIndices<nbits...>(); | |
| typename detail::remove_volatile<T>::type res{0}; | |
| (void)folding_dummy{0, ((res |= (1 << nbits)), 0)...}; | |
| return res; | |
| } | |
| template <size_t... nbits> | |
| constexpr void bit_config(T& reg,bool value) | |
| { | |
| AssertIndices<nbits...>(); | |
| (void)folding_dummy{((reg = (reg & ~(1 << nbits)) | (value << nbits)), 0)...}; | |
| } | |
| template <size_t... nbits> | |
| constexpr void set_bits(T ®) | |
| { | |
| AssertIndices<nbits...>(); | |
| (void)folding_dummy{((reg |= (1 << nbits)), 0)...}; | |
| } | |
| template <size_t... nbits> | |
| constexpr void clear_bits(T ®) | |
| { | |
| AssertIndices<nbits...>(); | |
| (void)folding_dummy{(reg &= ~(1 << nbits), 0)...}; | |
| } | |
| template <size_t... nbits> | |
| constexpr void toggle_bits(T ®) | |
| { | |
| AssertIndices<nbits...>(); | |
| (void)folding_dummy{(reg ^= (1 << nbits), 0)...}; | |
| } | |
| template <size_t nbit> | |
| constexpr T get_bit(T ®) const | |
| { | |
| AssertIndices<nbit>(); | |
| return (reg & (1 << nbit)) >> nbit; | |
| } | |
| constexpr void set_bits(T ®) { reg = ~static_cast<T>(0); } | |
| constexpr void clear_bits(T ®) { reg = 0; } | |
| constexpr void toggle_bits(T ®) { reg ^= ~(reg); } | |
| // (for non-const arguments) make a range of bits set to 1 and the rest is 0 | |
| inline typename detail::remove_volatile<T>::type make_range(size_t start_nbit, size_t end_nbit) | |
| { | |
| return (((1 << start_nbit) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| // for non-const arguments | |
| constexpr void set_range(T ®, size_t start_nbit, size_t end_nbit) | |
| { | |
| reg |= (((1 << (start_nbit)) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| // for non-const arguments | |
| constexpr void clear_range(T ®, size_t start_nbit, size_t end_nbit) | |
| { | |
| reg &= ~(((1 << (start_nbit)) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| // for non-const arguments | |
| constexpr void toggle_range(T ®, size_t start_nbit, size_t end_nbit) | |
| { | |
| reg ^= (((1 << (start_nbit)) - 1) ^ ((1 << (end_nbit + 1)) - 1)); | |
| } | |
| // (for non-const arguments) make a variable with the specified bits set to 1 and the rest is zero | |
| template <typename... Ts> | |
| inline typename detail::remove_volatile<T>::type make_bits(Ts... nbits) | |
| { | |
| typename detail::remove_volatile<T>::type res{0}; | |
| (void)folding_dummy{((res |= (1 << nbits)), 0)...}; | |
| return res; | |
| } | |
| // for non-const arguments | |
| template <typename... Ts> | |
| constexpr void bit_config(T& reg,bool value,Ts... nbits) | |
| { | |
| (void)folding_dummy{((reg = (reg & ~(1 << nbits)) | (value << nbits)), 0)...}; | |
| } | |
| // for non-const arguments | |
| template <typename... Ts> | |
| constexpr void set_bits(T ®, Ts... nbits) | |
| { | |
| (void)folding_dummy{((reg |= (1 << nbits)), 0)...}; | |
| } | |
| // for non-const arguments | |
| template <typename... Ts> | |
| constexpr void clear_bits(T ®, Ts... nbits) | |
| { | |
| (void)folding_dummy{((reg &= ~(1 << nbits)), 0)...}; | |
| } | |
| // for non-const arguments | |
| template <typename... Ts> | |
| constexpr void toggle_bits(T ®, Ts... nbits) | |
| { | |
| (void)folding_dummy{((reg ^= (1 << nbits)), 0)...}; | |
| } | |
| // for non-const arguments | |
| constexpr T get_bit(T ®, size_t nbit) const { return (reg & (1 << nbit)) >> nbit; } | |
| }; | |
| template <typename T> | |
| inline constexpr auto make_wrapper(__attribute__((unused)) const T ®) -> TReg<T> | |
| { | |
| return TReg<T>{}; | |
| } | |
| } // namespace reg_wrapper | |
| // example on atmega16 microcontroller | |
| // here we can pass any PORT/REG just so we can just deduce the type we are dealing with | |
| auto myreg = reg_wrapper::make_wrapper(PORTA); | |
| int main() | |
| { | |
| // setup | |
| myreg.set_bits<DDB0, DDB1>(DDRB); // make PORTB P0 , P1 OUTPUT | |
| // DDRB = myreg.make_bits<DDB0,DDB1>(); // initialize the register with DDB0 and DDB1 set to 1 | |
| for (;;) | |
| { | |
| // toggling P0,P1 of PORTB every 1 second | |
| myreg.set_bits<PB0, PB1>(PORTB); | |
| _delay_ms(1000); | |
| myreg.clear_bits<PB0, PB1>(PORTB); | |
| _delay_ms(1000); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment