Anklang-0.3.0-1416-gfa8ce8d2 anklang-0.3.0-1416-gfa8ce8d2
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); abort(); } 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 ==
540 friend class Scope;
541 struct TypeidKey {
542 const std::type_index tindex;
543 void *ptr;
544 bool
545 operator< (const TypeidKey &other) const noexcept
546 {
547 return tindex < other.tindex || (tindex == other.tindex && ptr < other.ptr);
548 }
549 };
550public:
551 class Wrapper {
552 virtual TypeidKey typeid_key () = 0;
553 virtual ~Wrapper () {}
554 friend class InstanceMap;
555 public:
556 virtual Closure* lookup_closure (const char *method) = 0;
557 virtual std::vector<std::string> list_methods () = 0;
558 virtual void try_upcast (const std::string &baseclass, void *sptrB) = 0;
559 virtual std::string classname () = 0;
560 };
561 using CreateWrapper = Wrapper* (*) (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth);
562 static std::vector<CreateWrapper>& wrapper_creators() { static std::vector<CreateWrapper> wrapper_creators_; return wrapper_creators_; }
563 static void
564 register_wrapper (CreateWrapper createwrapper)
565 {
566 wrapper_creators().push_back (createwrapper);
567 }
568 template<typename T>
569 class InstanceWrapper : public Wrapper {
570 std::shared_ptr<T> sptr_;
571 virtual
573 {
574 // printf ("InstanceMap::Wrapper: %s: deleting %s wrapper: %p\n", __func__, rtti_typename<T>().c_str(), sptr_.get());
575 }
576 public:
577 explicit InstanceWrapper (const std::shared_ptr<T> &sptr) : sptr_ (sptr) {}
578 Closure* lookup_closure (const char *method) override { return Class<T>::lookup_closure (method); }
579 std::vector<std::string> list_methods () override { return Class<T>::list_methods(); }
580 TypeidKey typeid_key () override { return create_typeid_key (sptr_); }
581 void try_upcast (const std::string &baseclass, void *sptrB) override
582 { Class<T>::try_upcast (sptr_, baseclass, sptrB); }
583 static TypeidKey
584 create_typeid_key (const std::shared_ptr<T> &sptr)
585 {
586 return { typeid (T), sptr.get() };
587 }
589 classname() override
590 {
591 return Class<T>::classname();
592 }
593 };
596 using IdSet = std::set<size_t>;
597 WrapperMap wmap_;
598 TypeidMap typeid_map_;
599 IdSet *idset_ = nullptr;
600 static size_t next_counter() { static size_t counter_ = 0; return ++counter_; }
601 bool
602 delete_id (size_t thisid)
603 {
604 const auto w = wmap_.find (thisid);
605 if (w != wmap_.end())
606 {
607 Wrapper *wrapper = w->second;
608 wmap_.erase (w);
609 const auto t = typeid_map_.find (wrapper->typeid_key());
610 if (t != typeid_map_.end())
611 typeid_map_.erase (t);
612 delete wrapper;
613 if (idset_)
614 idset_->erase (thisid);
615 return true;
616 }
617 return false;
618 }
619public:
620 bool
621 mark_unused()
622 {
623 if (idset_)
624 return false;
625 idset_ = new IdSet();
626 return true;
627 }
628 size_t
629 purge_unused (const std::vector<size_t> &unused)
630 {
631 IdSet preserve;
632 if (idset_)
633 {
634 idset_->swap (preserve);
635 delete idset_;
636 idset_ = nullptr;
637 }
638 auto contains = [] (const auto &c, const auto &e) {
639 return c.end() != c.find (e);
640 };
641 size_t preserved = 0;
642 for (const size_t id : unused)
643 if (!contains (preserve, id))
644 delete_id (id);
645 else
646 preserved++;
647 return preserved;
648 }
649 bool
650 empty() const
651 {
652 return wmap_.empty();
653 }
654 size_t
655 size() const
656 {
657 return wmap_.size();
658 }
659 void
660 clear (const bool printdebug = false)
661 {
662 if (idset_)
663 {
664 delete idset_;
665 idset_ = nullptr;
666 }
667 WrapperMap old;
668 std::swap (old, wmap_);
669 typeid_map_.clear();
670 for (auto &pair : old)
671 {
672 Wrapper *wrapper = pair.second;
673 if (printdebug)
674 fprintf (stderr, "Jsonipc::~Wrapper: %s: $id=%zu\n", string_demangle_cxx (wrapper->typeid_key().tindex.name()).c_str(), pair.first);
675 delete wrapper;
676 }
677 }
678 virtual
679 ~InstanceMap()
680 {
681 clear();
682 JSONIPC_ASSERT_RETURN (wmap_.size() == 0); // deleters shouldn't re-add
683 JSONIPC_ASSERT_RETURN (typeid_map_.size() == 0); // deleters shouldn't re-add
684 }
685 virtual JsonValue
686 wrapper_to_json (Wrapper *wrapper, const size_t thisid, JsonAllocator &allocator)
687 {
688 if (!wrapper)
689 return JsonValue(); // null
690 JsonValue jobject (rapidjson::kObjectType);
691 jobject.AddMember ("$id", thisid, allocator);
692 jobject.AddMember ("$class", JsonValue (wrapper->classname().c_str(), allocator), allocator);
693 return jobject;
694 }
695 template<typename T> static JsonValue
696 scope_wrap_object (const std::shared_ptr<T> &sptr, JsonAllocator &allocator)
697 {
698 InstanceMap *imap = Scope::instance_map();
699 size_t thisid = 0;
700 Wrapper *wrapper = nullptr;
701 if (sptr.get())
702 {
703 const TypeidKey tkey = InstanceWrapper<T>::create_typeid_key (sptr);
704 auto it = imap->typeid_map_.find (tkey);
705 if (it == imap->typeid_map_.end())
706 {
707 thisid = next_counter();
708 if constexpr (std::is_base_of_v<SharedBase, T>) {
709 std::vector<CreateWrapper> &wcreators = wrapper_creators(); // using CreateWrapper = Wrapper* (*) (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth);
710 size_t basedepth = 0;
711 for (size_t i = 0; i < wcreators.size(); i++) {
712 Wrapper *w = wcreators[i] (sptr, basedepth);
713 if (w) {
714 delete wrapper;
715 wrapper = w;
716 }
717 }
718 }
719 if (!wrapper)
720 wrapper = new InstanceWrapper<T> (sptr);
721 imap->wmap_[thisid] = wrapper;
722 imap->typeid_map_[tkey] = thisid;
723 }
724 else
725 {
726 thisid = it->second;
727 auto wt = imap->wmap_.find (thisid);
728 wrapper = wt != imap->wmap_.end() ? wt->second : nullptr;
729 }
730 }
731 if (imap->idset_)
732 imap->idset_->insert (thisid);
733 /* A note about TypeidKey:
734 * Two tuples (TypeX,ptr0x123) and (TypeY,ptr0x123) holding the same pointer address can
735 * occur if the RTII lookup to determine the actual Wrapper class fails, e.g. when
736 * Class<MostDerived> is unregisterd. In this case, ptr0x123 can be wrapped multiple
737 * times through different base classes.
738 */
739 return imap->wrapper_to_json (wrapper, thisid, allocator);
740 }
741 virtual Wrapper*
742 wrapper_from_json (const JsonValue &value)
743 {
744 if (value.IsObject())
745 {
746 auto it = value.FindMember ("$id");
747 if (it != value.MemberEnd())
748 {
749 const size_t thisid = Convert<size_t>::from_json (it->value);
750 if (thisid)
751 {
752 auto tit = wmap_.find (thisid);
753 if (tit != wmap_.end())
754 return tit->second;
755 }
756 }
757 }
758 return nullptr;
759 }
760 static Wrapper*
761 scope_lookup_wrapper (const JsonValue &value)
762 {
763 InstanceMap *imap = Scope::instance_map();
764 return imap ? imap->wrapper_from_json (value) : nullptr;
765 }
766 static bool
767 scope_forget_id (size_t thisid)
768 {
769 InstanceMap *imap = Scope::instance_map();
770 return imap->delete_id (thisid);
771 }
772};
773
774inline Closure*
775CallbackInfo::find_closure (const char *methodname)
776{
777 const JsonValue &value = ntharg (0);
778 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
779 return iw ? iw->lookup_closure (methodname) : nullptr;
780}
781
782inline std::string
783CallbackInfo::classname (const std::string &fallback) const
784{
785 const JsonValue &value = ntharg (0);
786 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
787 return iw ? iw->classname() : fallback;
788}
789
790inline
791Scope::Scope (InstanceMap &instance_map, ScopeLocalsP localsp) :
792 instance_map_ (instance_map), localsp_ (localsp ? localsp : ScopeLocalsP (&scope_locals_, [] (ScopeLocals*) {}))
793{
794 auto &stack_ = stack();
795 stack_.push_back (this);
796}
797
798// == DefaultConstant ==
802 DefaultConstant () = default;
803 template<typename T, REQUIRES< std::is_integral<T>::value && std::is_unsigned<T>::value> = true>
805 template<typename T, REQUIRES< std::is_integral<T>::value && !std::is_unsigned<T>::value> = true>
807 template<typename T, REQUIRES< std::is_convertible<T, double>::value && !std::is_integral<T>::value> = true>
808 DefaultConstant (T a) : DefaultConstantVariant (double (a)) {}
809 template<typename T, REQUIRES< std::is_convertible<T, std::string>::value && !std::is_same<T, std::nullptr_t>::value> = true>
811 template<typename T, REQUIRES< std::is_same<T, std::nullptr_t>::value> = true>
813};
815
816// == normalize_typename ==
818inline std::string
820{
821 std::string normalized;
822 auto is_identifier_char = [] (int ch) {
823 return ( (ch >= 'A' && ch <= 'Z') ||
824 (ch >= 'a' && ch <= 'z') ||
825 (ch >= '0' && ch <= '9') ||
826 ch == '_' || ch == '$' );
827 };
828 for (size_t i = 0; i < string.size() && string[i]; ++i)
829 if (is_identifier_char (string[i]))
830 normalized += string[i];
831 else if (normalized.size() && normalized[normalized.size() - 1] != '.')
832 normalized += '.';
833 return normalized;
834}
835
836// == JavaScript Helpers ==
838template<class V> static inline unsigned
839js_initializer_index ()
840{
841 using T = std::decay_t<V>;
842 if constexpr (std::is_same<T,bool>::value) return 3;
843 if constexpr (std::is_integral<T>::value) return 1;
844 if constexpr (std::is_floating_point<T>::value) return 2;
847 if constexpr (std::is_enum<T>::value) return 4; // [4]='' [1]=0
848 if constexpr (DerivesVector<T>::value) return 5;
849 if constexpr (DerivesPair<T>::value) return 5;
850 if constexpr (!IsSharedPtr<T>::value && std::is_class<T>::value) return 6;
851 return 0;
852}
853static constexpr const char *const js_initializers[] = { "null", "0", "0.0", "false", "''", "[]", "{}" };
854
855// == TypeScript Type Mapping ==
856inline std::string
857short_name (const std::string &full_name)
858{
859 const size_t last_colon = full_name.rfind ("::");
860 return last_colon == std::string::npos ? full_name : full_name.substr (last_colon + 2);
861}
862template<typename T> struct typescript_name {
863 static std::string name() { return short_name (rtti_typename<T>()); }
864};
865template<typename T> struct typescript_name<T&> : typescript_name<T> {};
866template<typename T> struct typescript_name<const T&> : typescript_name<T> {};
867template<typename T> struct typescript_name<std::shared_ptr<T>> : typescript_name<T> {};
868template<typename T>
869struct typescript_name<T*> {
870 static std::string name() { return typescript_name<T>::name() + " | null"; }
871};
872template<typename T1, typename T2>
873struct typescript_name<std::pair<T1, T2>> {
874 static std::string name() { return "[" + typescript_name<T1>::name() + ", " + typescript_name<T2>::name() + "]"; }
875};
876template<typename T>
878 static std::string name() { return typescript_name<T>::name() + "[]"; }
879};
880template<typename T>
881struct typescript_name<std::map<std::string, T>> {
882 static std::string name() { return "{ [key: string]: " + typescript_name<T>::name() + " }"; }
883};
884template<typename T>
885struct typescript_name<std::unordered_map<std::string, T>> {
886 static std::string name() { return "{ [key: string]: " + typescript_name<T>::name() + " }"; }
887};
888#define JSONIPC_MAP_TO_TYPESCRIPT(CXXTYPE,TSTYPE) \
889 template<> struct Jsonipc::typescript_name< CXXTYPE > { static std::string name() { return TSTYPE; } }
890
891template<typename... Args> std::string
892typescript_arg_list()
893{
894 std::string s;
895 int i = 0;
896 auto print_one_arg = [&] (const std::string &type_name) {
897 if (i > 0) s += ", ";
898 s += string_format ("arg%d: %s", ++i, type_name.c_str());
899 };
900 (print_one_arg (typescript_name<Args>::name()), ...); // C++17 fold expr
901 return s;
902}
903template<typename... Args> std::string
904typescript_arg_names_list()
905{
906 std::string s;
907 int i = 0;
908 auto append_arg_name = [&] (const std::string &type_name) {
909 s += string_format (", arg%d", ++i);
910 };
911 (append_arg_name (typescript_name<Args>::name()), ...); // C++17 fold expr
912 return s;
913}
914template<typename C, typename R, typename... Args> std::string
915typescript_call_impl (const std::string &method_name)
916{
917 std::string s;
918 s += string_format (" %s (", method_name.c_str());
919 s += typescript_arg_list<Args...>();
920 s += string_format ("): Promise<%s>\n", typescript_name<R>::name().c_str());
921 s += string_format (" { return this.$rpc (\"%s\", [this%s]); }\n",
922 method_name.c_str(), typescript_arg_names_list<Args...>().c_str());
923 return s;
924}
925template<typename T, typename Ret, typename... Args> std::string
926typescript_call (const std::string &method_name, Ret (T::*func) (Args...))
927{
928 return typescript_call_impl<T, Ret, Args...> (method_name);
929}
930template<typename T, typename Ret, typename... Args> std::string // const overload
931typescript_call (const std::string &method_name, Ret (T::*func) (Args...) const)
932{
933 return typescript_call_impl<T, Ret, Args...> (method_name);
934}
935
936// == BindingPrinter ==
938 enum Kind { ANY, ENUM, VALUE, RECORD, FIELD, CLASS, METHOD };
939 std::string b_;
940 std::string open_enum_, open_record_, open_class_;
941 std::set<std::string> record_types_;
943 // opos are output positions to insert expressions with late resolution.
945 size_t class_inherit_opos_ = 0;
946 void
947 close()
948 {
949 if (open_enum_.size())
950 close_enum();
951 if (open_record_.size())
952 close_record();
953 if (open_class_.size())
954 close_class();
955 }
956 template<class, class = void> struct has_nested_T : std::false_type {}; // Check for nested ::T type
957 template<typename U> struct has_nested_T<U, std::void_t<typename U::T>> : std::true_type {};
958 template<typename M> struct typescript_call_from_type; // Typescript signature from member function pointer
959 template<typename C, typename R, typename... Args>
960 struct typescript_call_from_type<R (C::*)(Args...)> {
961 static std::string
962 generate (const std::string &method_name)
963 {
964 return typescript_call_impl<C, R, Args...> (method_name);
965 }
966 };
967 template<typename C, typename R, typename... Args>
968 struct typescript_call_from_type<R (C::*)(Args...) const> {
969 static std::string
970 generate (const std::string &method_name)
971 {
972 return typescript_call_impl<C, R, Args...>(method_name);
973 }
974 };
975public:
977 finish()
978 {
979 close();
980 // Resolve defaults after all record types have been registered.
981 for (auto it = initializers_opos_.rbegin(); it != initializers_opos_.rend(); ++it)
982 {
983 const auto &[pos, type, index] = *it;
984 const std::string value = index == 6 && record_types_.count (type) ? "new " + type + "()" : js_initializers[index];
985 b_.insert (pos, value);
986 }
987 initializers_opos_.clear();
988 return b_;
989 }
990 template<typename T> void
991 enum_type()
992 {
993 close();
994 open_enum_ = rtti_typename<typename std::decay<T>::type>();
995 b_ += "export const " + short_name (open_enum_) + " = { // " + open_enum_ + "\n";
996 }
997 template<typename T> void
998 enum_value (const std::string &name, T v)
999 {
1000 const std::string full_js_name = normalize_typename (open_enum_) + "." + name;
1001 using underlying = typename std::underlying_type<T>::type;
1002 b_ += " " + name + ": \"" + full_js_name + "\", // " + std::to_string(static_cast<underlying>(v)) + "\n";
1003 }
1004 void
1005 close_enum()
1006 {
1007 b_ += "} as const;\n";
1008 const std::string shortname = short_name (open_enum_);
1009 b_ += "export type " + shortname + " = typeof " + shortname + "[keyof typeof " + shortname + "];\n";
1010 b_ += "Jsonipc.classes[\"" + open_enum_ + "\"] = " + shortname + ";\n\n";
1011 open_enum_.clear();
1012 }
1013 template<typename T> void
1014 record_type()
1015 {
1016 close();
1017 open_record_ = rtti_typename<typename std::decay<T>::type>();
1018 record_types_.insert (short_name (open_record_));
1019 b_ += "export class " + short_name (open_record_) + " { // " + open_record_ + "\n";
1020 }
1021 template<typename T, typename A> void
1022 field_member (const std::string &name)
1023 {
1024 const std::string ts_type_name = typescript_name<A>::name();
1025 const std::string as_cast = std::is_enum<A>::value ? ts_type_name : "";
1026 record_fields_.emplace_back (name, ts_type_name, js_initializer_index<A>(), as_cast);
1027 }
1028 void
1029 close_record()
1030 {
1031 for (const auto &[field_name, ts_type_name, default_index, as_cast] : record_fields_)
1032 b_ += " " + field_name + ": " + ts_type_name + ";\n";
1033 b_ += " constructor (";
1034 for (size_t i = 0; i < record_fields_.size(); i++) {
1035 const auto &[field_name, ts_type_name, default_index, as_cast] = record_fields_[i];
1036 b_ += (i ? ", " : "") + field_name + ": " + ts_type_name + " = ";
1037 initializers_opos_.emplace_back (b_.size(), ts_type_name, default_index);
1038 if (as_cast.size())
1039 b_ += " as " + as_cast;
1040 }
1041 b_ += ")\n {\n";
1042 for (const auto &[field_name, ts_type_name, default_index, as_cast] : record_fields_)
1043 b_ += " this." + field_name + " = " + field_name + ";\n";
1044 b_ += " }\n";
1045 b_ += "};\n";
1046 const std::string shortname = short_name (open_record_);
1047 b_ += "Jsonipc.classes[\"" + open_record_ + "\"] = " + shortname + ";\n\n";
1048 record_fields_.clear();
1049 open_record_.clear();
1050 }
1051 template<typename T> void
1052 class_type()
1053 {
1054 close();
1055 open_class_ = rtti_typename<typename std::decay<T>::type>();
1056 const std::string shortname = short_name (open_class_);
1057 b_ += "export class " + shortname + " // " + open_class_ + "\n";
1058 class_inherit_opos_ = b_.size();
1059 b_ += "{\n";
1060 b_ += " constructor ($id)\n";
1061 b_ += " { super ($id); if (new.target === " + shortname + ") Jsonipc.ofreeze (this); }\n";
1062 }
1063 template<typename B> void
1064 inherit_type()
1065 {
1066 const std::string base_class_ = rtti_typename<typename std::decay<B>::type>();
1067 const std::string inheritance = " extends Jsonipc.classes[\"" + base_class_ + "\"]\n";
1068 b_.insert (class_inherit_opos_, inheritance);
1069 for (auto &[pos, type, index] : initializers_opos_)
1070 if (pos >= class_inherit_opos_)
1071 pos += inheritance.size();
1072 }
1073 template<typename T, typename M> void
1074 method_member (const std::string &name)
1075 {
1076 b_ += typescript_call_from_type<M>::generate (name);
1077 }
1078 template<typename T, typename R, typename A> void
1079 field_accessor (const std::string &name)
1080 {
1081 const std::string ts_type = typescript_name<R>::name();
1082 b_ += " get " + name + " (): " + ts_type + "\n";
1083 b_ += " { return this.$get (\"" + name + "\", ";
1084 initializers_opos_.emplace_back (b_.size(), ts_type, js_initializer_index<R>());
1085 b_ += ") as " + ts_type + "; }\n";
1086 b_ += " set " + name + " (v: " + ts_type + ")\n";
1087 b_ += " { this.$set (\"" + name + "\", v); }\n";
1088 }
1089 void
1090 close_class()
1091 {
1092 b_ += "};\n";
1093 const std::string shortname = short_name (open_class_);
1094 b_ += "Jsonipc.classes[\"" + open_class_ + "\"] = " + shortname + ";\n\n";
1095 open_class_.clear();
1096 }
1097};
1098inline BindingPrinter *g_binding_printer = nullptr;
1099
1100// == TypeInfo ==
1103protected:
1104 virtual ~TypeInfo() {}
1105 explicit TypeInfo() {}
1106};
1107
1108// == Enum ==
1110template<typename T>
1111struct Enum final : TypeInfo {
1112 static_assert (std::is_enum<T>::value, "");
1113 Enum ()
1114 {
1115 if (JSONIPC_UNLIKELY (g_binding_printer))
1116 g_binding_printer->enum_type<T> ();
1117 }
1118 using UnderlyingType = typename std::underlying_type<T>::type;
1119 Enum&
1120 set (T v, const char *valuename)
1121 {
1122 const std::string class_name = typename_of<T>();
1123 auto &entries_ = entries();
1124 auto normalized_typename = normalize_typename (class_name + "." + valuename);
1125 Entry e { normalized_typename, v };
1126 entries_.push_back (e);
1127 if (JSONIPC_UNLIKELY (g_binding_printer))
1128 g_binding_printer->enum_value<T> (valuename, v);
1129 return *this;
1130 }
1131 static bool
1132 has_names ()
1133 {
1134 return !entries().empty();
1135 }
1136 static const std::string&
1137 get_name (T v)
1138 {
1139 const auto &entries_ = entries();
1140 for (const auto &e : entries_)
1141 if (v == e.value)
1142 return e.name;
1143 static const std::string empty;
1144 return empty;
1145 }
1146 static T
1147 get_value (const std::string &name, T fallback)
1148 {
1149 auto c_isalnum = [] (char c) {
1150 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
1151 };
1152 const auto &entries_ = entries();
1153 for (const auto &e : entries_)
1154 if (name == e.name || // exact match, or
1155 (name.size() < e.name.size() && // name starts at e.name word boundary
1156 !c_isalnum (e.name[e.name.size() - name.size() - 1]) && // and matches the tail of e.name
1157 e.name.compare (e.name.size() - name.size(), name.size(), name) == 0))
1158 return e.value;
1159 return fallback;
1160 }
1162 static EnumValueS
1163 list_values ()
1164 {
1165 EnumValueS enumvalues;
1166 const auto &entries_ = entries();
1167 for (const auto &e : entries_)
1168 enumvalues.push_back ({ int64_t (e.value), e.name });
1169 return enumvalues;
1170 }
1171private:
1172 struct Entry { const std::string name; T value; };
1173 static std::vector<Entry>& entries() { static std::vector<Entry> entries_; return entries_; }
1174 template<typename U> static std::string
1175 typename_of()
1176 {
1177 using Type = typename std::decay<U>::type;
1178 return rtti_typename<Type>();
1179 }
1180};
1181
1182// enum types
1183template<typename T>
1184struct Convert<T, REQUIRESv< std::is_enum<T>::value > > {
1185 using UnderlyingType = typename std::underlying_type<T>::type;
1186 static T
1187 from_json (const JsonValue &value, T fallback = T())
1188 {
1189 if (value.IsString())
1190 {
1191 using EnumType = Enum<T>;
1192 const std::string string = Convert<std::string>::from_json (value);
1193 return EnumType::get_value (string, fallback);
1194 }
1195 return T (Convert<UnderlyingType>::from_json (value, UnderlyingType (fallback)));
1196 }
1197 static JsonValue
1198 to_json (T evalue, JsonAllocator &allocator)
1199 {
1200 using EnumType = Enum<T>;
1201 if (EnumType::has_names())
1202 {
1203 const std::string &name = EnumType::get_name (evalue);
1204 if (!name.empty())
1205 return Convert<std::string>::to_json (name, allocator);
1206 }
1207 return Convert<UnderlyingType>::to_json (UnderlyingType (evalue), allocator);
1208 }
1209};
1210
1211// == Serializable ==
1213template<typename T>
1214struct Serializable final : TypeInfo {
1217 {
1218 make_serializable<T>();
1219 if (JSONIPC_UNLIKELY (g_binding_printer))
1220 g_binding_printer->record_type<T>();
1221 }
1223 template<typename A, REQUIRES< std::is_member_object_pointer<A>::value > = true> Serializable&
1224 set (const char *name, A attribute)
1225 {
1226 using SetterAttributeType = typename FunctionTraits<A>::ReturnType;
1227 Accessors accessors;
1228 accessors.setter = [attribute] (T &obj, const JsonValue &value) -> void { obj.*attribute = from_json<SetterAttributeType> (value); };
1229 accessors.getter = [attribute] (const T &obj, JsonAllocator &a) -> JsonValue { return to_json (obj.*attribute, a); };
1230 AccessorMap &amap = accessormap();
1231 auto it = amap.find (name);
1232 if (it != amap.end())
1233 throw std::runtime_error ("duplicate attribute registration: " + std::string (name));
1234 amap.insert (std::make_pair<std::string, Accessors> (name, std::move (accessors)));
1235 const std::string class_name = rtti_typename<T>();
1236 if (JSONIPC_UNLIKELY (g_binding_printer))
1237 g_binding_printer->field_member<T,SetterAttributeType> (name);
1238 return *this;
1239 }
1240 static bool is_serializable () { return serialize_from_json_() && serialize_to_json_(); }
1241 static JsonValue serialize_to_json (const T &o, JsonAllocator &a) { return serialize_to_json_() (o, a); }
1242 static std::shared_ptr<T> serialize_from_json (const JsonValue &value,
1243 const std::shared_ptr<T> &p = 0) { return serialize_from_json_() (value, p); }
1244private:
1245 struct Accessors {
1246 std::function<void (T&,const JsonValue&)> setter;
1247 std::function<JsonValue (const T&, JsonAllocator&)> getter;
1248 };
1249 using AccessorMap = std::map<std::string, Accessors>;
1250 static AccessorMap& accessormap() { static AccessorMap amap; return amap; }
1251 template<typename U> static void
1252 make_serializable()
1253 {
1254 // implement serialize_from_json by calling all setters
1255 SerializeFromJson sfj = [] (const JsonValue &value, const std::shared_ptr<T> &p) -> std::shared_ptr<T> {
1256 std::shared_ptr<T> obj = p ? p : Scope::make_shared<T>();
1257 if (!obj)
1258 return obj;
1259 AccessorMap &amap = accessormap();
1260 for (const auto &field : value.GetObject())
1261 {
1262 const std::string field_name = field.name.GetString();
1263 auto it = amap.find (field_name);
1264 if (it == amap.end())
1265 continue;
1266 Accessors &accessors = it->second;
1267 accessors.setter (*obj, field.value);
1268 }
1269 return obj;
1270 };
1271 serialize_from_json_() = sfj;
1272 // implement serialize_to_json by calling all getters
1273 SerializeToJson stj = [] (const T &object, JsonAllocator &allocator) -> JsonValue {
1274 JsonValue jobject (rapidjson::kObjectType); // serialized result
1275 AccessorMap &amap = accessormap();
1276 for (auto &it : amap)
1277 {
1278 const std::string field_name = it.first;
1279 Accessors &accessors = it.second;
1280 JsonValue result = accessors.getter (object, allocator);
1281 jobject.AddMember (JsonValue (field_name.c_str(), allocator), result, allocator);
1282 }
1283 return jobject;
1284 };
1285 serialize_to_json_() = stj;
1286 }
1287 using SerializeFromJson = std::function<std::shared_ptr<T> (const JsonValue&, const std::shared_ptr<T>&)>;
1288 using SerializeToJson = std::function<JsonValue (const T&, JsonAllocator&)>;
1289 static SerializeFromJson& serialize_from_json_ () { static SerializeFromJson impl; return impl; }
1290 static SerializeToJson& serialize_to_json_ () { static SerializeToJson impl; return impl; }
1291};
1292
1293// == Class ==
1294template<typename T>
1295struct Class final : TypeInfo {
1296 Class (bool internal = false)
1297 {
1298 auto create_wrapper = [] (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth) -> InstanceMap::Wrapper*
1299 {
1300 /* This is an exhaustive search for the best (most derived) wrapper type for
1301 * an object. We currently use a linear search that can involve as many
1302 * dynamic casts as the inheritance depth of the registered wrappers.
1303 */
1304 const size_t class_depth = Class::base_depth();
1305 if (class_depth > basedepth) {
1307 if (derived_sptr.get()) {
1308 basedepth = class_depth;
1309 return new InstanceMap::InstanceWrapper<T> (derived_sptr);
1310 }
1311 }
1312 return nullptr;
1313 };
1314 InstanceMap::register_wrapper (create_wrapper);
1315 if (!internal && JSONIPC_UNLIKELY (g_binding_printer))
1316 g_binding_printer->class_type<T>();
1317 }
1318 // Inherit base class `B`
1319 template<typename B> Class&
1320 inherit()
1321 {
1322 add_base<B>();
1323 if (JSONIPC_UNLIKELY (g_binding_printer))
1324 g_binding_printer->inherit_type<B>();
1325 return *this;
1326 }
1328 template<typename F, REQUIRES< std::is_member_function_pointer<F>::value > = true> Class&
1329 set (const char *name, const F &method)
1330 {
1331 add_member_function_closure (name, make_closure (method));
1332 if (JSONIPC_UNLIKELY (g_binding_printer))
1333 g_binding_printer->method_member<T,F> (name);
1334 return *this;
1335 }
1337 template<typename R, typename A, typename C, typename VB> Class&
1338 set (const char *name, R (C::*get) () const, VB (C::*set) (A))
1339 {
1341 JSONIPC_ASSERT_RETURN (get && set, *this);
1342 add_member_function_closure (std::string ("get/") + name, make_closure (get));
1343 add_member_function_closure (std::string ("set/") + name, make_closure (set));
1344 if (JSONIPC_UNLIKELY (g_binding_printer))
1345 g_binding_printer->field_accessor<T,R,A> (name);
1346 return *this;
1347 }
1348 template<typename F, REQUIRES< std::is_member_function_pointer<F>::value > = true> Class&
1349 set_d (const char *name, const F &method, const DefaultsList &dflts)
1350 {
1351 constexpr const size_t N_ARGS = CallTraits<F>::N_ARGS;
1352 JSONIPC_ASSERT_RETURN (dflts.size() <= N_ARGS, *this);
1353 add_member_function_closure (name, make_closure (method));
1354 if (JSONIPC_UNLIKELY (g_binding_printer))
1355 g_binding_printer->method_member<T,F> (name);
1356 return *this;
1357 }
1358 static std::string
1359 classname ()
1360 {
1361 return typename_of<T>();
1362 }
1363 static std::shared_ptr<T>
1364 object_from_json (const JsonValue &value)
1365 {
1366 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
1367 if (iw)
1368 {
1369 std::shared_ptr<T> base_sptr = nullptr;
1370 iw->try_upcast (classname(), &base_sptr);
1371 if (base_sptr)
1372 return base_sptr;
1373 }
1374 return nullptr;
1375 }
1376private:
1377 template<typename U> static std::string
1378 typename_of()
1379 {
1380 using Type = typename std::decay<U>::type;
1381 return rtti_typename<Type>();
1382 }
1383 template<typename F>
1385 template<typename F, REQUIRES< HasVoidReturn<F>::value > = true> Closure
1386 make_closure (const F &method)
1387 {
1388 return [method] (const CallbackInfo &cbi) {
1389 const bool HAS_THIS = true;
1390 if (HAS_THIS + CallTraits<F>::N_ARGS != cbi.n_args())
1391 throw Jsonipc::bad_invocation (-32602, "Invalid params: wrong number of arguments");
1392 std::shared_ptr<T> instance = object_from_json (cbi.ntharg (0));
1393 if (!instance)
1394 throw Jsonipc::bad_invocation (-32603, "Internal error: closure without this");
1395 call_from_json (*instance, method, cbi);
1396 };
1397 }
1398 template<typename F, REQUIRES< !HasVoidReturn<F>::value > = true> Closure
1399 make_closure (const F &method)
1400 {
1401 return [method] (CallbackInfo &cbi) {
1402 const bool HAS_THIS = true;
1403 if (HAS_THIS + CallTraits<F>::N_ARGS != cbi.n_args())
1404 throw Jsonipc::bad_invocation (-32602, "Invalid params: wrong number of arguments");
1405 std::shared_ptr<T> instance = object_from_json (cbi.ntharg (0));
1406 if (!instance)
1407 throw Jsonipc::bad_invocation (-32603, "Internal error: closure without this");
1408 JsonValue rv;
1409 rv = to_json (call_from_json (*instance, method, cbi), cbi.allocator());
1410 cbi.set_result (rv);
1411 };
1412 }
1413 void
1414 add_member_function_closure (const std::string &name, Closure &&closure)
1415 {
1416 MethodMap &mmap = methodmap();
1417 auto it = mmap.find (name);
1418 if (it != mmap.end())
1419 throw std::runtime_error ("duplicate method registration: " + name);
1420 mmap.insert (std::make_pair<std::string, Closure> (name.c_str(), std::move (closure)));
1421 }
1422 using MethodMap = std::map<std::string, Closure>;
1423 static MethodMap& methodmap() { static MethodMap methodmap_; return methodmap_; }
1424 struct BaseInfo {
1425 std::string basetypename;
1426 size_t (*base_depth) ();
1427 bool (*upcast_impl) (const std::shared_ptr<T>&, const std::string&, void*) = NULL;
1428 Closure* (*lookup_closure) (const char*) = NULL;
1429 std::vector<std::string> (*list_methods) () = NULL;
1430 };
1431 using BaseVec = std::vector<BaseInfo>;
1432 template<typename B> void
1433 add_base ()
1434 {
1435 BaseVec &bvec = basevec();
1436 BaseInfo binfo { typename_of<B>(), Class<B>::base_depth, &upcast_impl<B>, &Class<B>::lookup_closure, &Class<B>::list_methods, };
1437 for (const auto &it : bvec)
1438 if (it.basetypename == binfo.basetypename)
1439 throw std::runtime_error ("duplicate base registration: " + binfo.basetypename);
1440 bvec.push_back (binfo);
1441 Class<B> bclass (true); // internal=true; force registration for base_depth
1442 }
1443 static BaseVec& basevec () { static BaseVec basevec_; return basevec_; }
1444 template<typename B> static bool
1445 upcast_impl (const std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
1446 {
1447 std::shared_ptr<B> bptr = sptr;
1448 return Class<B>::try_upcast (bptr, baseclass, sptrB);
1449 }
1450public:
1451 static size_t
1452 base_depth ()
1453 {
1454 const BaseVec &bvec = basevec();
1455 size_t d = 0;
1456 for (const auto &binfo : bvec)
1457 {
1458 const size_t b = binfo.base_depth();
1459 if (b > d)
1460 d = b;
1461 }
1462 return d + 1;
1463 }
1464 static Closure*
1465 lookup_closure (const char *methodname)
1466 {
1467 MethodMap &mmap = methodmap();
1468 auto it = mmap.find (methodname);
1469 if (it != mmap.end())
1470 return &it->second;
1471 const BaseVec &bvec = basevec();
1472 for (const auto &base : bvec)
1473 {
1474 Closure *closure = base.lookup_closure (methodname);
1475 if (closure)
1476 return closure;
1477 }
1478 return nullptr;
1479 }
1483 {
1485 for (const auto &it : methodmap())
1486 names.push_back (it.first);
1487 for (const auto &base : basevec())
1488 for (const auto &name : base.list_methods())
1489 names.push_back (name);
1490 std::sort (names.begin(), names.end());
1491 names.erase (std::unique (names.begin(), names.end()), names.end());
1492 return names;
1493 }
1494 static bool
1495 try_upcast (std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
1496 {
1497 if (classname() == baseclass)
1498 {
1499 std::shared_ptr<T> *baseptrp = static_cast<std::shared_ptr<T>*> (sptrB);
1500 *baseptrp = sptr;
1501 return true;
1502 }
1503 const BaseVec &bvec = basevec();
1504 for (const auto &it : bvec)
1505 if (it.upcast_impl (sptr, baseclass, sptrB))
1506 return true;
1507 return false;
1508 }
1509};
1510
1512template<typename T, typename Enable = void>
1514template<typename T>
1515struct IsWrappableClass<T, REQUIRESv< std::is_class<T>::value &&
1516 !IsSharedPtr<T>::value &&
1517 !DerivesPair<T>::value &&
1518 !DerivesVector<T>::value >> : std::true_type {};
1519template<>
1521template<typename T>
1523template<typename T>
1525
1527template<typename T>
1528struct Convert<std::shared_ptr<T>, REQUIRESv< IsWrappableClass<T>::value >> {
1529 using ClassType = typename std::remove_cv<T>::type;
1530 static std::shared_ptr<T>
1531 from_json (const JsonValue &value)
1532 {
1533 if (Serializable<ClassType>::is_serializable() && value.IsObject())
1535 else
1537 }
1538 static JsonValue
1539 to_json (const std::shared_ptr<T> &sptr, JsonAllocator &allocator)
1540 {
1542 return sptr ? Serializable<ClassType>::serialize_to_json (*sptr, allocator) : JsonValue (rapidjson::kObjectType);
1543 if (sptr)
1544 {
1545 // Wrap sptr, determine most derived wrapper via dynamic casts
1546 const std::string impltype = rtti_typename (*sptr);
1547 JsonValue result = InstanceMap::scope_wrap_object<ClassType> (sptr, allocator);
1548 return result;
1549 }
1550 return JsonValue(); // null
1551 }
1552};
1553
1555static inline void
1556forget_json_id (size_t id)
1557{
1558 InstanceMap::scope_forget_id (id);
1559}
1560
1562template<typename T>
1564 using ClassType = typename std::remove_cv<T>::type;
1565 static T*
1566 from_json (const JsonValue &value)
1567 {
1568 return &*Convert<std::shared_ptr<T>>::from_json (value);
1569 }
1570 static JsonValue
1571 to_json (const T *obj, JsonAllocator &allocator)
1572 {
1574 return obj ? Serializable<ClassType>::serialize_to_json (*obj, allocator) : JsonValue (rapidjson::kObjectType);
1575 // Caveat: Jsonipc will only auto-convert to most-derived-type iff it is registered and when looking at a shared_ptr<BaseType>
1576 std::shared_ptr<T> sptr;
1577 if constexpr (Has_shared_from_this<T>::value)
1578 {
1579 if (obj)
1580 sptr = std::dynamic_pointer_cast<T> (const_cast<T&> (*obj).shared_from_this());
1581 }
1582 // dprintf (2, "shared_from_this: type<%d>=%s ptr=%p\n", Has_shared_from_this<T>::value, rtti_typename<T>().c_str(), sptr.get());
1583 return Convert<std::shared_ptr<T>>::to_json (sptr, allocator);
1584 }
1585};
1586
1588template<typename T>
1589struct Convert<T, REQUIRESv< IsWrappableClass<T>::value >> {
1590 using ClassType = typename std::remove_cv<T>::type;
1591 static T&
1592 from_json (const JsonValue &value)
1593 {
1594 T *object = Convert<T*>::from_json (value);
1595 if (object)
1596 return *object;
1597 throw Jsonipc::bad_invocation (-32602, "Invalid params: attempt to cast null to reference type");
1598 }
1599 static JsonValue
1600 to_json (const T &object, JsonAllocator &allocator)
1601 {
1602 return Convert<T*>::to_json (&object, allocator);
1603 }
1604};
1605
1606// == IpcDispatcher ==
1608 void
1609 add_method (const std::string &methodname, const Closure &closure)
1610 {
1611 extra_methods[methodname] = closure;
1612 }
1613 // Dispatch JSON message and return result. Requires a live Scope instance in the current thread.
1615 dispatch_message (const std::string &message)
1616 {
1617 rapidjson::Document document;
1618 document.Parse<rapidjson_parse_flags> (message.data(), message.size());
1619 size_t id = 0;
1620 try {
1621 if (document.HasParseError())
1622 return create_error (id, -32700, "Parse error");
1623 const char *methodname = nullptr;
1624 const JsonValue *args = nullptr;
1625 for (const auto &m : document.GetObject())
1626 if (m.name == "id")
1627 id = from_json<size_t> (m.value, 0);
1628 else if (m.name == "method")
1629 methodname = from_json<const char*> (m.value);
1630 else if (m.name == "params" && m.value.IsArray())
1631 args = &m.value;
1632 if (!id || !methodname || !args || !args->IsArray())
1633 return create_error (id, -32600, "Invalid Request");
1634 CallbackInfo cbi (*args);
1635 Closure *closure = cbi.find_closure (methodname);
1636 if (!closure)
1637 {
1638 const auto it = extra_methods.find (methodname);
1639 if (it != extra_methods.end())
1640 closure = &it->second;
1641 else if (strcmp (methodname, "Jsonipc/handshake") == 0)
1642 {
1643 static Closure initialize = [] (CallbackInfo &cbi) { return jsonipc_initialize (cbi); };
1644 closure = &initialize;
1645 }
1646 }
1647 if (!closure)
1648 return create_error (id, -32601, "Method not found: " + cbi.classname ("<unknown-this>") + "['" + methodname + "']");
1649 (*closure) (cbi);
1650 return create_reply (id, cbi.get_result(), !cbi.have_result(), cbi.document());
1651 } catch (const Jsonipc::bad_invocation &exc) {
1652 return create_error (id, exc.code(), exc.what());
1653 }
1654 }
1655private:
1656 std::map<std::string, Closure> extra_methods;
1658 create_reply (size_t id, JsonValue &result, bool skip_result, rapidjson::Document &d)
1659 {
1660 auto &a = d.GetAllocator();
1661 d.SetObject();
1662 d.AddMember ("id", id, a);
1663 d.AddMember ("result", result, a); // move-semantics!
1664 rapidjson::StringBuffer buffer;
1665 StringBufferWriter writer (buffer);
1666 d.Accept (writer);
1667 std::string output { buffer.GetString(), buffer.GetSize() };
1668 return output;
1669 }
1671 create_error (size_t id, int errorcode, const std::string &message)
1672 {
1673 rapidjson::Document d (rapidjson::kObjectType);
1674 auto &a = d.GetAllocator();
1675 d.AddMember ("id", id ? JsonValue (id) : JsonValue(), a);
1676 JsonValue error (rapidjson::kObjectType);
1677 error.AddMember ("code", errorcode, a);
1678 error.AddMember ("message", JsonValue (message.c_str(), a).Move(), a);
1679 d.AddMember ("error", error, a); // moves error to null
1680 rapidjson::StringBuffer buffer;
1681 rapidjson::Writer<rapidjson::StringBuffer> writer (buffer);
1682 d.Accept (writer);
1683 std::string output { buffer.GetString(), buffer.GetSize() };
1684 return output;
1685 }
1686 static std::string*
1687 jsonipc_initialize (CallbackInfo &cbi)
1688 {
1689 cbi.set_result (to_json (0x00000001, cbi.allocator()).Move());
1690 return nullptr; // no error
1691 }
1692};
1693
1694} // Jsonipc
1695
1696JSONIPC_MAP_TO_TYPESCRIPT (void, "void");
1697JSONIPC_MAP_TO_TYPESCRIPT (bool, "boolean");
1698JSONIPC_MAP_TO_TYPESCRIPT (::int8_t, "number");
1699JSONIPC_MAP_TO_TYPESCRIPT (::uint8_t, "number");
1700JSONIPC_MAP_TO_TYPESCRIPT (::int32_t, "number");
1701JSONIPC_MAP_TO_TYPESCRIPT (::uint32_t, "number");
1702JSONIPC_MAP_TO_TYPESCRIPT (::int64_t, "number");
1703JSONIPC_MAP_TO_TYPESCRIPT (::uint64_t, "number");
1704JSONIPC_MAP_TO_TYPESCRIPT (float, "number");
1705JSONIPC_MAP_TO_TYPESCRIPT (double, "number");
1706JSONIPC_MAP_TO_TYPESCRIPT (const char*, "string");
1707JSONIPC_MAP_TO_TYPESCRIPT (::std::string, "string");
T c_str(T... args)
Maps C++ shared_ptr instances to JSON-wrapped objects with unique IDs, supporting polymorphic upcasti...
Definition jsonipc.hh:539
Keep track of temporary instances during IpcDispatcher::dispatch_message().
Definition jsonipc.hh:163
Base class for type registration (Enum, Serializable, Class) that optionally generates TypeScript bin...
Definition jsonipc.hh:1102
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:819
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:296
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)
T sort(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:1338
Class & set(const char *name, const F &method)
Add a member function pointer.
Definition jsonipc.hh:1329
static std::vector< std::string > list_methods()
List the method names registered for T and its base classes, accessors as "get/name" and "set/name".
Definition jsonipc.hh:1482
Template class providing C++ <-> JsonValue conversions for various types.
Definition jsonipc.hh:213
Wrapper for function argument default value constants.
Definition jsonipc.hh:801
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
Registers C++ enum values with string names for JSON serialization and TypeScript binding generation.
Definition jsonipc.hh:1111
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:1513
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:1214
Serializable & set(const char *name, A attribute)
Add a member object pointer.
Definition jsonipc.hh:1224
Serializable()
Allow object handles to be streamed to/from Javascript, needs a Scope for temporaries.
Definition jsonipc.hh:1216
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)
T unique(T... args)
uselocale
va_start
vsnprintf