Anklang-0.3.0.dev797+g4e3241f3 anklang-0.3.0.dev797+g4e3241f3
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
jsonipc.hh
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#pragma once
3
4#include <rapidjson/document.h>
5#include <rapidjson/stringbuffer.h>
6#include <rapidjson/writer.h>
7#include <stdarg.h>
8#include <cxxabi.h> // abi::__cxa_demangle
9#include <algorithm>
10#include <functional>
11#include <typeindex>
12#include <memory>
13#include <vector>
14#include <unordered_map>
15#include <variant>
16#include <map>
17#include <set>
18
19// Much of the API and some implementation ideas are influenced by https://github.com/pmed/v8pp/ and https://www.jsonrpc.org/.
20
21#define JSONIPC_ISLIKELY(expr) __builtin_expect (bool (expr), 1)
22#define JSONIPC_UNLIKELY(expr) __builtin_expect (bool (expr), 0)
23#define JSONIPC_WARNING(fmt,...) do { fprintf (stderr, "%s:%d: warning: ", __FILE__, __LINE__); fprintf (stderr, fmt, __VA_ARGS__); fputs ("\n", stderr); } while (0)
24#define JSONIPC_ASSERT_RETURN(expr,...) do { if (JSONIPC_ISLIKELY (expr)) break; fprintf (stderr, "%s:%d: assertion failed: %s\n", __FILE__, __LINE__, #expr); return __VA_ARGS__; } while (0)
25
26namespace Jsonipc {
27
28#ifdef JSONIPC_CUSTOM_SHARED_BASE
29using SharedBase = JSONIPC_CUSTOM_SHARED_BASE;
30#else
32struct JsonipcSharedBase : public virtual std::enable_shared_from_this<JsonipcSharedBase> {
33 virtual ~JsonipcSharedBase() {}
34};
36#endif
37
38// == Json types ==
39using JsonValue = rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >;
40using JsonAllocator = rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>;
41using StringBufferWriter = rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfNullFlag>;
42static constexpr const unsigned rapidjson_parse_flags =
43 rapidjson::kParseFullPrecisionFlag |
44 rapidjson::kParseCommentsFlag |
45 rapidjson::kParseTrailingCommasFlag |
46 rapidjson::kParseNanAndInfFlag |
47 rapidjson::kParseEscapedApostropheFlag;
48
49// == C++ Utilities ==
51static inline std::string
52string_format (const char *format, ...)
53{
54 va_list vargs;
55 va_start (vargs, format);
56 static locale_t posix_c_locale = newlocale (LC_ALL_MASK, "C", NULL);
57 locale_t saved_locale = uselocale (posix_c_locale);
58 constexpr const size_t maxlen = 8192;
59 char buffer[maxlen + 1 + 1] = { 0, };
60 vsnprintf (buffer, maxlen, format, vargs);
61 buffer[maxlen] = 0;
62 uselocale (saved_locale);
63 va_end (vargs);
64 return std::string (buffer);
65}
66
68template<bool value> using REQUIRES = typename ::std::enable_if<value, bool>::type;
69
71template<bool value> using REQUIRESv = typename ::std::enable_if<value, void>::type;
72
74template<typename> struct IsSharedPtr : std::false_type {};
75template<typename T> struct IsSharedPtr<std::shared_ptr<T>> : std::true_type {};
76
78static inline constexpr bool
79constexpr_equals (const char *a, const char *b, size_t n)
80{
81 return n == 0 || (a[0] == b[0] && (a[0] == 0 || constexpr_equals (a + 1, b + 1, n - 1)));
82}
83
88static inline std::string
89string_demangle_cxx (const char *mangled_identifier)
90{
91 int status = 0;
92 char *malloced_result = abi::__cxa_demangle (mangled_identifier, NULL, NULL, &status);
93 std::string result = malloced_result && !status ? malloced_result : mangled_identifier;
94 if (malloced_result)
95 free (malloced_result);
96 return result;
97}
98
100template<class T> static inline std::string
101rtti_typename()
102{
103 return string_demangle_cxx (typeid (T).name());
104}
105
107template<class T> static inline std::string
108rtti_typename (T &o)
109{
110 return string_demangle_cxx (typeid (o).name());
111}
112
114template<class T, typename = void> struct DerivesSharedPtr : std::false_type {};
115template<class T> struct DerivesSharedPtr<T, std::void_t< typename T::element_type > > :
116 std::is_base_of< std::shared_ptr<typename T::element_type>, T > {};
117
119template<class, class = void> struct Has_shared_from_this : std::false_type {};
120template<typename T> struct Has_shared_from_this<T, std::void_t< decltype (std::declval<T&>().shared_from_this()) > > :
121 DerivesSharedPtr< decltype (std::declval<T&>().shared_from_this()) > {};
122
124template<class, class = void> struct Has___typename__ : std::false_type {};
125template<typename T> struct Has___typename__<T, std::void_t< decltype (std::declval<const T&>().__typename__()) > > : std::true_type {};
126
128template<class, class = void> struct Has_setget : std::false_type {};
129template<typename T> struct Has_setget<T, std::void_t< decltype (std::declval<T&>().set (std::declval<T&>().get())) > > : std::true_type {};
130
132template<typename T, REQUIRES< Has___typename__<T>::value > = true> static inline std::string
133get___typename__ (const T &o)
134{
135 return o.__typename__();
136}
137template<typename T, REQUIRES< !Has___typename__<T>::value > = true> static inline std::string
138get___typename__ (const T &o)
139{
140 return rtti_typename (o);
141}
142
145 const char* what () const noexcept override { return reason_; }
146 int code () const noexcept { return code_; }
147 explicit bad_invocation (int code, const char *staticreason) noexcept :
148 reason_ (staticreason), code_ (code) {}
149private:
150 const char *const reason_;
151 const int code_;
152};
153
154// == Forward Decls ==
155class InstanceMap;
156template<typename> struct Class;
157
158// == Scope ==
161
163class Scope {
164 InstanceMap &instance_map_;
165 ScopeLocals scope_locals_;
166 ScopeLocalsP localsp_;
167 static std::vector<Scope*>&
168 stack()
169 {
170 static thread_local std::vector<Scope*> stack_;
171 return stack_;
172 }
173 static Scope*
174 head()
175 {
176 auto &stack_ = stack();
177 return stack_.empty() ? nullptr : stack_.back();
178 }
179public:
180 template<typename T> static std::shared_ptr<T>
181 make_shared ()
182 {
183 Scope *scope = head();
184 if (!scope)
185 throw std::logic_error ("Jsonipc::Scope::make_shared(): invalid Scope: nullptr");
187 if (scope)
188 {
189 sptr = std::make_shared<T>();
190 scope->localsp_->push_back (sptr);
191 }
192 return sptr;
193 }
194 static InstanceMap*
195 instance_map ()
196 {
197 Scope *scope = head();
198 if (!scope)
199 throw std::logic_error ("Jsonipc::Scope::instance_map(): invalid Scope: nullptr");
200 return scope ? &scope->instance_map_ : nullptr;
201 }
202 explicit Scope (InstanceMap &instance_map, ScopeLocalsP localsp = {});
203 ~Scope()
204 {
205 auto &stack_ = stack();
206 stack_.erase (std::remove (stack_.begin(), stack_.end(), this), stack_.end());
207 }
208};
209
210// == Convert ==
212template<typename T, typename Enable = void>
213struct Convert;
214
215// int types
216template<typename T>
217struct Convert<T, REQUIRESv< std::is_integral<T>::value > > {
218 static T
219 from_json (const JsonValue &value, T fallback = T())
220 {
221 if (value.IsBool()) return value.GetBool();
222 else if (value.IsInt()) return value.GetInt();
223 else if (value.IsUint()) return value.GetUint();
224 else if (value.IsInt64()) return value.GetInt64();
225 else if (value.IsUint64()) return value.GetUint64();
226 else if (value.IsDouble()) return value.GetDouble();
227 else return fallback; // !IsNumber()
228 }
229 static JsonValue
230 to_json (T i, JsonAllocator &allocator)
231 {
232 return JsonValue (i);
233 }
234};
235
236// bool type
237template<>
238struct Convert<bool> {
239 static bool
240 from_json (const JsonValue &value, bool fallback = bool())
241 {
242 return Convert<uint64_t>::from_json (value, fallback);
243 }
244 static JsonValue
245 to_json (bool b, JsonAllocator &allocator)
246 {
247 return JsonValue (b);
248 }
249};
250
251// floating point types
252template<typename T>
253struct Convert<T, REQUIRESv< std::is_floating_point<T>::value >> {
254 static T
255 from_json (const JsonValue &value, T fallback = T())
256 {
257 if (value.IsBool()) return value.GetBool();
258 else if (value.IsInt()) return value.GetInt();
259 else if (value.IsUint()) return value.GetUint();
260 else if (value.IsInt64()) return value.GetInt64();
261 else if (value.IsUint64()) return value.GetUint64();
262 else if (value.IsDouble()) return value.GetDouble();
263 else return fallback; // !IsNumber()
264 }
265 static JsonValue
266 to_json (T f, JsonAllocator &allocator)
267 {
268 return JsonValue (f);
269 }
270};
271
272// const char* type
273template<>
274struct Convert<const char*> {
275 static const char*
276 from_json (const JsonValue &value, const char *fallback = "")
277 {
278 return value.IsString() ? value.GetString() : fallback;
279 }
280 static JsonValue
281 to_json (const char *str, size_t l, JsonAllocator &allocator)
282 {
283 return str ? JsonValue (str, l, allocator) : JsonValue();
284 }
285 static JsonValue
286 to_json (const char *str, JsonAllocator &allocator)
287 {
288 return str ? JsonValue (str, strlen (str), allocator) : JsonValue();
289 }
290};
291
292// std::string
293template<>
294struct Convert<std::string> {
295 static std::string
296 from_json (const JsonValue &value, const std::string &fallback = std::string())
297 {
298 return value.IsString() ? std::string (value.GetString(), value.GetStringLength()) : fallback;
299 }
300 static JsonValue
301 to_json (const std::string &s, JsonAllocator &allocator)
302 {
303 return JsonValue (s.data(), s.size(), allocator);
304 }
305};
306
308template<class T, typename = void> struct DerivesVector : std::false_type {};
309// Use void_t to prevent errors for T without vector's typedefs
310template<class T> struct DerivesVector<T, std::void_t< typename T::value_type, typename T::allocator_type >> :
311 std::is_base_of< std::vector<typename T::value_type, typename T::allocator_type>, T > {};
312
314template<class T, typename = void> struct DerivesPair : std::false_type {};
315// Use void_t to prevent errors for T without pair's typedefs
316template<class T> struct DerivesPair<T, std::void_t< typename T::first_type, typename T::second_type >> :
317 std::is_base_of< std::pair<typename T::first_type, typename T::second_type>, T > {};
318
319// std::vector
320template<typename T>
321struct Convert<T, REQUIRESv< DerivesVector<T>::value >> {
322 static T
323 from_json (const JsonValue &jarray)
324 {
325 T vec;
326 if (jarray.IsArray())
327 {
328 vec.reserve (jarray.Size());
329 for (size_t i = 0; i < jarray.Size(); i++)
330 vec.emplace_back (Convert<typename T::value_type>::from_json (jarray[i]));
331 }
332 return vec;
333 }
334 static JsonValue
335 to_json (const T &vec, JsonAllocator &allocator)
336 {
337 JsonValue jarray (rapidjson::kArrayType);
338 jarray.Reserve (vec.size(), allocator);
339 for (size_t i = 0; i < vec.size(); ++i)
340 jarray.PushBack (Convert<typename T::value_type>::to_json (vec[i], allocator).Move(), allocator);
341 return jarray;
342 }
343};
344
345// std::pair
346template<typename T>
347struct Convert<T, REQUIRESv< DerivesPair<T>::value >> {
348 static T
349 from_json (const JsonValue &jarray)
350 {
351 T pair = {};
352 if (jarray.IsArray() && jarray.Size() >= 2)
353 {
354 pair.first = Convert<typename T::first_type >::from_json (jarray[0]);
355 pair.second = Convert<typename T::second_type>::from_json (jarray[1]);
356 }
357 return pair;
358 }
359 static JsonValue
360 to_json (const T &pair, JsonAllocator &allocator)
361 {
362 JsonValue jarray (rapidjson::kArrayType);
363 jarray.Reserve (2, allocator);
364 jarray.PushBack (Convert<typename T::first_type >::to_json (pair.first, allocator).Move(), allocator);
365 jarray.PushBack (Convert<typename T::second_type>::to_json (pair.second, allocator).Move(), allocator);
366 return jarray;
367 }
368};
369
370// reference types
371template<typename T>
372struct Convert<T&> : Convert<T> {};
373
374// const reference types
375template<typename T>
376struct Convert<T const&> : Convert<T> {};
377
379template<typename T> static inline auto
380from_json (const JsonValue &value)
381 -> decltype (Convert<T>::from_json (value))
382{
383 return Convert<T>::from_json (value);
384}
385
387template<typename T> static inline auto
388from_json (const JsonValue &value, const T &fallback)
389 -> decltype (Convert<T>::from_json (value, fallback))
390{
391 return Convert<T>::from_json (value, fallback);
392}
393
395template<typename T> static inline JsonValue
396to_json (const T &value, JsonAllocator &allocator)
397{
398 return Convert<T>::to_json (value, allocator);
399}
400
402template<> inline JsonValue
403to_json<const char*> (const char *const &value, JsonAllocator &allocator)
404{
405 return Convert<const char*>::to_json (value, allocator);
406}
407
409template<size_t N> static inline auto
410to_json (const char (&c)[N], JsonAllocator &allocator)
411{
412 return Convert<const char*>::to_json (c, N - 1, allocator);
413}
414template<size_t N> static inline auto
415to_json (const char (&c)[N], size_t l, JsonAllocator &allocator)
416{
417 return Convert<const char*>::to_json (c, l, allocator);
418}
419
421static inline std::string
422jsonvalue_to_string (const JsonValue &value)
423{
424 rapidjson::StringBuffer buffer;
425 StringBufferWriter writer (buffer);
426 value.Accept (writer);
427 const std::string output { buffer.GetString(), buffer.GetSize() };
428 return output;
429}
430
432template<class T1, class T2 = bool, class T3 = bool, class T4 = bool> static inline std::string
433jsonobject_to_string (const char *m1, T1 &&v1, const char *m2 = 0, T2 &&v2 = {},
434 const char *m3 = 0, T3 &&v3 = {}, const char *m4 = 0, T4 &&v4 = {})
435{
436 rapidjson::Document doc (rapidjson::kObjectType);
437 auto &a = doc.GetAllocator();
438 if (m1 && m1[0]) doc.AddMember (JsonValue (m1, a), to_json (v1, a), a);
439 if (m2 && m2[0]) doc.AddMember (JsonValue (m2, a), to_json (v2, a), a);
440 if (m3 && m3[0]) doc.AddMember (JsonValue (m3, a), to_json (v3, a), a);
441 if (m4 && m4[0]) doc.AddMember (JsonValue (m4, a), to_json (v4, a), a);
442 return jsonvalue_to_string (doc);
443}
444
445// == CallbackInfo ==
446struct CallbackInfo;
447using Closure = std::function<void (CallbackInfo&)>;
448
450struct CallbackInfo final {
451 explicit CallbackInfo (const JsonValue &args, JsonAllocator *allocator = nullptr) :
452 args_ (args), doc_ (allocator)
453 {}
454 const JsonValue& ntharg (size_t index) const { static JsonValue j0; return index < args_.Size() ? args_[index] : j0; }
455 size_t n_args () const { return args_.Size(); }
456 Closure* find_closure (const char *methodname);
457 std::string classname (const std::string &fallback) const;
458 JsonAllocator& allocator () { return doc_.GetAllocator(); }
459 void set_result (JsonValue &result) { result_ = result; have_result_ = true; } // move-semantic!
460 JsonValue& get_result () { return result_; }
461 bool have_result () const { return have_result_; }
462 rapidjson::Document& document () { return doc_; }
463private:
464 const JsonValue &args_;
465 JsonValue result_;
466 bool have_result_ = false;
467 rapidjson::Document doc_;
468};
469
470// == FunctionTraits ==
472template<typename F> struct FunctionTraits;
473template<typename R, typename ...Args> struct FunctionTraits<R (Args...)> {
474 using ReturnType = R;
475 using Arguments = std::tuple<Args...>;
476};
477
478// Member function pointer
479template<typename C, typename R, typename ...Args>
480struct FunctionTraits<R (C::*) (Args...)> : FunctionTraits<R (C&, Args...)> {
481 // HAS_THIS = true
482};
483
484// Const member function pointer
485template<typename C, typename R, typename ...Args>
486struct FunctionTraits<R (C::*) (Args...) const> : FunctionTraits<R (const C&, Args...)> {
487 // HAS_THIS = true
488};
489
490// Reference/const callables
491template<typename F>
493template<typename F>
494struct FunctionTraits<const F> : FunctionTraits<F> {};
495
496// Member object pointer
497template<typename C, typename R>
498struct FunctionTraits<R (C::*)> : FunctionTraits<R (C&)> {
499 template<typename D = C> using PointerType = R (D::*);
500};
501
502// == CallTraits ==
504template<typename F>
506 using FuncType = typename std::decay<F>::type;
507 using Arguments = typename FunctionTraits<FuncType>::Arguments;
508 static constexpr const bool HAS_THIS = std::is_member_function_pointer<FuncType>::value;
509 static constexpr const size_t N_ARGS = std::tuple_size<Arguments>::value - HAS_THIS;
510
511 // Argument type via INDEX
512 template<size_t INDEX, bool> struct TupleElement { using Type = typename std::tuple_element<INDEX, Arguments>::type; };
513 template<size_t INDEX> struct TupleElement<INDEX, false> { using Type = void; }; // void iff INDEX out of range
514 template<size_t INDEX> using ArgType = typename TupleElement<HAS_THIS + INDEX, (INDEX < N_ARGS)>::Type;
515 template<size_t INDEX> using ConvertType = decltype (Convert<ArgType<INDEX>>::from_json (std::declval<JsonValue>()));
516
517 template<size_t INDEX> static ConvertType<INDEX>
518 arg_from_json (const CallbackInfo &args)
519 {
520 return Convert<ArgType<INDEX>>::from_json (args.ntharg (HAS_THIS + INDEX));
521 }
522 template<typename T, size_t ...INDICES> static typename FunctionTraits<F>::ReturnType
523 call_unpacked (T &obj, const F &func, const CallbackInfo &args, std::index_sequence<INDICES...>)
524 {
525 return (obj.*func) (arg_from_json<INDICES> (args)...);
526 }
527};
528
529// == call_from_json ==
530template<typename T, typename F> static inline typename FunctionTraits<F>::ReturnType
531call_from_json (T &obj, const F &func, const CallbackInfo &args)
532{
533 using CallTraits = CallTraits<F>;
534 return CallTraits::call_unpacked (obj, func, args, std::make_index_sequence<CallTraits::N_ARGS>());
535}
536
537// == InstanceMap ==
539 friend class Scope;
540 struct TypeidKey {
541 const std::type_index tindex;
542 void *ptr;
543 bool
544 operator< (const TypeidKey &other) const noexcept
545 {
546 return tindex < other.tindex || (tindex == other.tindex && ptr < other.ptr);
547 }
548 };
549public:
550 class Wrapper {
551 virtual TypeidKey typeid_key () = 0;
552 virtual ~Wrapper () {}
553 friend class InstanceMap;
554 public:
555 virtual Closure* lookup_closure (const char *method) = 0;
556 virtual void try_upcast (const std::string &baseclass, void *sptrB) = 0;
557 virtual std::string classname () = 0;
558 };
559 using CreateWrapper = Wrapper* (*) (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth);
560 static std::vector<CreateWrapper>& wrapper_creators() { static std::vector<CreateWrapper> wrapper_creators_; return wrapper_creators_; }
561 static void
562 register_wrapper (CreateWrapper createwrapper)
563 {
564 wrapper_creators().push_back (createwrapper);
565 }
566 template<typename T>
567 class InstanceWrapper : public Wrapper {
568 std::shared_ptr<T> sptr_;
569 virtual
571 {
572 // printf ("InstanceMap::Wrapper: %s: deleting %s wrapper: %p\n", __func__, rtti_typename<T>().c_str(), sptr_.get());
573 }
574 public:
575 explicit InstanceWrapper (const std::shared_ptr<T> &sptr) : sptr_ (sptr) {}
576 Closure* lookup_closure (const char *method) override { return Class<T>::lookup_closure (method); }
577 TypeidKey typeid_key () override { return create_typeid_key (sptr_); }
578 void try_upcast (const std::string &baseclass, void *sptrB) override
579 { Class<T>::try_upcast (sptr_, baseclass, sptrB); }
580 static TypeidKey
581 create_typeid_key (const std::shared_ptr<T> &sptr)
582 {
583 return { typeid (T), sptr.get() };
584 }
586 classname() override
587 {
588 return Class<T>::classname();
589 }
590 };
593 using IdSet = std::set<size_t>;
594 WrapperMap wmap_;
595 TypeidMap typeid_map_;
596 IdSet *idset_ = nullptr;
597 static size_t next_counter() { static size_t counter_ = 0; return ++counter_; }
598 bool
599 delete_id (size_t thisid)
600 {
601 const auto w = wmap_.find (thisid);
602 if (w != wmap_.end())
603 {
604 Wrapper *wrapper = w->second;
605 wmap_.erase (w);
606 const auto t = typeid_map_.find (wrapper->typeid_key());
607 if (t != typeid_map_.end())
608 typeid_map_.erase (t);
609 delete wrapper;
610 if (idset_)
611 idset_->erase (thisid);
612 return true;
613 }
614 return false;
615 }
616public:
617 bool
618 mark_unused()
619 {
620 if (idset_)
621 return false;
622 idset_ = new IdSet();
623 return true;
624 }
625 size_t
626 purge_unused (const std::vector<size_t> &unused)
627 {
628 IdSet preserve;
629 if (idset_)
630 {
631 idset_->swap (preserve);
632 delete idset_;
633 idset_ = nullptr;
634 }
635 auto contains = [] (const auto &c, const auto &e) {
636 return c.end() != c.find (e);
637 };
638 size_t preserved = 0;
639 for (const size_t id : unused)
640 if (!contains (preserve, id))
641 delete_id (id);
642 else
643 preserved++;
644 return preserved;
645 }
646 bool
647 empty() const
648 {
649 return wmap_.empty();
650 }
651 size_t
652 size() const
653 {
654 return wmap_.size();
655 }
656 void
657 clear (const bool printdebug = false)
658 {
659 if (idset_)
660 {
661 delete idset_;
662 idset_ = nullptr;
663 }
664 WrapperMap old;
665 std::swap (old, wmap_);
666 typeid_map_.clear();
667 for (auto &pair : old)
668 {
669 Wrapper *wrapper = pair.second;
670 if (printdebug)
671 fprintf (stderr, "Jsonipc::~Wrapper: %s: $id=%zu\n", string_demangle_cxx (wrapper->typeid_key().tindex.name()).c_str(), pair.first);
672 delete wrapper;
673 }
674 }
675 virtual
676 ~InstanceMap()
677 {
678 clear();
679 JSONIPC_ASSERT_RETURN (wmap_.size() == 0); // deleters shouldn't re-add
680 JSONIPC_ASSERT_RETURN (typeid_map_.size() == 0); // deleters shouldn't re-add
681 }
682 virtual JsonValue
683 wrapper_to_json (Wrapper *wrapper, const size_t thisid, JsonAllocator &allocator)
684 {
685 if (!wrapper)
686 return JsonValue(); // null
687 JsonValue jobject (rapidjson::kObjectType);
688 jobject.AddMember ("$id", thisid, allocator);
689 jobject.AddMember ("$class", JsonValue (wrapper->classname().c_str(), allocator), allocator);
690 return jobject;
691 }
692 template<typename T> static JsonValue
693 scope_wrap_object (const std::shared_ptr<T> &sptr, JsonAllocator &allocator)
694 {
695 InstanceMap *imap = Scope::instance_map();
696 size_t thisid = 0;
697 Wrapper *wrapper = nullptr;
698 if (sptr.get())
699 {
700 const TypeidKey tkey = InstanceWrapper<T>::create_typeid_key (sptr);
701 auto it = imap->typeid_map_.find (tkey);
702 if (it == imap->typeid_map_.end())
703 {
704 thisid = next_counter();
705 if constexpr (std::is_base_of_v<SharedBase, T>) {
706 std::vector<CreateWrapper> &wcreators = wrapper_creators(); // using CreateWrapper = Wrapper* (*) (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth);
707 size_t basedepth = 0;
708 for (size_t i = 0; i < wcreators.size(); i++) {
709 Wrapper *w = wcreators[i] (sptr, basedepth);
710 if (w) {
711 delete wrapper;
712 wrapper = w;
713 }
714 }
715 }
716 if (!wrapper)
717 wrapper = new InstanceWrapper<T> (sptr);
718 imap->wmap_[thisid] = wrapper;
719 imap->typeid_map_[tkey] = thisid;
720 }
721 else
722 {
723 thisid = it->second;
724 auto wt = imap->wmap_.find (thisid);
725 wrapper = wt != imap->wmap_.end() ? wt->second : nullptr;
726 }
727 }
728 if (imap->idset_)
729 imap->idset_->insert (thisid);
730 /* A note about TypeidKey:
731 * Two tuples (TypeX,ptr0x123) and (TypeY,ptr0x123) holding the same pointer address can
732 * occur if the RTII lookup to determine the actual Wrapper class fails, e.g. when
733 * Class<MostDerived> is unregisterd. In this case, ptr0x123 can be wrapped multiple
734 * times through different base classes.
735 */
736 return imap->wrapper_to_json (wrapper, thisid, allocator);
737 }
738 virtual Wrapper*
739 wrapper_from_json (const JsonValue &value)
740 {
741 if (value.IsObject())
742 {
743 auto it = value.FindMember ("$id");
744 if (it != value.MemberEnd())
745 {
746 const size_t thisid = Convert<size_t>::from_json (it->value);
747 if (thisid)
748 {
749 auto tit = wmap_.find (thisid);
750 if (tit != wmap_.end())
751 return tit->second;
752 }
753 }
754 }
755 return nullptr;
756 }
757 static Wrapper*
758 scope_lookup_wrapper (const JsonValue &value)
759 {
760 InstanceMap *imap = Scope::instance_map();
761 return imap ? imap->wrapper_from_json (value) : nullptr;
762 }
763 static bool
764 scope_forget_id (size_t thisid)
765 {
766 InstanceMap *imap = Scope::instance_map();
767 return imap->delete_id (thisid);
768 }
769};
770
771inline Closure*
772CallbackInfo::find_closure (const char *methodname)
773{
774 const JsonValue &value = ntharg (0);
775 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
776 return iw ? iw->lookup_closure (methodname) : nullptr;
777}
778
779inline std::string
780CallbackInfo::classname (const std::string &fallback) const
781{
782 const JsonValue &value = ntharg (0);
783 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
784 return iw ? iw->classname() : fallback;
785}
786
787inline
788Scope::Scope (InstanceMap &instance_map, ScopeLocalsP localsp) :
789 instance_map_ (instance_map), localsp_ (localsp ? localsp : ScopeLocalsP (&scope_locals_, [] (ScopeLocals*) {}))
790{
791 auto &stack_ = stack();
792 stack_.push_back (this);
793}
794
795// == DefaultConstant ==
799 DefaultConstant () = default;
800 template<typename T, REQUIRES< std::is_integral<T>::value && std::is_unsigned<T>::value> = true>
802 template<typename T, REQUIRES< std::is_integral<T>::value && !std::is_unsigned<T>::value> = true>
804 template<typename T, REQUIRES< std::is_convertible<T, double>::value && !std::is_integral<T>::value> = true>
805 DefaultConstant (T a) : DefaultConstantVariant (double (a)) {}
806 template<typename T, REQUIRES< std::is_convertible<T, std::string>::value && !std::is_same<T, std::nullptr_t>::value> = true>
808 template<typename T, REQUIRES< std::is_same<T, std::nullptr_t>::value> = true>
810};
812
813// == normalize_typename ==
815inline std::string
817{
818 std::string normalized;
819 auto is_identifier_char = [] (int ch) {
820 return ( (ch >= 'A' && ch <= 'Z') ||
821 (ch >= 'a' && ch <= 'z') ||
822 (ch >= '0' && ch <= '9') ||
823 ch == '_' || ch == '$' );
824 };
825 for (size_t i = 0; i < string.size() && string[i]; ++i)
826 if (is_identifier_char (string[i]))
827 normalized += string[i];
828 else if (normalized.size() && normalized[normalized.size() - 1] != '.')
829 normalized += '.';
830 return normalized;
831}
832
833// == JavaScript Helpers ==
835template<class V> static inline unsigned
836js_initializer_index ()
837{
838 using T = std::decay_t<V>;
839 if constexpr (std::is_same<T,bool>::value) return 3;
840 if constexpr (std::is_integral<T>::value) return 1;
841 if constexpr (std::is_floating_point<T>::value) return 2;
844 if constexpr (std::is_enum<T>::value) return 4; // [4]='' [1]=0
845 if constexpr (DerivesVector<T>::value) return 5;
846 if constexpr (DerivesPair<T>::value) return 5;
847 if constexpr (!IsSharedPtr<T>::value && std::is_class<T>::value) return 6;
848 return 0;
849}
850static constexpr const char *const js_initializers[] = { "null", "0", "0.0", "false", "''", "[]", "{}" };
851
852// == TypeScript Type Mapping ==
853inline std::string
854short_name (const std::string &full_name)
855{
856 const size_t last_colon = full_name.rfind ("::");
857 return last_colon == std::string::npos ? full_name : full_name.substr (last_colon + 2);
858}
859template<typename T> struct typescript_name {
860 static std::string name() { return short_name (rtti_typename<T>()); }
861};
862template<typename T> struct typescript_name<T&> : typescript_name<T> {};
863template<typename T> struct typescript_name<const T&> : typescript_name<T> {};
864template<typename T> struct typescript_name<std::shared_ptr<T>> : typescript_name<T> {};
865template<typename T>
866struct typescript_name<T*> {
867 static std::string name() { return typescript_name<T>::name() + " | null"; }
868};
869template<typename T1, typename T2>
870struct typescript_name<std::pair<T1, T2>> {
871 static std::string name() { return "[" + typescript_name<T1>::name() + ", " + typescript_name<T2>::name() + "]"; }
872};
873template<typename T>
875 static std::string name() { return typescript_name<T>::name() + "[]"; }
876};
877template<typename T>
878struct typescript_name<std::map<std::string, T>> {
879 static std::string name() { return "{ [key: string]: " + typescript_name<T>::name() + " }"; }
880};
881template<typename T>
882struct typescript_name<std::unordered_map<std::string, T>> {
883 static std::string name() { return "{ [key: string]: " + typescript_name<T>::name() + " }"; }
884};
885#define JSONIPC_MAP_TO_TYPESCRIPT(CXXTYPE,TSTYPE) \
886 template<> struct ::Jsonipc::typescript_name< CXXTYPE > { static std::string name() { return TSTYPE; } }
887
888template<typename... Args> std::string
889typescript_arg_list()
890{
891 std::string s;
892 int i = 0;
893 auto print_one_arg = [&] (const std::string &type_name) {
894 if (i > 0) s += ", ";
895 s += string_format ("arg%d: %s", ++i, type_name.c_str());
896 };
897 (print_one_arg (typescript_name<Args>::name()), ...); // C++17 fold expr
898 return s;
899}
900template<typename... Args> std::string
901typescript_arg_names_list()
902{
903 std::string s;
904 int i = 0;
905 auto append_arg_name = [&] (const std::string &type_name) {
906 s += string_format (", arg%d", ++i);
907 };
908 (append_arg_name (typescript_name<Args>::name()), ...); // C++17 fold expr
909 return s;
910}
911template<typename C, typename R, typename... Args> std::string
912typescript_call_impl (const std::string &method_name)
913{
914 std::string s;
915 s += string_format (" %s (", method_name.c_str());
916 s += typescript_arg_list<Args...>();
917 s += string_format ("): Promise<%s>\n", typescript_name<R>::name().c_str());
918 s += string_format (" { return Jsonipc.send (\"%s\", [this%s]); }\n",
919 method_name.c_str(), typescript_arg_names_list<Args...>().c_str());
920 return s;
921}
922template<typename T, typename Ret, typename... Args> std::string
923typescript_call (const std::string &method_name, Ret (T::*func) (Args...))
924{
925 return typescript_call_impl<T, Ret, Args...> (method_name);
926}
927template<typename T, typename Ret, typename... Args> std::string // const overload
928typescript_call (const std::string &method_name, Ret (T::*func) (Args...) const)
929{
930 return typescript_call_impl<T, Ret, Args...> (method_name);
931}
932
933// == BindingPrinter ==
935 enum Kind { ANY, ENUM, VALUE, RECORD, FIELD, CLASS, METHOD };
936 std::string b_;
937 std::string open_enum_, open_record_, open_class_;
939 size_t class_inherit_pos_ = 0;
940 void
941 close()
942 {
943 if (open_enum_.size())
944 close_enum();
945 if (open_record_.size())
946 close_record();
947 if (open_class_.size())
948 close_class();
949 }
950 template<class, class = void> struct has_nested_T : std::false_type {}; // Check for nested ::T type
951 template<typename U> struct has_nested_T<U, std::void_t<typename U::T>> : std::true_type {};
952 template<typename M> struct typescript_call_from_type; // Typescript signature from member function pointer
953 template<typename C, typename R, typename... Args>
954 struct typescript_call_from_type<R (C::*)(Args...)> {
955 static std::string
956 generate (const std::string &method_name)
957 {
958 return typescript_call_impl<C, R, Args...> (method_name);
959 }
960 };
961 template<typename C, typename R, typename... Args>
962 struct typescript_call_from_type<R (C::*)(Args...) const> {
963 static std::string
964 generate (const std::string &method_name)
965 {
966 return typescript_call_impl<C, R, Args...>(method_name);
967 }
968 };
969public:
970 std::string finish() { close(); return b_; }
971 template<typename T> void
972 enum_type()
973 {
974 close();
975 open_enum_ = rtti_typename<typename std::decay<T>::type>();
976 b_ += "export const " + short_name (open_enum_) + " = { // " + open_enum_ + "\n";
977 }
978 template<typename T> void
979 enum_value (const std::string &name, T v)
980 {
981 const std::string full_js_name = normalize_typename (open_enum_) + "." + name;
982 using underlying = typename std::underlying_type<T>::type;
983 b_ += " " + name + ": \"" + full_js_name + "\", // " + std::to_string(static_cast<underlying>(v)) + "\n";
984 }
985 void
986 close_enum()
987 {
988 b_ += "} as const;\n";
989 const std::string shortname = short_name (open_enum_);
990 b_ += "export type " + shortname + " = typeof " + shortname + "[keyof typeof " + shortname + "];\n";
991 b_ += "Jsonipc.classes[\"" + open_enum_ + "\"] = " + shortname + ";\n\n";
992 open_enum_.clear();
993 }
994 template<typename T> void
995 record_type()
996 {
997 close();
998 open_record_ = rtti_typename<typename std::decay<T>::type>();
999 b_ += "export class " + short_name (open_record_) + " { // " + open_record_ + "\n";
1000 }
1001 template<typename T, typename A> void
1002 field_member (const std::string &name)
1003 {
1004 const std::string ts_type_name = typescript_name<A>::name();
1005 const std::string default_value = js_initializers[js_initializer_index<A>()];
1006 const std::string as_cast = std::is_enum<A>::value ? ts_type_name : "";
1007 record_fields_.emplace_back (name, ts_type_name, default_value, as_cast);
1008 }
1009 void
1010 close_record()
1011 {
1012 for (const auto &[field_name, ts_type_name, default_value, as_cast] : record_fields_)
1013 b_ += " " + field_name + ": " + ts_type_name + ";\n";
1014 b_ += " constructor (";
1015 for (size_t i = 0; i < record_fields_.size(); i++) {
1016 const auto &[field_name, ts_type_name, default_value, as_cast] = record_fields_[i];
1017 b_ += (i ? ", " : "") + field_name + ": " + ts_type_name + " = " + default_value;
1018 if (as_cast.size())
1019 b_ += " as " + as_cast;
1020 }
1021 b_ += ")\n {\n";
1022 for (const auto &[field_name, ts_type_name, default_value, as_cast] : record_fields_)
1023 b_ += " this." + field_name + " = " + field_name + ";\n";
1024 b_ += " }\n";
1025 b_ += "};\n";
1026 const std::string shortname = short_name (open_record_);
1027 b_ += "Jsonipc.classes[\"" + open_record_ + "\"] = " + shortname + ";\n\n";
1028 record_fields_.clear();
1029 open_record_.clear();
1030 }
1031 template<typename T> void
1032 class_type()
1033 {
1034 close();
1035 open_class_ = rtti_typename<typename std::decay<T>::type>();
1036 const std::string shortname = short_name (open_class_);
1037 b_ += "export class " + shortname + " // " + open_class_ + "\n";
1038 class_inherit_pos_ = b_.size();
1039 b_ += "{\n";
1040 b_ += " constructor ($id)\n";
1041 b_ += " { super ($id); if (new.target === " + shortname + ") Jsonipc.ofreeze (this); }\n";
1042 }
1043 template<typename B> void
1044 inherit_type()
1045 {
1046 const std::string base_class_ = rtti_typename<typename std::decay<B>::type>();
1047 b_.insert (class_inherit_pos_, " extends Jsonipc.classes[\"" + base_class_ + "\"]\n");
1048 }
1049 template<typename T, typename M> void
1050 method_member (const std::string &name)
1051 {
1052 b_ += typescript_call_from_type<M>::generate (name);
1053 }
1054 template<typename T, typename R, typename A> void
1055 field_accessor (const std::string &name)
1056 {
1057 const std::string ts_type = typescript_name<R>::name();
1058 b_ += " get " + name + " (): " + ts_type + "\n";
1059 b_ += " { return Jsonipc.get_reactive_prop.call (this, \"" + name + "\", " + js_initializers[js_initializer_index<R>()] + ") as " + ts_type + "; }\n";
1060 b_ += " set " + name + " (v: " + ts_type + ")\n";
1061 b_ += " { Jsonipc.send ('set/' + '" + name + "', [this, v]); }\n";
1062 }
1063 void
1064 close_class()
1065 {
1066 b_ += "};\n";
1067 const std::string shortname = short_name (open_class_);
1068 b_ += "Jsonipc.classes[\"" + open_class_ + "\"] = " + shortname + ";\n\n";
1069 open_class_.clear();
1070 }
1071};
1072inline BindingPrinter *g_binding_printer = nullptr;
1073
1074// == TypeInfo ==
1076protected:
1077 virtual ~TypeInfo() {}
1078 explicit TypeInfo() {}
1079};
1080
1081// == Enum ==
1082template<typename T>
1083struct Enum final : TypeInfo {
1084 static_assert (std::is_enum<T>::value, "");
1085 Enum ()
1086 {
1087 if (JSONIPC_UNLIKELY (g_binding_printer))
1088 g_binding_printer->enum_type<T> ();
1089 }
1090 using UnderlyingType = typename std::underlying_type<T>::type;
1091 Enum&
1092 set (T v, const char *valuename)
1093 {
1094 const std::string class_name = typename_of<T>();
1095 auto &entries_ = entries();
1096 auto normalized_typename = normalize_typename (class_name + "." + valuename);
1097 Entry e { normalized_typename, v };
1098 entries_.push_back (e);
1099 if (JSONIPC_UNLIKELY (g_binding_printer))
1100 g_binding_printer->enum_value<T> (valuename, v);
1101 return *this;
1102 }
1103 static bool
1104 has_names ()
1105 {
1106 return !entries().empty();
1107 }
1108 static const std::string&
1109 get_name (T v)
1110 {
1111 const auto &entries_ = entries();
1112 for (const auto &e : entries_)
1113 if (v == e.value)
1114 return e.name;
1115 static const std::string empty;
1116 return empty;
1117 }
1118 static T
1119 get_value (const std::string &name, T fallback)
1120 {
1121 auto c_isalnum = [] (char c) {
1122 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
1123 };
1124 const auto &entries_ = entries();
1125 for (const auto &e : entries_)
1126 if (name == e.name || // exact match, or
1127 (name.size() < e.name.size() && // name starts at e.name word boundary
1128 !c_isalnum (e.name[e.name.size() - name.size() - 1]) && // and matches the tail of e.name
1129 e.name.compare (e.name.size() - name.size(), name.size(), name) == 0))
1130 return e.value;
1131 return fallback;
1132 }
1134 static EnumValueS
1135 list_values ()
1136 {
1137 EnumValueS enumvalues;
1138 const auto &entries_ = entries();
1139 for (const auto &e : entries_)
1140 enumvalues.push_back ({ int64_t (e.value), e.name });
1141 return enumvalues;
1142 }
1143private:
1144 struct Entry { const std::string name; T value; };
1145 static std::vector<Entry>& entries() { static std::vector<Entry> entries_; return entries_; }
1146 template<typename U> static std::string
1147 typename_of()
1148 {
1149 using Type = typename std::decay<U>::type;
1150 return rtti_typename<Type>();
1151 }
1152};
1153
1154// enum types
1155template<typename T>
1156struct Convert<T, REQUIRESv< std::is_enum<T>::value > > {
1157 using UnderlyingType = typename std::underlying_type<T>::type;
1158 static T
1159 from_json (const JsonValue &value, T fallback = T())
1160 {
1161 if (value.IsString())
1162 {
1163 using EnumType = Enum<T>;
1164 const std::string string = Convert<std::string>::from_json (value);
1165 return EnumType::get_value (string, fallback);
1166 }
1167 return T (Convert<UnderlyingType>::from_json (value, UnderlyingType (fallback)));
1168 }
1169 static JsonValue
1170 to_json (T evalue, JsonAllocator &allocator)
1171 {
1172 using EnumType = Enum<T>;
1173 if (EnumType::has_names())
1174 {
1175 const std::string &name = EnumType::get_name (evalue);
1176 if (!name.empty())
1177 return Convert<std::string>::to_json (name, allocator);
1178 }
1179 return Convert<UnderlyingType>::to_json (UnderlyingType (evalue), allocator);
1180 }
1181};
1182
1183// == Serializable ==
1185template<typename T>
1186struct Serializable final : TypeInfo {
1189 {
1190 make_serializable<T>();
1191 if (JSONIPC_UNLIKELY (g_binding_printer))
1192 g_binding_printer->record_type<T>();
1193 }
1195 template<typename A, REQUIRES< std::is_member_object_pointer<A>::value > = true> Serializable&
1196 set (const char *name, A attribute)
1197 {
1198 using SetterAttributeType = typename FunctionTraits<A>::ReturnType;
1199 Accessors accessors;
1200 accessors.setter = [attribute] (T &obj, const JsonValue &value) -> void { obj.*attribute = from_json<SetterAttributeType> (value); };
1201 accessors.getter = [attribute] (const T &obj, JsonAllocator &a) -> JsonValue { return to_json (obj.*attribute, a); };
1202 AccessorMap &amap = accessormap();
1203 auto it = amap.find (name);
1204 if (it != amap.end())
1205 throw std::runtime_error ("duplicate attribute registration: " + std::string (name));
1206 amap.insert (std::make_pair<std::string, Accessors> (name, std::move (accessors)));
1207 const std::string class_name = rtti_typename<T>();
1208 if (JSONIPC_UNLIKELY (g_binding_printer))
1209 g_binding_printer->field_member<T,SetterAttributeType> (name);
1210 return *this;
1211 }
1212 static bool is_serializable () { return serialize_from_json_() && serialize_to_json_(); }
1213 static JsonValue serialize_to_json (const T &o, JsonAllocator &a) { return serialize_to_json_() (o, a); }
1214 static std::shared_ptr<T> serialize_from_json (const JsonValue &value,
1215 const std::shared_ptr<T> &p = 0) { return serialize_from_json_() (value, p); }
1216private:
1217 struct Accessors {
1218 std::function<void (T&,const JsonValue&)> setter;
1219 std::function<JsonValue (const T&, JsonAllocator&)> getter;
1220 };
1221 using AccessorMap = std::map<std::string, Accessors>;
1222 static AccessorMap& accessormap() { static AccessorMap amap; return amap; }
1223 template<typename U> static void
1224 make_serializable()
1225 {
1226 // implement serialize_from_json by calling all setters
1227 SerializeFromJson sfj = [] (const JsonValue &value, const std::shared_ptr<T> &p) -> std::shared_ptr<T> {
1228 std::shared_ptr<T> obj = p ? p : Scope::make_shared<T>();
1229 if (!obj)
1230 return obj;
1231 AccessorMap &amap = accessormap();
1232 for (const auto &field : value.GetObject())
1233 {
1234 const std::string field_name = field.name.GetString();
1235 auto it = amap.find (field_name);
1236 if (it == amap.end())
1237 continue;
1238 Accessors &accessors = it->second;
1239 accessors.setter (*obj, field.value);
1240 }
1241 return obj;
1242 };
1243 serialize_from_json_() = sfj;
1244 // implement serialize_to_json by calling all getters
1245 SerializeToJson stj = [] (const T &object, JsonAllocator &allocator) -> JsonValue {
1246 JsonValue jobject (rapidjson::kObjectType); // serialized result
1247 AccessorMap &amap = accessormap();
1248 for (auto &it : amap)
1249 {
1250 const std::string field_name = it.first;
1251 Accessors &accessors = it.second;
1252 JsonValue result = accessors.getter (object, allocator);
1253 jobject.AddMember (JsonValue (field_name.c_str(), allocator), result, allocator);
1254 }
1255 return jobject;
1256 };
1257 serialize_to_json_() = stj;
1258 }
1259 using SerializeFromJson = std::function<std::shared_ptr<T> (const JsonValue&, const std::shared_ptr<T>&)>;
1260 using SerializeToJson = std::function<JsonValue (const T&, JsonAllocator&)>;
1261 static SerializeFromJson& serialize_from_json_ () { static SerializeFromJson impl; return impl; }
1262 static SerializeToJson& serialize_to_json_ () { static SerializeToJson impl; return impl; }
1263};
1264
1265// == Class ==
1266template<typename T>
1267struct Class final : TypeInfo {
1268 Class (bool internal = false)
1269 {
1270 auto create_wrapper = [] (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth) -> InstanceMap::Wrapper*
1271 {
1272 /* This is an exhaustive search for the best (most derived) wrapper type for
1273 * an object. We currently use a linear search that can involve as many
1274 * dynamic casts as the inheritance depth of the registered wrappers.
1275 */
1276 const size_t class_depth = Class::base_depth();
1277 if (class_depth > basedepth) {
1279 if (derived_sptr.get()) {
1280 basedepth = class_depth;
1281 return new InstanceMap::InstanceWrapper<T> (derived_sptr);
1282 }
1283 }
1284 return nullptr;
1285 };
1286 InstanceMap::register_wrapper (create_wrapper);
1287 if (!internal && JSONIPC_UNLIKELY (g_binding_printer))
1288 g_binding_printer->class_type<T>();
1289 }
1290 // Inherit base class `B`
1291 template<typename B> Class&
1292 inherit()
1293 {
1294 add_base<B>();
1295 if (JSONIPC_UNLIKELY (g_binding_printer))
1296 g_binding_printer->inherit_type<B>();
1297 return *this;
1298 }
1300 template<typename F, REQUIRES< std::is_member_function_pointer<F>::value > = true> Class&
1301 set (const char *name, const F &method)
1302 {
1303 add_member_function_closure (name, make_closure (method));
1304 if (JSONIPC_UNLIKELY (g_binding_printer))
1305 g_binding_printer->method_member<T,F> (name);
1306 return *this;
1307 }
1309 template<typename R, typename A, typename C, typename VB> Class&
1310 set (const char *name, R (C::*get) () const, VB (C::*set) (A))
1311 {
1313 JSONIPC_ASSERT_RETURN (get && set, *this);
1314 add_member_function_closure (std::string ("get/") + name, make_closure (get));
1315 add_member_function_closure (std::string ("set/") + name, make_closure (set));
1316 if (JSONIPC_UNLIKELY (g_binding_printer))
1317 g_binding_printer->field_accessor<T,R,A> (name);
1318 return *this;
1319 }
1320 template<typename F, REQUIRES< std::is_member_function_pointer<F>::value > = true> Class&
1321 set_d (const char *name, const F &method, const DefaultsList &dflts)
1322 {
1323 constexpr const size_t N_ARGS = CallTraits<F>::N_ARGS;
1324 JSONIPC_ASSERT_RETURN (dflts.size() <= N_ARGS, *this);
1325 add_member_function_closure (name, make_closure (method));
1326 if (JSONIPC_UNLIKELY (g_binding_printer))
1327 g_binding_printer->method_member<T,F> (name);
1328 return *this;
1329 }
1330 static std::string
1331 classname ()
1332 {
1333 return typename_of<T>();
1334 }
1335 static std::shared_ptr<T>
1336 object_from_json (const JsonValue &value)
1337 {
1338 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
1339 if (iw)
1340 {
1341 std::shared_ptr<T> base_sptr = nullptr;
1342 iw->try_upcast (classname(), &base_sptr);
1343 if (base_sptr)
1344 return base_sptr;
1345 }
1346 return nullptr;
1347 }
1348private:
1349 template<typename U> static std::string
1350 typename_of()
1351 {
1352 using Type = typename std::decay<U>::type;
1353 return rtti_typename<Type>();
1354 }
1355 template<typename F>
1357 template<typename F, REQUIRES< HasVoidReturn<F>::value > = true> Closure
1358 make_closure (const F &method)
1359 {
1360 return [method] (const CallbackInfo &cbi) {
1361 const bool HAS_THIS = true;
1362 if (HAS_THIS + CallTraits<F>::N_ARGS != cbi.n_args())
1363 throw Jsonipc::bad_invocation (-32602, "Invalid params: wrong number of arguments");
1364 std::shared_ptr<T> instance = object_from_json (cbi.ntharg (0));
1365 if (!instance)
1366 throw Jsonipc::bad_invocation (-32603, "Internal error: closure without this");
1367 call_from_json (*instance, method, cbi);
1368 };
1369 }
1370 template<typename F, REQUIRES< !HasVoidReturn<F>::value > = true> Closure
1371 make_closure (const F &method)
1372 {
1373 return [method] (CallbackInfo &cbi) {
1374 const bool HAS_THIS = true;
1375 if (HAS_THIS + CallTraits<F>::N_ARGS != cbi.n_args())
1376 throw Jsonipc::bad_invocation (-32602, "Invalid params: wrong number of arguments");
1377 std::shared_ptr<T> instance = object_from_json (cbi.ntharg (0));
1378 if (!instance)
1379 throw Jsonipc::bad_invocation (-32603, "Internal error: closure without this");
1380 JsonValue rv;
1381 rv = to_json (call_from_json (*instance, method, cbi), cbi.allocator());
1382 cbi.set_result (rv);
1383 };
1384 }
1385 void
1386 add_member_function_closure (const std::string &name, Closure &&closure)
1387 {
1388 MethodMap &mmap = methodmap();
1389 auto it = mmap.find (name);
1390 if (it != mmap.end())
1391 throw std::runtime_error ("duplicate method registration: " + name);
1392 mmap.insert (std::make_pair<std::string, Closure> (name.c_str(), std::move (closure)));
1393 }
1394 using MethodMap = std::map<std::string, Closure>;
1395 static MethodMap& methodmap() { static MethodMap methodmap_; return methodmap_; }
1396 struct BaseInfo {
1397 std::string basetypename;
1398 size_t (*base_depth) ();
1399 bool (*upcast_impl) (const std::shared_ptr<T>&, const std::string&, void*) = NULL;
1400 Closure* (*lookup_closure) (const char*) = NULL;
1401 };
1402 using BaseVec = std::vector<BaseInfo>;
1403 template<typename B> void
1404 add_base ()
1405 {
1406 BaseVec &bvec = basevec();
1407 BaseInfo binfo { typename_of<B>(), Class<B>::base_depth, &upcast_impl<B>, &Class<B>::lookup_closure, };
1408 for (const auto &it : bvec)
1409 if (it.basetypename == binfo.basetypename)
1410 throw std::runtime_error ("duplicate base registration: " + binfo.basetypename);
1411 bvec.push_back (binfo);
1412 Class<B> bclass (true); // internal=true; force registration for base_depth
1413 }
1414 static BaseVec& basevec () { static BaseVec basevec_; return basevec_; }
1415 template<typename B> static bool
1416 upcast_impl (const std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
1417 {
1418 std::shared_ptr<B> bptr = sptr;
1419 return Class<B>::try_upcast (bptr, baseclass, sptrB);
1420 }
1421public:
1422 static size_t
1423 base_depth ()
1424 {
1425 const BaseVec &bvec = basevec();
1426 size_t d = 0;
1427 for (const auto &binfo : bvec)
1428 {
1429 const size_t b = binfo.base_depth();
1430 if (b > d)
1431 d = b;
1432 }
1433 return d + 1;
1434 }
1435 static Closure*
1436 lookup_closure (const char *methodname)
1437 {
1438 MethodMap &mmap = methodmap();
1439 auto it = mmap.find (methodname);
1440 if (it != mmap.end())
1441 return &it->second;
1442 const BaseVec &bvec = basevec();
1443 for (const auto &base : bvec)
1444 {
1445 Closure *closure = base.lookup_closure (methodname);
1446 if (closure)
1447 return closure;
1448 }
1449 return nullptr;
1450 }
1451 static bool
1452 try_upcast (std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
1453 {
1454 if (classname() == baseclass)
1455 {
1456 std::shared_ptr<T> *baseptrp = static_cast<std::shared_ptr<T>*> (sptrB);
1457 *baseptrp = sptr;
1458 return true;
1459 }
1460 const BaseVec &bvec = basevec();
1461 for (const auto &it : bvec)
1462 if (it.upcast_impl (sptr, baseclass, sptrB))
1463 return true;
1464 return false;
1465 }
1466};
1467
1469template<typename T, typename Enable = void>
1471template<typename T>
1472struct IsWrappableClass<T, REQUIRESv< std::is_class<T>::value &&
1473 !IsSharedPtr<T>::value &&
1474 !DerivesPair<T>::value &&
1475 !DerivesVector<T>::value >> : std::true_type {};
1476template<>
1478template<typename T>
1480template<typename T>
1482
1484template<typename T>
1485struct Convert<std::shared_ptr<T>, REQUIRESv< IsWrappableClass<T>::value >> {
1486 using ClassType = typename std::remove_cv<T>::type;
1487 static std::shared_ptr<T>
1488 from_json (const JsonValue &value)
1489 {
1490 if (Serializable<ClassType>::is_serializable() && value.IsObject())
1492 else
1494 }
1495 static JsonValue
1496 to_json (const std::shared_ptr<T> &sptr, JsonAllocator &allocator)
1497 {
1499 return sptr ? Serializable<ClassType>::serialize_to_json (*sptr, allocator) : JsonValue (rapidjson::kObjectType);
1500 if (sptr)
1501 {
1502 // Wrap sptr, determine most derived wrapper via dynamic casts
1503 const std::string impltype = rtti_typename (*sptr);
1504 JsonValue result = InstanceMap::scope_wrap_object<ClassType> (sptr, allocator);
1505 return result;
1506 }
1507 return JsonValue(); // null
1508 }
1509};
1510
1512static inline void
1513forget_json_id (size_t id)
1514{
1515 InstanceMap::scope_forget_id (id);
1516}
1517
1519template<typename T>
1521 using ClassType = typename std::remove_cv<T>::type;
1522 static T*
1523 from_json (const JsonValue &value)
1524 {
1525 return &*Convert<std::shared_ptr<T>>::from_json (value);
1526 }
1527 static JsonValue
1528 to_json (const T *obj, JsonAllocator &allocator)
1529 {
1531 return obj ? Serializable<ClassType>::serialize_to_json (*obj, allocator) : JsonValue (rapidjson::kObjectType);
1532 // Caveat: Jsonipc will only auto-convert to most-derived-type iff it is registered and when looking at a shared_ptr<BaseType>
1533 std::shared_ptr<T> sptr;
1534 if constexpr (Has_shared_from_this<T>::value)
1535 {
1536 if (obj)
1537 sptr = std::dynamic_pointer_cast<T> (const_cast<T&> (*obj).shared_from_this());
1538 }
1539 // dprintf (2, "shared_from_this: type<%d>=%s ptr=%p\n", Has_shared_from_this<T>::value, rtti_typename<T>().c_str(), sptr.get());
1540 return Convert<std::shared_ptr<T>>::to_json (sptr, allocator);
1541 }
1542};
1543
1545template<typename T>
1546struct Convert<T, REQUIRESv< IsWrappableClass<T>::value >> {
1547 using ClassType = typename std::remove_cv<T>::type;
1548 static T&
1549 from_json (const JsonValue &value)
1550 {
1551 T *object = Convert<T*>::from_json (value);
1552 if (object)
1553 return *object;
1554 throw Jsonipc::bad_invocation (-32602, "Invalid params: attempt to cast null to reference type");
1555 }
1556 static JsonValue
1557 to_json (const T &object, JsonAllocator &allocator)
1558 {
1559 return Convert<T*>::to_json (&object, allocator);
1560 }
1561};
1562
1563// == IpcDispatcher ==
1565 void
1566 add_method (const std::string &methodname, const Closure &closure)
1567 {
1568 extra_methods[methodname] = closure;
1569 }
1570 // Dispatch JSON message and return result. Requires a live Scope instance in the current thread.
1572 dispatch_message (const std::string &message)
1573 {
1574 rapidjson::Document document;
1575 document.Parse<rapidjson_parse_flags> (message.data(), message.size());
1576 size_t id = 0;
1577 try {
1578 if (document.HasParseError())
1579 return create_error (id, -32700, "Parse error");
1580 const char *methodname = nullptr;
1581 const JsonValue *args = nullptr;
1582 for (const auto &m : document.GetObject())
1583 if (m.name == "id")
1584 id = from_json<size_t> (m.value, 0);
1585 else if (m.name == "method")
1586 methodname = from_json<const char*> (m.value);
1587 else if (m.name == "params" && m.value.IsArray())
1588 args = &m.value;
1589 if (!id || !methodname || !args || !args->IsArray())
1590 return create_error (id, -32600, "Invalid Request");
1591 CallbackInfo cbi (*args);
1592 Closure *closure = cbi.find_closure (methodname);
1593 if (!closure)
1594 {
1595 const auto it = extra_methods.find (methodname);
1596 if (it != extra_methods.end())
1597 closure = &it->second;
1598 else if (strcmp (methodname, "Jsonipc/handshake") == 0)
1599 {
1600 static Closure initialize = [] (CallbackInfo &cbi) { return jsonipc_initialize (cbi); };
1601 closure = &initialize;
1602 }
1603 }
1604 if (!closure)
1605 return create_error (id, -32601, "Method not found: " + cbi.classname ("<unknown-this>") + "['" + methodname + "']");
1606 (*closure) (cbi);
1607 return create_reply (id, cbi.get_result(), !cbi.have_result(), cbi.document());
1608 } catch (const Jsonipc::bad_invocation &exc) {
1609 return create_error (id, exc.code(), exc.what());
1610 }
1611 }
1612private:
1613 std::map<std::string, Closure> extra_methods;
1615 create_reply (size_t id, JsonValue &result, bool skip_result, rapidjson::Document &d)
1616 {
1617 auto &a = d.GetAllocator();
1618 d.SetObject();
1619 d.AddMember ("id", id, a);
1620 d.AddMember ("result", result, a); // move-semantics!
1621 rapidjson::StringBuffer buffer;
1622 StringBufferWriter writer (buffer);
1623 d.Accept (writer);
1624 std::string output { buffer.GetString(), buffer.GetSize() };
1625 return output;
1626 }
1628 create_error (size_t id, int errorcode, const std::string &message)
1629 {
1630 rapidjson::Document d (rapidjson::kObjectType);
1631 auto &a = d.GetAllocator();
1632 d.AddMember ("id", id ? JsonValue (id) : JsonValue(), a);
1633 JsonValue error (rapidjson::kObjectType);
1634 error.AddMember ("code", errorcode, a);
1635 error.AddMember ("message", JsonValue (message.c_str(), a).Move(), a);
1636 d.AddMember ("error", error, a); // moves error to null
1637 rapidjson::StringBuffer buffer;
1638 rapidjson::Writer<rapidjson::StringBuffer> writer (buffer);
1639 d.Accept (writer);
1640 std::string output { buffer.GetString(), buffer.GetSize() };
1641 return output;
1642 }
1643 static std::string*
1644 jsonipc_initialize (CallbackInfo &cbi)
1645 {
1646 cbi.set_result (to_json (0x00000001, cbi.allocator()).Move());
1647 return nullptr; // no error
1648 }
1649};
1650
1651} // Jsonipc
1652
1653JSONIPC_MAP_TO_TYPESCRIPT (void, "void");
1654JSONIPC_MAP_TO_TYPESCRIPT (bool, "boolean");
1655JSONIPC_MAP_TO_TYPESCRIPT (::int8_t, "number");
1656JSONIPC_MAP_TO_TYPESCRIPT (::uint8_t, "number");
1657JSONIPC_MAP_TO_TYPESCRIPT (::int32_t, "number");
1658JSONIPC_MAP_TO_TYPESCRIPT (::uint32_t, "number");
1659JSONIPC_MAP_TO_TYPESCRIPT (::int64_t, "number");
1660JSONIPC_MAP_TO_TYPESCRIPT (::uint64_t, "number");
1661JSONIPC_MAP_TO_TYPESCRIPT (float, "number");
1662JSONIPC_MAP_TO_TYPESCRIPT (double, "number");
1663JSONIPC_MAP_TO_TYPESCRIPT (const char*, "string");
1664JSONIPC_MAP_TO_TYPESCRIPT (::std::string, "string");
T c_str(T... args)
Keep track of temporary instances during IpcDispatcher::dispatch_message().
Definition jsonipc.hh:163
T clear(T... args)
close
T data(T... args)
fprintf
T empty(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
free
T insert(T... args)
j0
std::string normalize_typename(const std::string &string)
Yield the Javascript identifier name by substituting ':+' with '.'.
Definition jsonipc.hh:816
typename ::std::enable_if< value, bool >::type REQUIRES
REQUIRES<value> - Simplified version of std::enable_if<cond,bool>::type to use SFINAE in function tem...
Definition jsonipc.hh:68
typename ::std::enable_if< value, void >::type REQUIRESv
REQUIRESv<value> - Simplified version of std::enable_if<cond,void>::type to use SFINAE in struct temp...
Definition jsonipc.hh:71
typedef char
mmap
bool contains(const C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Returns true if container element for which pred() is true.
Definition utils.hh:297
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
newlocale
T rfind(T... args)
T size(T... args)
typedef uint64_t
strcmp
strlen
Template class providing conversion helpers for JsonValue to indexed C++ function argument.
Definition jsonipc.hh:505
Context for calling C++ functions from Json.
Definition jsonipc.hh:450
Class & set(const char *name, R(C::*get)() const, VB(C::*set)(A))
Add a member object accessors.
Definition jsonipc.hh:1310
Class & set(const char *name, const F &method)
Add a member function pointer.
Definition jsonipc.hh:1301
Template class providing C++ <-> JsonValue conversions for various types.
Definition jsonipc.hh:213
Wrapper for function argument default value constants.
Definition jsonipc.hh:798
DerivesPair<T> - Check if T derives from std::pair<>.
Definition jsonipc.hh:314
DerivesSharedPtr<T> - Check if T derives from std::shared_ptr<>.
Definition jsonipc.hh:114
DerivesVector<T> - Check if T derives from std::vector<>.
Definition jsonipc.hh:308
Template class providing return type and argument type information for functions.
Definition jsonipc.hh:472
Has___typename__<T> - Check if T provides a __typename__() method.
Definition jsonipc.hh:124
Has_setget<T> - Check if type T provides methods set() and get()
Definition jsonipc.hh:128
Has_shared_from_this<T> - Check if t.shared_from_this() yields a std::shared_ptr<>.
Definition jsonipc.hh:119
Template class to identify std::shared_ptr<> classes.
Definition jsonipc.hh:74
Template class to identify wrappable classes.
Definition jsonipc.hh:1470
Common base type for polymorphic classes managed by std::shared_ptr<>.
Definition jsonipc.hh:32
Jsonipc wrapper type for objects that support field-wise serialization to/from JSON.
Definition jsonipc.hh:1186
Serializable & set(const char *name, A attribute)
Add a member object pointer.
Definition jsonipc.hh:1196
Serializable()
Allow object handles to be streamed to/from Javascript, needs a Scope for temporaries.
Definition jsonipc.hh:1188
Jsonipc exception that is relayed to caller when thrown during invocations.
Definition jsonipc.hh:144
T substr(T... args)
T swap(T... args)
typedef size_t
T to_string(T... args)
uselocale
va_start
vsnprintf