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:
Allen Hill
2026-07-17 14:24:03 -07:00
parent 200a225add
commit 5208e083f3
17 changed files with 536 additions and 187 deletions
+17 -11
View File
@@ -5,9 +5,11 @@
#include <concepts>
#include <cstdint>
#include <memory>
#include "avclan.h"
#include "frame.hpp"
#include "stdshim.hpp"
namespace avclan {
@@ -46,15 +48,19 @@ enum class Device : uint8_t {
};
template <class T>
concept DeviceInterface = requires {
std::integral_constant<Device, T::id>{};
} && requires(T dev, const Frame *in, Frame *out, detail::Error::Send err) {
dev.init();
dev.handle(in, out);
dev.enable(out);
dev.react(out, err);
{ dev.pending() } -> std::convertible_to<bool>;
dev.resolvepending();
dev.emit(out);
};
concept DeviceInterface =
requires { std::integral_constant<Device, T::id>{}; } &&
requires(T dev, const Frame *in, Frame *out,
expected<std::unique_ptr<Frame>, detail::SendError> exp) {
dev.init();
dev.handle(in, out);
dev.enable(out);
{
dev.react(std::move(exp))
} -> std::same_as<std::unique_ptr<Frame>>;
{ dev.pending() } -> std::convertible_to<bool>;
// Devices must clear `pending()` after `emit()` is called
dev.emit(out);
};
} // namespace avclan