mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-20 20:42:53 +00:00
Redesign (simplify) top-level avclan interface to use expected/nullable unique_ptr
- Peripheral::read/send now return `expected<unique_ptr<Frame>, Error>` for a unified success/error interface. - Peripheral::route returns `expected<unique_ptr<Frame>, Error>` to distinguish intent: (intentional) non-response vs unable to respond - Other functions with optional message semantics (handle,poll,react) return a unique_ptr whose ownership-state indicates response intent (i.e. send new message) Bundled necessary changes: - Switch Frame allocation from global to local (enabled via member function new/delete) - Add new header-library tl::expected to shim avr-libstdcpp Unrelated: Defensive `continue` added after `route`, to reduce Bus activity check latency Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+47
-24
@@ -8,33 +8,44 @@
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace detail {
|
||||
template <class T, auto N> struct Deleter;
|
||||
}
|
||||
|
||||
template <class T, std::integral auto N, bool Owning = false>
|
||||
requires((N & (N - 1)) == 0 && N <= std::numeric_limits<uint8_t>::max())
|
||||
template <class T, std::integral auto N, bool Owning = false,
|
||||
class Deleter = std::default_delete<T>>
|
||||
requires((N & (N - 1)) == 0 && N <= std::numeric_limits<uint8_t>::max() &&
|
||||
// push() drops the passed-in deleter and pop() fabricates a fresh
|
||||
// one, so a deleter must either derive its state from the queue
|
||||
// itself (the pool Deleter) or carry no state at all
|
||||
(std::is_same_v<Deleter, detail::Deleter<T, N>> ||
|
||||
(std::is_empty_v<Deleter> &&
|
||||
std::is_nothrow_default_constructible_v<Deleter>)))
|
||||
class Queue {
|
||||
using Deleter = detail::template Deleter<T, N>;
|
||||
friend Deleter;
|
||||
friend detail::Deleter<T, N>;
|
||||
|
||||
public:
|
||||
// Only full and copy-convert-from-full construction is allowed
|
||||
Queue() = delete;
|
||||
// Only empty construction is allowed for non-Owning, non-pool deleters
|
||||
constexpr Queue()
|
||||
requires(!Owning && !std::is_same_v<Deleter, detail::Deleter<T, N>>)
|
||||
: owner{nullptr} {}
|
||||
Queue(const Queue &) = delete;
|
||||
|
||||
// Moving is unsupported due to being self-referential
|
||||
Queue(Queue &&) = delete;
|
||||
Queue &operator=(Queue &&) = delete;
|
||||
|
||||
// Construct from pre-defined storage
|
||||
constexpr Queue(T (&items)[N])
|
||||
requires(Owning)
|
||||
requires(Owning) && std::same_as<Deleter, detail::Deleter<T, N>>
|
||||
: owner{this}, write{N} {
|
||||
for (uint8_t i = 0; i < N; ++i)
|
||||
buf[i] = &items[i];
|
||||
}
|
||||
constexpr Queue(Queue<T, N, true> &queue)
|
||||
// Construct non-Owning empty from Owning queue
|
||||
constexpr Queue(Queue<T, N, true, Deleter> &queue)
|
||||
requires(!Owning)
|
||||
: owner{&queue} {}
|
||||
|
||||
@@ -46,14 +57,22 @@ public:
|
||||
uint8_t push(std::unique_ptr<T, Deleter> x)
|
||||
requires(!Owning)
|
||||
{
|
||||
// structurally unnecessary; empty construction from same size parent
|
||||
// guarantees
|
||||
// !isFull assert(!isFull());
|
||||
if constexpr (!std::is_same_v<Deleter, detail::Deleter<T, N>>) {
|
||||
// structurally unnecessary for detail::Deleter, where empty construction
|
||||
// is only from same size parent
|
||||
if (isFull())
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!x || !x.get_deleter().is_owned_by(owner))
|
||||
if (!x)
|
||||
return 1;
|
||||
|
||||
reclaim(x.release());
|
||||
if constexpr (std::is_same_v<Deleter, detail::Deleter<T, N>>) {
|
||||
if (!x.get_deleter().is_owned_by(owner))
|
||||
return 1;
|
||||
}
|
||||
|
||||
claim(x.release());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -69,35 +88,39 @@ public:
|
||||
if (isEmpty())
|
||||
return nullptr;
|
||||
|
||||
if constexpr (Owning)
|
||||
return {buf[mask(read++)], {this}};
|
||||
if constexpr (!std::is_same_v<Deleter, detail::Deleter<T, N>>)
|
||||
return {buf[mask(read++)], Deleter{}};
|
||||
else if constexpr (Owning)
|
||||
return {buf[mask(read++)], Deleter{this}};
|
||||
else
|
||||
return {buf[mask(read++)], {owner}};
|
||||
return {buf[mask(read++)], Deleter{owner}};
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t mask(uint8_t pos) const { return pos & (N - 1); }
|
||||
void reclaim(T *x) { buf[mask(write++)] = x; }
|
||||
void claim(T *x) { buf[mask(write++)] = x; }
|
||||
|
||||
std::array<T *, N> buf = {};
|
||||
Queue<T, N, true> *const owner;
|
||||
Queue<T, N, true, Deleter> *const owner;
|
||||
uint8_t read = 0;
|
||||
uint8_t write = 0;
|
||||
};
|
||||
|
||||
template <class T, auto N> Queue(Queue<T, N, true> &) -> Queue<T, N, false>;
|
||||
template <class T, auto N> Queue(T (&items)[N]) -> Queue<T, N, true>;
|
||||
template <class T, auto N, class D>
|
||||
Queue(Queue<T, N, true, D> &) -> Queue<T, N, false, D>;
|
||||
template <class T, auto N>
|
||||
Queue(T (&items)[N]) -> Queue<T, N, true, detail::Deleter<T, N>>;
|
||||
|
||||
namespace detail {
|
||||
template <class T, auto N> struct Deleter {
|
||||
Deleter() = default;
|
||||
constexpr Deleter(Queue<T, N, true> *owner) : owner{owner} {}
|
||||
void operator()(T *x) const { owner->reclaim(x); }
|
||||
constexpr bool is_owned_by(const Queue<T, N, true> *parent) const {
|
||||
constexpr Deleter(Queue<T, N, true, Deleter> *owner) : owner{owner} {}
|
||||
void operator()(T *x) const { owner->claim(x); }
|
||||
constexpr bool is_owned_by(const Queue<T, N, true, Deleter> *parent) const {
|
||||
return owner == parent;
|
||||
}
|
||||
|
||||
private:
|
||||
Queue<T, N, true> *const owner = nullptr;
|
||||
Queue<T, N, true, Deleter> *const owner = nullptr;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
Reference in New Issue
Block a user