blob: ec2585d07ca090402755b66095311fb3976a135b [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <type_traits>
namespace pw::digital_io {
namespace internal {
// A type trait that describes the functionality required by a particular type
// of line, and also the functionality that we assume is always provided by an
// instance of that line. Used by Converter to determine if a conversion should
// be allowed.
//
// Specialize this for each type of DigitalIO line by defining a required
// functionality as `true` and optional functionality as `false`.
//
// Specializations must define the following fields:
// static constexpr bool input;
// static constexpr bool output;
// static constexpr bool interrupt;
//
template <typename T>
struct Requires;
// Concrete struct describing the available functionality of a line at runtime.
struct Provides {
bool input;
bool output;
bool interrupt;
};
// Returns the functionality always provided by the given type.
template <typename T>
constexpr Provides AlwaysProvidedBy() {
return {
.input = Requires<T>::input,
.output = Requires<T>::output,
.interrupt = Requires<T>::interrupt,
};
}
// Provides conversion operators between line objects based on the available
// functionality.
template <typename Self, typename CommonBase>
class Conversions {
private:
// Static check to enable conversions from `Self` to `T`.
template <
typename T,
typename = std::enable_if_t<std::is_base_of_v<CommonBase, T>>,
typename = std::enable_if_t<Requires<Self>::input || !Requires<T>::input>,
typename =
std::enable_if_t<Requires<Self>::output || !Requires<T>::output>,
typename = std::enable_if_t<Requires<Self>::interrupt ||
!Requires<T>::interrupt>>
struct Enabled {};
public:
template <typename T, typename = Enabled<T>>
constexpr operator T&() {
return as<T>();
}
template <typename T, typename = Enabled<T>>
constexpr operator const T&() const {
return as<T>();
}
template <typename T, typename = Enabled<T>>
constexpr T& as() {
return static_cast<T&>(static_cast<CommonBase&>(static_cast<Self&>(*this)));
}
template <typename T, typename = Enabled<T>>
constexpr const T& as() const {
return static_cast<const T&>(
static_cast<const CommonBase&>(static_cast<const Self&>(*this)));
}
};
} // namespace internal
// Specializations of Requires for each of the line types.
// These live outside the `internal` namespace so that the forward class
// declarations are in the correct namespace.
template <>
struct internal::Requires<class DigitalInterrupt> {
static constexpr bool input = false;
static constexpr bool output = false;
static constexpr bool interrupt = true;
};
template <>
struct internal::Requires<class DigitalIn> {
static constexpr bool input = true;
static constexpr bool output = false;
static constexpr bool interrupt = false;
};
template <>
struct internal::Requires<class DigitalInInterrupt> {
static constexpr bool input = true;
static constexpr bool output = false;
static constexpr bool interrupt = true;
};
template <>
struct internal::Requires<class DigitalOut> {
static constexpr bool input = false;
static constexpr bool output = true;
static constexpr bool interrupt = false;
};
template <>
struct internal::Requires<class DigitalOutInterrupt> {
static constexpr bool input = false;
static constexpr bool output = true;
static constexpr bool interrupt = true;
};
template <>
struct internal::Requires<class DigitalInOut> {
static constexpr bool input = true;
static constexpr bool output = true;
static constexpr bool interrupt = false;
};
template <>
struct internal::Requires<class DigitalInOutInterrupt> {
static constexpr bool input = true;
static constexpr bool output = true;
static constexpr bool interrupt = true;
};
} // namespace pw::digital_io