Nui
Loading...
Searching...
No Matches
rpc_client.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <traits/functions.hpp>
10
11#include <string>
12#include <cstdint>
13#include <tuple>
14
15namespace Nui
16{
17 namespace Detail
18 {
19 template <typename ReturnType, typename ArgsTypes, typename IndexSeq>
20 struct FunctionWrapperImpl
21 {};
22
23 template <typename ArgT>
24 constexpr static auto extractMember(Nui::val const& val) -> decltype(auto)
25 {
26 if constexpr (std::is_same_v<std::decay_t<ArgT>, Nui::val>)
27 return val;
28 else
29 {
30 std::decay_t<ArgT> value;
31 convertFromVal(val, value);
32 return value;
33 }
34 }
35
36 template <typename ReturnType>
37 struct FunctionWrapperImpl<ReturnType, std::tuple<Nui::val>, std::index_sequence<0>>
38 {
39 template <typename FunctionT>
40 constexpr static auto wrapFunction(FunctionT&& func)
41 {
42 return [func = std::forward<FunctionT>(func)](Nui::val const& args) mutable {
43 func(args);
44 };
45 }
46 };
47
48 template <typename ReturnType, typename ArgType>
49 struct FunctionWrapperImpl<ReturnType, std::tuple<ArgType>, std::index_sequence<0>>
50 {
51 template <typename FunctionT>
52 constexpr static auto wrapFunction(FunctionT&& func)
53 {
54 return [func = std::forward<FunctionT>(func)](Nui::val const& arg) mutable {
56 };
57 }
58 };
59
60 template <typename ReturnType, typename... ArgsTypes, std::size_t... Is>
61 struct FunctionWrapperImpl<ReturnType, std::tuple<ArgsTypes...>, std::index_sequence<Is...>>
62 {
63 template <typename FunctionT>
64 constexpr static auto wrapFunction(FunctionT&& func)
65 {
66 return [func = std::forward<FunctionT>(func)](Nui::val const& args) mutable {
68 };
69 }
70 };
71
72 template <typename ReturnType, typename ArgsTypes>
73 struct FunctionWrapperImpl2
74 {};
75
76 template <typename ReturnType, typename... ArgsTypes>
77 struct FunctionWrapperImpl2<ReturnType, std::tuple<ArgsTypes...>>
78 : public FunctionWrapperImpl<ReturnType, std::tuple<ArgsTypes...>, std::index_sequence_for<ArgsTypes...>>
79 {};
80
81 template <typename FunctionT>
82 struct FunctionWrapper
83 : public FunctionWrapperImpl2<
84 typename Traits::FunctionTraits<std::decay_t<FunctionT>>::ReturnType,
85 typename Traits::FunctionTraits<std::decay_t<FunctionT>>::ArgsTuple>
86 {};
87 }
88
90 {
91 public:
93 {
94 public:
95 template <typename... Args>
96 auto operator()(Args&&... args) const
97 {
98 using namespace std::string_literals;
99 if (!resolve())
100 {
101 WebApi::Console::error("Remote callable with name '"s + name_ + "' is undefined");
102 return Nui::val::undefined();
103 }
104 if (backChannel_.empty())
105 return callable_(convertToVal(args)...);
106 return callable_(convertToVal(backChannel_), convertToVal(args)...);
107 }
109 {
110 using namespace std::string_literals;
111 if (!resolve())
112 {
113 WebApi::Console::error("Remote callable with name '"s + name_ + "' is undefined");
114 return Nui::val::undefined();
115 }
116 if (backChannel_.empty())
117 return callable_(val);
118 return callable_(convertToVal(backChannel_), val);
119 }
120
121 bool isValid() const
122 {
123 return resolve();
124 }
125
126 explicit operator bool() const
127 {
128 return isValid();
129 }
130
131 explicit RemoteCallable(std::string name)
132 : name_{std::move(name)}
133 , backChannel_{}
134 , callable_{Nui::val::undefined()}
135 , isSet_{false}
136 {}
137
138 RemoteCallable(std::string name, std::string backChannel)
139 : name_{std::move(name)}
140 , backChannel_{std::move(backChannel)}
141 , callable_{Nui::val::undefined()}
142 , isSet_{false}
143 {}
144
145 private:
146 bool resolve() const
147 {
148 using namespace std::string_literals;
149 if (isSet_)
150 return true;
151
152 const auto rpcObject = Nui::val::global("nui_rpc");
153 if (rpcObject.isUndefined())
154 return false;
155
156 callable_ = Nui::val::global("nui_rpc")["backend"][name_.c_str()];
157 isSet_ = !callable_.isUndefined();
158 return isSet_;
159 }
160
161 private:
162 std::string name_;
163 std::string backChannel_;
164 mutable Nui::val callable_;
165 mutable bool isSet_;
166 };
167
174 static auto getRemoteCallable(std::string name)
175 {
176 return RemoteCallable{std::move(name)};
177 }
178
186 template <typename... ArgsT>
187 static auto call(std::string name, ArgsT&&... args)
188 {
189 return getRemoteCallable(std::move(name))(std::forward<ArgsT>(args)...);
190 }
191
200 template <typename FunctionT, typename... ArgsT>
201 static auto callWithBackChannel(std::string name, FunctionT&& cb, ArgsT&&... args)
202 {
203 return getRemoteCallableWithBackChannel(std::move(name), std::forward<FunctionT>(cb))(
204 std::forward<ArgsT>(args)...);
205 }
206
210 template <typename FunctionT>
211 static auto getRemoteCallableWithBackChannel(std::string name, FunctionT&& func)
212 {
213 return RemoteCallable{std::move(name), registerFunctionOnce(std::forward<FunctionT>(func))};
214 }
215
222 template <typename FunctionT>
223 static std::string registerFunctionOnce(FunctionT&& func)
224 {
225 using namespace std::string_literals;
226 if (Nui::val::global("nui_rpc").isUndefined())
227 {
228 WebApi::Console::error("rpc was not setup by backend"s);
229 return {};
230 }
231 auto tempId = Nui::val::global("nui_rpc")["tempId"].as<uint32_t>() + 1;
232 Nui::val::global("nui_rpc").set("tempId", tempId);
233 const auto tempIdString = "temp_"s + std::to_string(tempId);
234 Nui::val::global("nui_rpc")["frontend"].set(
235 tempIdString,
236 Nui::bind(
237 [funcInner = Detail::FunctionWrapper<FunctionT>::wrapFunction(std::forward<FunctionT>(func)),
238 tempIdString](Nui::val param) mutable {
239 try
240 {
241 funcInner(param);
242 }
243 catch (std::exception const& exc)
244 {
245 // If you see this, never let an exception bubble up from your rpc function!
247 "Caught exception leaving rpc function '{}': {}", tempIdString, exc.what());
248 }
249 catch (...)
250 {
251 // If you see this, never let an exception bubble up from your rpc function!
252 // Also, please only throw things that derive from std::exception!
253 WebApi::Console::error("Caught unknown exception leaving rpc function '{}'", tempIdString);
254 }
255 Nui::val::global("nui_rpc")["frontend"].delete_(tempIdString);
256 },
257 std::placeholders::_1));
258 return tempIdString;
259 }
260
267 template <typename FunctionT>
268 static void registerFunction(std::string const& name, FunctionT&& func)
269 {
270 using namespace std::string_literals;
271 if (Nui::val::global("nui_rpc").isUndefined())
272 {
273 WebApi::Console::error("rpc was not setup by backend"s);
274 return;
275 }
276 Nui::val::global("nui_rpc")["frontend"].set(
277 name.c_str(),
278 Nui::bind(
279 [funcInner = Detail::FunctionWrapper<FunctionT>::wrapFunction(std::forward<FunctionT>(func)),
280 name](Nui::val param) mutable {
281 try
282 {
283 funcInner(param);
284 }
285 catch (std::exception const& exc)
286 {
287 // If you see this, never let an exception bubble up from your rpc function!
288 WebApi::Console::error("Caught exception leaving rpc function '{}': {}", name, exc.what());
289 }
290 catch (...)
291 {
292 // If you see this, never let an exception bubble up from your rpc function!
293 // Also, please only throw things that derive from std::exception!
294 WebApi::Console::error("Caught unknown exception leaving rpc function '{}'", name);
295 }
296 },
297 std::placeholders::_1));
298 }
299
305 static void unregisterFunction(std::string const& name)
306 {
307 using namespace std::string_literals;
308 if (Nui::val::global("nui_rpc").isUndefined())
309 {
310 WebApi::Console::error("rpc was not setup by backend"s);
311 return;
312 }
313 Nui::val::global("nui_rpc")["frontend"].delete_(name.c_str());
314 }
315
316 struct AutoUnregister : public OnDestroy
317 {
319 : OnDestroy{[]() {}}
320 {}
321 explicit AutoUnregister(std::string name)
322 : OnDestroy{[name = std::move(name)]() {
323 unregisterFunction(name);
324 }}
325 {}
326 ~AutoUnregister() = default;
327
330 : OnDestroy{std::move(other)}
331 {}
334 {
335 OnDestroy::operator=(std::move(other));
336 return *this;
337 }
338
339 void reset()
340 {
341 trigger();
342 }
343 };
344
345 template <typename FunctionT>
346 static AutoUnregister autoRegisterFunction(std::string const& name, FunctionT&& func)
347 {
348 registerFunction(name, std::forward<FunctionT>(func));
349 return AutoUnregister{name};
350 }
351 };
352}
Definition on_destroy.hpp:8
Definition rpc_client.hpp:93
RemoteCallable(std::string name)
Definition rpc_client.hpp:131
auto operator()(Args &&... args) const
Definition rpc_client.hpp:96
RemoteCallable(std::string name, std::string backChannel)
Definition rpc_client.hpp:138
bool isValid() const
Definition rpc_client.hpp:121
auto operator()(Nui::val val) const
Definition rpc_client.hpp:108
Definition rpc_client.hpp:90
static void unregisterFunction(std::string const &name)
Unregister a function.
Definition rpc_client.hpp:305
static void registerFunction(std::string const &name, FunctionT &&func)
Register a permanent function that is callable from the backend.
Definition rpc_client.hpp:268
static auto call(std::string name, ArgsT &&... args)
Get a callable remote function and call it immediately.
Definition rpc_client.hpp:187
static auto getRemoteCallableWithBackChannel(std::string name, FunctionT &&func)
Get a callable remote function and register a temporary callable for a response.
Definition rpc_client.hpp:211
static AutoUnregister autoRegisterFunction(std::string const &name, FunctionT &&func)
Definition rpc_client.hpp:346
static std::string registerFunctionOnce(FunctionT &&func)
Registers a single shot function that is removed after it was called.
Definition rpc_client.hpp:223
static auto getRemoteCallable(std::string name)
Get a callable remote function.
Definition rpc_client.hpp:174
static auto callWithBackChannel(std::string name, FunctionT &&cb, ArgsT &&... args)
Get a callable remote function and call it immediately with a callback.
Definition rpc_client.hpp:201
static void error(Args &&... args)
Definition console.hpp:18
static constexpr auto extractJsonMember(nlohmann::json const &json) -> decltype(auto)
Definition rpc_hub.hpp:29
static constexpr auto extractMember(Nui::val const &val) -> decltype(auto)
Definition rpc_client.hpp:24
Definition file_dialog.hpp:6
Nui::val convertToVal(T const &obj)
Converts a boost described class/struct to a Nui::val object.
Definition val_conversion.hpp:402
emscripten::val bind(F &&f, Args &&... args)
Equivalent of std::bind returning a javascript functor.
Definition functions.hpp:98
void convertFromVal(Nui::val const &val, T &obj)
Converts a Nui::val to a class/struct object.
Definition val_conversion.hpp:534
emscripten::val val
Definition val.hpp:5
static constexpr auto wrapFunction(FunctionT &&func)
Definition rpc_client.hpp:52
static constexpr auto wrapFunction(FunctionT &&func)
Definition rpc_client.hpp:64
static constexpr auto wrapFunction(FunctionT &&func)
Definition rpc_client.hpp:40
Definition rpc_hub.hpp:26
Definition rpc_hub.hpp:75
Definition rpc_client.hpp:317
AutoUnregister & operator=(AutoUnregister &&other) noexcept
Definition rpc_client.hpp:333
AutoUnregister & operator=(AutoUnregister const &)=delete
AutoUnregister(std::string name)
Definition rpc_client.hpp:321
void reset()
Definition rpc_client.hpp:339
AutoUnregister(AutoUnregister &&other) noexcept
Definition rpc_client.hpp:329
AutoUnregister(AutoUnregister const &)=delete
AutoUnregister()
Definition rpc_client.hpp:318