Comms CCF
This is a simple communication layer based on COBS, CBOR and FNV-1A
Loading...
Searching...
No Matches
rpc.hpp
Go to the documentation of this file.
1
23
24#pragma once
25
26#include "cbor.hpp"
27#include "comptime_str.hpp"
28
29#if defined(DEBUG_RPC)
30#include DEBUG_RPC
31#else
32#include "ndebug.hpp"
33#endif
34
35#include <stddef.h>
36#include <stdint.h>
37
38#include <array>
39#include <functional>
40#include <span>
41#include <string_view>
42#include <tuple>
43#include <utility>
44
45#if defined(INLINE_VTABLE)
46#define self (this_)
47#define self_arg(type) const type & this_,
48#define self_app(value) value,
49#define prototype(ret, name, args) const ret (*name) args
50#define definition(ret, name, args) static ret name args
51#else
52#define self (*this)
53#define self_arg(type) /* implicit */
54#define self_app(value) /* implicit */
55#define prototype(ret, name, args) virtual ret name args const = 0
56#define definition(ret, name, args) ret name args const override
57#endif
58
59#define LITERAL_COMPTIME_STRING(name, value) const decltype(CompTimeString{value}) name{value}
60template<typename T>
61struct Type { static constexpr CompTimeString python = "Any"; };
62
63template<std::integral I>
64struct Type<I> { static constexpr CompTimeString python = "int"; };
65
66template<>
67struct Type<std::string_view> { static constexpr CompTimeString python = "str"; };
68
69template<size_t extent>
70struct Type<std::span<uint8_t, extent>> { static constexpr CompTimeString python = "bytes"; };
71
72template<>
73struct Type<std::tuple<>> { static constexpr CompTimeString python{"tuple[()]"}; };
74template<typename... Ts>
75struct Type<std::tuple<Ts...>>
76{
77 constexpr static CompTimeString python{
78 CompTimeString{"tuple["} +
79 (..., Type<Ts>::python) +
81 };
82};
83
85{
86public:
91 prototype(bool, call, (self_arg(NonTemplatedCall) std::span<uint8_t> & args, std::span<uint8_t> & ret));
92};
93
94template<typename Ret, typename... Args>
95class Call : public NonTemplatedCall
96{
97public:
98 using Fun = Ret (*)(Args...);
99 using ArgsTup = std::tuple<Args...>;
100 using Return = Ret;
101
102 Call(
103 const char * name_,
104 const char * doc_,
105 std::array<const char *, sizeof...(Args)> argNames_,
106 Fun ptr_)
107 :
108#if defined(INLINE_VTABLE)
110 {
111 // Note: Function arguments are normally contravariant
112 // but in this case the `this_` are const so it is safe
113 // (and all other arguments match).
114 .schema = reinterpret_cast<decltype(NonTemplatedCall::schema)>(reinterpret_cast<void *>(&schema)),
115 .call = reinterpret_cast<decltype(NonTemplatedCall::call)>(reinterpret_cast<void *>(&call)),
116 },
117#endif
118 name(name_),
119 doc(doc_),
120 argNames(argNames_),
121 ptr(ptr_) { }
122
123 definition(bool, schema, (self_arg(Call) Cbor::Sequence<Cbor::Major::Array> & seq))
124 {
125 Cbor::Sequence<Cbor::Major::Array> subseq(seq, 3 + 2 * sizeof...(Args));
126 return
127 subseq.encode(std::string_view(self.name)) &&
128 subseq.encode(std::string_view(self.doc)) &&
129 subseq.encode(static_cast<std::string_view>(Type<Return>::python)) &&
130 [&]<size_t... Idx>(std::index_sequence<Idx...>)
131 {
132 return (
133 (
134 subseq.encode(std::string_view(self.argNames[Idx])) &&
135 subseq.encode(static_cast<std::string_view>(
136 Type<std::tuple_element_t<Idx, ArgsTup>>::python))
137 ) &&
138 ...
139 );
140 }(std::index_sequence_for<Args...>{}) &&
141 subseq.as_expected();
142 }
143
144 definition(bool, call, (self_arg(Call) std::span<uint8_t> & args, std::span<uint8_t> & ret))
145 {
146 if (self.ptr == nullptr)
147 {
148 debugf(WARN "function ptr is null, ignoring call" END LOGLEVEL_ARGS);
149 return false;
150 }
151 debugf(DEBUG "decoding" LOGLEVEL_ARGS);
152 for (const auto byte : args)
153 {
154 debugf(" %02X", byte);
155 }
156 debugf(END);
157 auto argsTup = Cbor::Cbor<ArgsTup>::decode(args);
158 if (argsTup)
159 {
160 debugf(DEBUG "function is %p" END LOGLEVEL_ARGS, self.ptr);
161 auto retVal = Cbor::WrapVoid<Ret, Cbor::Undefined>{self.ptr, *argsTup};
162 return Cbor::Cbor<Ret>::encode(retVal.value, ret);
163 }
164 return false;
165 }
166private:
167 const char * name;
168 const char * doc;
169 std::array<const char *, sizeof...(Args)> argNames;
170 Fun ptr;
171};
172
173template<typename... Calls>
174class Rpc
175{
176public:
177 Rpc(Calls && ... calls_)
178 : tuple{std::forward<Calls>(calls_)...},
179 calls{
180 [&]<size_t... Idx>(std::index_sequence<Idx...>)
181 {
182 return std::array<std::reference_wrapper<NonTemplatedCall>, sizeof...(Calls)>{
183 (std::reference_wrapper<NonTemplatedCall>{
184 *static_cast<NonTemplatedCall *>(&std::get<Idx>(tuple))})...
185 };
186 }(std::index_sequence_for<Calls...>{})
187 } { }
188
190 bool schema(std::span<uint8_t> & buf) const
191 {
192 Cbor::Sequence<Cbor::Major::Array> seq(buf, sizeof...(Calls));
193 for (auto & c : calls)
194 {
195 auto & call = c.get();
196 if (!call.schema(self_app(call) seq))
197 {
198 debugf(WARN "Schema failed to encode (buf size %zu)" END LOGLEVEL_ARGS, buf.size());
199 return false;
200 }
201 }
202 return seq.as_expected();
203 }
204
207 bool call(size_t n, std::span<uint8_t> & args, std::span<uint8_t> & ret) const
208 {
209 if (n == 0)
210 {
211 return schema(ret);
212 }
213 else if (n < sizeof...(Calls) + 1)
214 {
215 auto & call = calls[n-1].get();
216 return call.call(self_app(call) args, ret);
217 }
218 else
219 {
220 debugf(WARN "Tried to call function %zu but max is %zu" END LOGLEVEL_ARGS, n, sizeof...(Calls));
221 return false;
222 }
223 }
224
225 std::tuple<Calls...> tuple;
226 std::array<std::reference_wrapper<NonTemplatedCall>, sizeof...(Calls)> calls;
227};
228
229// This is a header, undefine the debugf macro
230#include "debug_end.hpp"
Encoding for structured data (numbers, strings, bytes, arrays and maps). Also supports custom tags.
Definition cbor.hpp:794
Definition rpc.hpp:85
prototype(bool, call,(self_arg(NonTemplatedCall) std::span< uint8_t > &args, std::span< uint8_t > &ret))
prototype(bool, schema,(self_arg(NonTemplatedCall) Cbor::Sequence< Cbor::Major::Array > &seq))
Encodes the schema for the function into seq.
bool schema(std::span< uint8_t > &buf) const
Encode the schema for all the RPC functions onto buf.
Definition rpc.hpp:190
bool call(size_t n, std::span< uint8_t > &args, std::span< uint8_t > &ret) const
Definition rpc.hpp:207
Compile-time string manipulation.
#define debugf(...)
Definition ndebug.hpp:23
#define END
Terminator of log message at the end.
Definition ndebug.hpp:50
#define LOGLEVEL_ARGS
Definition ndebug.hpp:28
#define DEBUG
Debug levels at the start of debugf.
Definition ndebug.hpp:33
Parametrized CBOR interface.
Definition cbor.hpp:400
Allows materialising void values using a placeholder.
Definition cbor.hpp:312
Definition comptime_str.hpp:23
Definition rpc.hpp:61