]> code.bitgloo.com Git - clyne/funreg.git/commitdiff
Add some concepts to verify types
authorClyne <clyne@bitgloo.com>
Thu, 11 Aug 2022 01:21:03 +0000 (21:21 -0400)
committerClyne <clyne@bitgloo.com>
Thu, 11 Aug 2022 01:21:03 +0000 (21:21 -0400)
funreg.hpp

index 00cd3a92d86a65c66e7d5b7a5494d9de4da3527a..accdb9d6a7bfca62f6e8676cea52712b233a06af 100644 (file)
  */
 #define FUNREG_ENABLE_EXTERNAL_IO
 
+/**
+ * Comment to disable compile-time error checks, which are mostly concepts
+ * to verify proper library usage.
+ */
+#define FUNREG_ENABLE_ERROR_CHECKS
+
 #include <stdint.h>
 
 #ifdef FUNREG_ENABLE_EXTERNAL_IO
 #include <type_traits>
 #endif
 
+#ifdef FUNREG_ENABLE_ERROR_CHECKS
+#include <concepts>
+#endif
+
 namespace fr {
 
 // A utility to measure a bit-mask's offset from bit zero.
 template<auto Mask, unsigned int N = 0>
-constexpr auto BitOffset = []() constexpr {
+requires(std::unsigned_integral<decltype(Mask)> &&
+         (N < 8 * sizeof(Mask)))
+constexpr auto BitOffset = []() constexpr -> unsigned int {
     if constexpr (Mask & 1)
         return N;
     else
@@ -41,6 +53,7 @@ constexpr auto BitOffset = []() constexpr {
  * structure as a template.
  */
 template<typename T, uintptr_t Addr>
+requires(std::unsigned_integral<T>)
 struct MemoryIO {
     using type = T;
     constexpr static auto addr = Addr;
@@ -60,6 +73,45 @@ struct MemoryIO {
     }
 };
 
+template<typename T>
+concept IsAccess = requires(typename T::type x) {
+    requires std::same_as<decltype(T::addr), const uintptr_t>;
+    {T::read()} -> std::same_as<typename T::type>;
+    {T::write(x)} -> std::same_as<void>;
+};
+
+#ifdef FUNREG_ENABLE_EXTERNAL_IO
+template<typename Access>
+requires IsAccess<Access>
+struct Register;
+
+template<typename T>
+concept IsRegister = requires {
+    requires IsAccess<typename T::access>;
+    requires std::same_as<T, Register<typename T::access>>;
+};
+#else
+template<typename T, uintptr_t Addr>
+requires(std::unsigned_integral<T>)
+class Register;
+
+template<typename T>
+concept IsRegister = requires {
+    requires std::unsigned_integral<typename T::type>;
+    requires IsAccess<typename T::Access>;
+};
+#endif
+
+template<typename Reg, typename Reg::type Mask>
+requires IsRegister<Reg>
+struct RegisterMask;
+
+template<typename T>
+concept IsRegisterMask = requires {
+    requires IsRegister<typename T::reg>;
+    requires std::same_as<T, RegisterMask<typename T::reg, T::mask>>;
+};
+
 /**
  * @struct Register
  * @brief  Defines a register, given how to access it.
@@ -70,16 +122,22 @@ struct MemoryIO {
  */
 #ifdef FUNREG_ENABLE_EXTERNAL_IO
 template<typename Access>
-struct Register {
+requires IsAccess<Access>
+class Register {
+public:
     using access = Access;
     using T = typename Access::type;
     constexpr static auto Addr = Access::addr;
 #else
 template<typename T, uintptr_t Addr>
-struct Register {
-    using RegAccess = MemoryIO<T, Addr>;
+requires(std::unsigned_integral<T>)
+class Register {
+public:
+    using Access = MemoryIO<T, Addr>;
 #endif // FUNREG_ENABLE_EXTERNAL_IO
 
+    using type = T;
+
     /**
      * Gets a pointer to the register.
      */
@@ -98,6 +156,7 @@ struct Register {
      * Sets register bits to '1' according to the given RegisterMasks.
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void set() {
         apply<Masks...>([](auto r, auto m) { return r | m; });
     }
@@ -113,6 +172,7 @@ struct Register {
      * Clears register bits to '0' according to the given RegisterMasks.
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void clear() {
         apply<Masks...>([](auto r, auto m) { return r & ~m; });
     }
@@ -128,6 +188,7 @@ struct Register {
      * Toggles bits in the register according to the given RegisterMasks.
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void toggle() {
         apply<Masks...>([](auto r, auto m) { return r ^m; });
     }
@@ -145,6 +206,7 @@ struct Register {
      * If no masks are given, all register bits are returned.
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static auto read() {
         if constexpr (sizeof...(Masks) > 0)
             return read() & mergeMasks<Masks...>();
@@ -157,6 +219,7 @@ struct Register {
      * If no masks are given, tests if the register has a non-zero value.
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static bool test() {
         if constexpr (sizeof...(Masks) > 0) {
             auto mask = mergeMasks<Masks...>();
@@ -185,13 +248,13 @@ struct Register {
         }
     }
 
-    // Below is meant for internal use only.
-
+private:
     // Applies bit-masks to the register through the provided function.
     // The provided function receives a pointer to the register's data and a
     // bit-mask created by merging all provided bit-masks.
     // If no masks are given, a mask selecting all bits is used.
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void apply(auto fn) {
         if constexpr (sizeof...(Masks) > 0) {
             auto mask = mergeMasks<Masks...>();
@@ -205,6 +268,7 @@ struct Register {
     // Takes a list of bit-masks, and returns a merged mask of those which are
     // meant for this register.
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static auto mergeMasks() {
         if constexpr (sizeof...(Masks) > 0) {
             if constexpr ((isThis<typename Masks::reg> | ...)) {
@@ -224,20 +288,20 @@ struct Register {
 #ifdef FUNREG_ENABLE_EXTERNAL_IO
     // Determines if the given register matches this one.
     template<typename Reg>
+    requires(IsRegister<Reg>)
     constexpr static bool isThis = [] {
         return std::is_same_v<typename Reg::access, access> && Addr == Reg::Addr;
     }();
 #else
     // Determines if the given register matches this one.
     template<typename Reg>
+    requires(IsRegister<Reg>)
     constexpr static bool isThis = [] {
         return Addr == Reg::Addr;
     }();
 #endif // FUNREG_ENABLE_EXTERNAL_IO
 
     Register() = delete;
-
-    using type = T;
 };
 
 /**
@@ -251,6 +315,7 @@ struct Register {
  *     using LED_RED = RegisterMask<GPIO_OUT, (1 << 2)>;
  */
 template<typename Reg, typename Reg::type Mask>
+requires IsRegister<Reg>
 struct RegisterMask
 {
     using T = typename Reg::type;
@@ -373,6 +438,7 @@ struct RegisterMask
  * @tparam value The value to be used for the given Mask.
  */
 template<typename Mask, Mask::T value>
+requires(IsRegisterMask<Mask>)
 struct RegisterMaskValue
 {
     /**
@@ -406,6 +472,7 @@ struct RegisterMaskValue
  * each register.
  */
 template<typename... Registers>
+requires(IsRegister<Registers>, ...)
 class RegisterGroup
 {
 public:
@@ -415,6 +482,7 @@ public:
      * only written once.
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void set() {
         apply<Masks...>([](auto r, auto m) { return r | m; });
     }
@@ -425,6 +493,7 @@ public:
      * Only reads and writes each register once; see set().
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void clear() {
         apply<Masks...>([](auto r, auto m) { return r & ~m; });
     }
@@ -435,6 +504,7 @@ public:
      * Only reads and writes each register once; see set().
      */
     template<typename... Masks>
+    requires(IsRegisterMask<Masks>, ...)
     static void toggle() {
         apply<Masks...>([](auto r, auto m) { return r ^ m; });
     }
@@ -465,6 +535,7 @@ private:
  * register.
  */
 template<typename... RegMasks>
+requires(IsRegisterMask<RegMasks>, ...)
 constexpr auto Masks = (RegMasks::mask | ...);
 
 #ifdef FUNREG_ENABLE_EXTERNAL_IO
@@ -501,4 +572,3 @@ using MemRegister = Register<T, Addr>;
 } // namespace fr
 
 #endif // FUNCTIONAL_REGISTER_IO_H
-