xref: /aosp_15_r20/external/pigweed/pw_digital_io/public/pw_digital_io/internal/conversions.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <type_traits>
17 
18 #include "pw_toolchain/internal/sibling_cast.h"
19 
20 namespace pw::digital_io {
21 namespace internal {
22 
23 // A type trait that describes the functionality required by a particular type
24 // of line, and also the functionality that we assume is always provided by an
25 // instance of that line. Used by Converter to determine if a conversion should
26 // be allowed.
27 //
28 // Specialize this for each type of DigitalIO line by defining a required
29 // functionality as `true` and optional functionality as `false`.
30 //
31 // Specializations must define the following fields:
32 // static constexpr bool input;
33 // static constexpr bool output;
34 // static constexpr bool interrupt;
35 //
36 template <typename T>
37 struct Requires;
38 
39 // Concrete struct describing the available functionality of a line at runtime.
40 struct Provides {
41   bool input;
42   bool output;
43   bool interrupt;
44 };
45 
46 // Returns the functionality always provided by the given type.
47 template <typename T>
AlwaysProvidedBy()48 constexpr Provides AlwaysProvidedBy() {
49   return {
50       .input = Requires<T>::input,
51       .output = Requires<T>::output,
52       .interrupt = Requires<T>::interrupt,
53   };
54 }
55 
56 // Provides conversion operators between line objects based on the available
57 // functionality.
58 template <typename Self, typename CommonBase>
59 class Conversions {
60  private:
61   // Static check to enable conversions from `Self` to `T`.
62   template <
63       typename T,
64       typename = std::enable_if_t<std::is_base_of_v<CommonBase, T>>,
65       typename = std::enable_if_t<Requires<Self>::input || !Requires<T>::input>,
66       typename =
67           std::enable_if_t<Requires<Self>::output || !Requires<T>::output>,
68       typename = std::enable_if_t<Requires<Self>::interrupt ||
69                                   !Requires<T>::interrupt>>
70   struct Enabled {};
71 
72  public:
73   template <typename T, typename = Enabled<T>>
74   constexpr operator T&() {
75     return as<T>();
76   }
77 
78   template <typename T, typename = Enabled<T>>
79   constexpr operator const T&() const {
80     return as<T>();
81   }
82 
83   template <typename T, typename = Enabled<T>>
as()84   [[nodiscard]] constexpr T& as() {
85     return pw::internal::SiblingCast<T&, CommonBase>(static_cast<Self&>(*this));
86   }
87 
88   template <typename T, typename = Enabled<T>>
as()89   [[nodiscard]] constexpr const T& as() const {
90     return static_cast<const T&>(
91         pw::internal::SiblingCast<const T&, CommonBase>(
92             static_cast<const Self&>(*this)));
93   }
94 };
95 
96 }  // namespace internal
97 
98 // Specializations of Requires for each of the line types.
99 // These live outside the `internal` namespace so that the forward class
100 // declarations are in the correct namespace.
101 
102 template <>
103 struct internal::Requires<class DigitalInterrupt> {
104   static constexpr bool input = false;
105   static constexpr bool output = false;
106   static constexpr bool interrupt = true;
107 };
108 
109 template <>
110 struct internal::Requires<class DigitalIn> {
111   static constexpr bool input = true;
112   static constexpr bool output = false;
113   static constexpr bool interrupt = false;
114 };
115 
116 template <>
117 struct internal::Requires<class DigitalInInterrupt> {
118   static constexpr bool input = true;
119   static constexpr bool output = false;
120   static constexpr bool interrupt = true;
121 };
122 
123 template <>
124 struct internal::Requires<class DigitalOut> {
125   static constexpr bool input = false;
126   static constexpr bool output = true;
127   static constexpr bool interrupt = false;
128 };
129 
130 template <>
131 struct internal::Requires<class DigitalOutInterrupt> {
132   static constexpr bool input = false;
133   static constexpr bool output = true;
134   static constexpr bool interrupt = true;
135 };
136 
137 template <>
138 struct internal::Requires<class DigitalInOut> {
139   static constexpr bool input = true;
140   static constexpr bool output = true;
141   static constexpr bool interrupt = false;
142 };
143 
144 template <>
145 struct internal::Requires<class DigitalInOutInterrupt> {
146   static constexpr bool input = true;
147   static constexpr bool output = true;
148   static constexpr bool interrupt = true;
149 };
150 
151 }  // namespace pw::digital_io
152