Dispatch on a message id¶
A header carries an id; the id says which of several messages follows. rbe::frames::any holds the
candidates and match dispatches to the right one.
Source: example/howto/any_match.cpp, CMake target howto_any_match.
Declare the candidates¶
The header marks the field the id is read from with =rbe::id; each candidate declares the id it answers
to with =rbe::id(value). The two must have the same type.
enum class msg_type : std::uint8_t { order = 1, cancel = 2, heartbeat = 3 };
// the header says which message follows: rbe::id marks the field
struct [[=rbe::pack_be]] Header {
[[=rbe::id]] msg_type type;
std::uint16_t sequence;
};
// each candidate says which id it answers to: rbe::id(value) on the type
struct [[=rbe::pack_be, =rbe::id(msg_type::order)]] Order { std::uint32_t order_id; std::uint64_t price; };
struct [[=rbe::pack_be, =rbe::id(msg_type::cancel)]] Cancel { std::uint32_t order_id; };
// a message with no fields: rbe::empty, zero bytes on the wire
struct [[=rbe::id(msg_type::heartbeat), =rbe::empty]] Heartbeat { };
using message = rbe::frames::frame<Header, rbe::frames::any<Order, Cancel, Heartbeat>>;
Match¶
One callback per candidate you handle, plus a fallback for everything else.
auto msg = rbe::deserialize<message>(bytes);
msg.payload().match(
[](Order const& order) { // by value: the Order is decoded, then handed over
std::println("order {} @ {}", order.order_id, order.price);
},
[](rbe::dsrl::proxy<Cancel> cancel) { // by proxy: decoded field by field, on access
std::println("cancel {}", cancel.field<"order_id">());
},
[](Heartbeat) { std::println("heartbeat"); },
[](rbe::unmatched auto other) { // mandatory: ids the code does not know, or does not handle here
std::println("id {} {}", std::to_underlying(other.id), other.known_id ? "not handled" : "unknown");
}
);
- Take a candidate by value (
Order const&) to have it decoded before the call, or asrbe::dsrl::proxy<T>to read it field by field. Not both: that is ambiguous and does not compile. - A candidate marked
=rbe::emptyis always taken by value. - The
rbe::unmatchedcallback is mandatory. It receives both ids the code does not know and candidates no other callback handles;known_idtells them apart,idis the value on the wire anddatathe bytes. - Every callback must return the same type.
Ask instead of matching¶
auto payload = rbe::deserialize<message>(bytes).payload();
bool is_order = payload.is<Order>();
auto id = payload.id(); // the id on the wire, as the header's field type
bool known = payload.known_id(); // whether one of the candidates answers to it
std::optional<Order> order = payload.as<Order>(rbe::dsrl::eager); // nullopt unless is<Order>()
std::optional<rbe::dsrl::proxy<Order>> view = payload.as<Order>(); // lazy by default
See also: Frames reference, any.