blob: bc8e4d4bf716b35a5032ac63bb6b26540b373199 [file] [log] [blame]
// Copyright 2024 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>
#include "pw_assert/assert.h"
#include "pw_channel/channel.h"
#include "pw_toolchain/internal/sibling_cast.h"
namespace pw::channel {
namespace internal {
// Returns whether a sibling channel supports the required properties.
template <typename Self, typename Sibling>
using EnableIfConversionIsValid =
std::enable_if_t< // Sibling type must be a channel
std::is_base_of_v<AnyChannel, Sibling> &&
// Datagram and byte channels are not interchangeable
(Sibling::data_type() == Self::data_type()) &&
// Cannot use a unreliable channel as a reliable channel
(!Sibling::reliable() || Self::reliable()) &&
// Cannot use a non-readable channel as a readable channel
(!Sibling::readable() || Self::readable()) &&
// Cannot use a non-writable channel as a writable channel
(!Sibling::writable() || Self::writable()) &&
// Cannot use a non-seekable channel as a seekable channel
(!Sibling::seekable() || Self::seekable())>;
// Defines conversions between compatible channel types.
template <typename Self>
class Conversions {
private:
// Performs the same checks as EnableIfConversionIsValid, but generates a
// static_assert with a helpful message if any condition is not met.
template <typename Sibling>
static constexpr void CheckThatConversionIsValid() {
static_assert(std::is_base_of_v<AnyChannel, Sibling>,
"Only conversions to other AnyChannel types are supported");
static_assert(Sibling::data_type() == Self::data_type(),
"Datagram and byte channels are not interchangeable");
static_assert(!Sibling::reliable() || Self::reliable(),
"Cannot use a unreliable channel as a reliable channel");
static_assert(!Sibling::readable() || Self::readable(),
"Cannot use a non-readable channel as a readable channel");
static_assert(!Sibling::writable() || Self::writable(),
"Cannot use a non-writable channel as a writable channel");
static_assert(!Sibling::seekable() || Self::seekable(),
"Cannot use a non-seekable channel as a seekable channel");
}
public:
// Explicit conversion to a compatible AnyChannel type.
template <typename Sibling>
[[nodiscard]] Sibling& as() {
if constexpr (std::is_same_v<Sibling, AnyChannel>) {
return static_cast<Self&>(*this);
} else {
CheckThatConversionIsValid<Sibling>();
return pw::internal::SiblingCast<Sibling&, AnyChannel>(
static_cast<Self&>(*this));
}
}
template <typename Sibling>
[[nodiscard]] const Sibling& as() const {
if constexpr (std::is_same_v<Sibling, AnyChannel>) {
return static_cast<const Self&>(*this);
} else {
CheckThatConversionIsValid<Sibling>();
return pw::internal::SiblingCast<const Sibling&, AnyChannel>(
static_cast<const Self&>(*this));
}
}
// Explicit conversion to a ByteChannel or DatagramChannel with the specified
// properties.
template <Property... kProperties>
[[nodiscard]] auto& as() {
using Sibling = Channel<Self::data_type(), kProperties...>;
CheckThatConversionIsValid<Sibling>();
return pw::internal::SiblingCast<Sibling&, AnyChannel>(
static_cast<Self&>(*this));
}
template <Property... kProperties>
[[nodiscard]] const auto& as() const {
using Sibling = Channel<Self::data_type(), kProperties...>;
CheckThatConversionIsValid<Sibling>();
return pw::internal::SiblingCast<const Sibling&, AnyChannel>(
static_cast<const Self&>(*this));
}
};
} // namespace internal
// Defines a channel specialization with the specified type and read/write/seek
// capabilities. This macro expands other macros to implement unsupported
// operations and hide them from the public API.
//
// Channel is specialized for each supported combination of byte/datagram and
// read/write/seek attributes. Invalid combinations fall back to the default
// implementation and fail a static_assert.
//
// Specializing these channel classes accomplishes the following:
//
// - Implement unsupported operations in a standard way. Extending a channel
// only requires implementing supported functions.
// - Hide unsupported overloads or functions from the public API. They are still
// accessible in the AnyChannel base class.
//
#define _PW_CHANNEL(type, read, write, seek, ...) \
template <> \
class Channel<DataType::type, __VA_ARGS__> \
: public AnyChannel, \
public internal::Conversions<Channel<DataType::type, __VA_ARGS__>> { \
private: \
static_assert(PropertiesAreValid<__VA_ARGS__>()); \
\
static constexpr uint8_t kProperties = GetProperties<__VA_ARGS__>(); \
\
_PW_CHANNEL_READABLE_##read; \
_PW_CHANNEL_WRITABLE_##write; \
_PW_CHANNEL_SEEKABLE_##seek; \
\
public: \
[[nodiscard]] static constexpr DataType data_type() { \
return DataType::type; \
} \
[[nodiscard]] static constexpr bool reliable() { \
return (kProperties & Property::kReliable) != 0; \
} \
[[nodiscard]] static constexpr bool seekable() { \
return (kProperties & Property::kSeekable) != 0; \
} \
[[nodiscard]] static constexpr bool readable() { \
return (kProperties & Property::kReadable) != 0; \
} \
[[nodiscard]] static constexpr bool writable() { \
return (kProperties & Property::kWritable) != 0; \
} \
\
/* Implicit conversion to a compatible channel type. */ \
template < \
typename Sibling, \
typename = internal::EnableIfConversionIsValid<Channel, Sibling>> \
operator Sibling&() { \
return as<Sibling>(); \
} \
template < \
typename Sibling, \
typename = internal::EnableIfConversionIsValid<Channel, Sibling>> \
operator const Sibling&() const { \
return as<Sibling>(); \
} \
\
_PW_CHANNEL_##type(__VA_ARGS__); \
\
protected: \
constexpr Channel() : AnyChannel(channel::DataType::type, kProperties) {} \
}
// Macros that stub out read/write/seek and hide them if unsupported.
#define _PW_CHANNEL_READABLE_READ static_assert(true)
#define _PW_CHANNEL_READABLE_SKIP \
async2::Poll<Result<multibuf::MultiBuf>> DoPendRead(async2::Context&) \
final { \
return async2::Ready(Result<multibuf::MultiBuf>(Status::Unimplemented())); \
} \
using AnyChannel::PendRead
#define _PW_CHANNEL_WRITABLE_WRTE static_assert(true)
#define _PW_CHANNEL_WRITABLE_SKIP \
async2::Poll<Status> DoPendReadyToWrite(async2::Context&) final { \
return Status::Unimplemented(); \
} \
multibuf::MultiBufAllocator& DoGetWriteAllocator() final { \
PW_ASSERT(false); /* shouldn't be called on non-writeable channels */ \
} \
Result<channel::WriteToken> DoWrite(multibuf::MultiBuf&&) final { \
return Status::Unimplemented(); \
} \
async2::Poll<Result<WriteToken>> DoPendFlush(async2::Context&) final { \
return async2::Ready(Result<WriteToken>(Status::Unimplemented())); \
} \
using AnyChannel::PendReadyToWrite; \
using AnyChannel::Write; \
using AnyChannel::PendFlush
#define _PW_CHANNEL_SEEKABLE_SEEK static_assert(true)
// TODO: b/323622630 - Implement DoSeek() and DoPosition()
#define _PW_CHANNEL_SEEKABLE_SKIP \
using AnyChannel::Seek; \
using AnyChannel::Position
// Function for converting datagram channels to byte channels.
#define _PW_CHANNEL_kDatagram(...) \
ByteChannel<__VA_ARGS__>& IgnoreDatagramBoundaries() { \
return pw::internal::SiblingCast<ByteChannel<__VA_ARGS__>&, AnyChannel>( \
*this); \
} \
const ByteChannel<__VA_ARGS__>& IgnoreDatagramBoundaries() const { \
return pw::internal::SiblingCast<const ByteChannel<__VA_ARGS__>&, \
AnyChannel>(*this); \
} \
static_assert(true)
#define _PW_CHANNEL_kByte(...) static_assert(true)
// Generate specializations for the supported channel types.
// _PW_CHANNEL(
// kByte, READ, WRTE, SEEK, kReliable, kReadable, kWritable, kSeekable);
_PW_CHANNEL(kByte, READ, WRTE, SKIP, kReliable, kReadable, kWritable);
// _PW_CHANNEL(kByte, READ, SKIP, SEEK, kReliable, kReadable, kSeekable);
_PW_CHANNEL(kByte, READ, SKIP, SKIP, kReliable, kReadable);
// _PW_CHANNEL(kByte, READ, WRTE, SEEK, kReadable, kWritable, kSeekable);
_PW_CHANNEL(kByte, READ, WRTE, SKIP, kReadable, kWritable);
// _PW_CHANNEL(kByte, READ, SKIP, SEEK, kReadable, kSeekable);
_PW_CHANNEL(kByte, READ, SKIP, SKIP, kReadable);
// _PW_CHANNEL(kByte, SKIP, WRTE, SEEK, kReliable, kWritable, kSeekable);
_PW_CHANNEL(kByte, SKIP, WRTE, SKIP, kReliable, kWritable);
// _PW_CHANNEL(kByte, SKIP, WRTE, SEEK, kWritable, kSeekable);
_PW_CHANNEL(kByte, SKIP, WRTE, SKIP, kWritable);
// _PW_CHANNEL(
// kDatagram, READ, WRTE, SEEK, kReliable, kReadable, kWritable, kSeekable);
_PW_CHANNEL(kDatagram, READ, WRTE, SKIP, kReliable, kReadable, kWritable);
// _PW_CHANNEL(kDatagram, READ, SKIP, SEEK, kReliable, kReadable, kSeekable);
_PW_CHANNEL(kDatagram, READ, SKIP, SKIP, kReliable, kReadable);
// _PW_CHANNEL(kDatagram, READ, WRTE, SEEK, kReadable, kWritable, kSeekable);
_PW_CHANNEL(kDatagram, READ, WRTE, SKIP, kReadable, kWritable);
// _PW_CHANNEL(kDatagram, READ, SKIP, SEEK, kReadable, kSeekable);
_PW_CHANNEL(kDatagram, READ, SKIP, SKIP, kReadable);
// _PW_CHANNEL(kDatagram, SKIP, WRTE, SEEK, kReliable, kWritable, kSeekable);
_PW_CHANNEL(kDatagram, SKIP, WRTE, SKIP, kReliable, kWritable);
// _PW_CHANNEL(kDatagram, SKIP, WRTE, SEEK, kWritable, kSeekable);
_PW_CHANNEL(kDatagram, SKIP, WRTE, SKIP, kWritable);
#undef _PW_CHANNEL
#undef _PW_CHANNEL_READABLE_READ
#undef _PW_CHANNEL_READABLE_SKIP
#undef _PW_CHANNEL_WRITABLE_WRTE
#undef _PW_CHANNEL_WRITABLE_SKIP
#undef _PW_CHANNEL_SEEKABLE_SEEK
#undef _PW_CHANNEL_SEEKABLE_SKIP
#undef _PW_CHANNEL_kDatagram
#undef _PW_CHANNEL_kByte
} // namespace pw::channel