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