Anklang 0.3.0-460-gc4ef46ba
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>
874struct typescript_name<std::vector<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 b_ += " get " + name + " (): " + typescript_name<R>::name() + "\n";
1058 b_ += " { return Jsonipc.get_reactive_prop.call (this, \"" + name + "\", " + js_initializers[js_initializer_index<R>()] + "); }\n";
1059 b_ += " set " + name + " (v: " + typescript_name<R>::name() + ")\n";
1060 b_ += " { Jsonipc.send ('set/' + '" + name + "', [this, v]); }\n";
1061 }
1062 void
1063 close_class()
1064 {
1065 b_ += "};\n";
1066 const std::string shortname = short_name (open_class_);
1067 b_ += "Jsonipc.classes[\"" + open_class_ + "\"] = " + shortname + ";\n\n";
1068 open_class_.clear();
1069 }
1070};
1071inline BindingPrinter *g_binding_printer = nullptr;
1072
1073// == TypeInfo ==
1075protected:
1076 virtual ~TypeInfo() {}
1077 explicit TypeInfo() {}
1078};
1079
1080// == Enum ==
1081template<typename T>
1082struct Enum final : TypeInfo {
1083 static_assert (std::is_enum<T>::value, "");
1084 Enum ()
1085 {
1086 if (JSONIPC_UNLIKELY (g_binding_printer))
1087 g_binding_printer->enum_type<T> ();
1088 }
1089 using UnderlyingType = typename std::underlying_type<T>::type;
1090 Enum&
1091 set (T v, const char *valuename)
1092 {
1093 const std::string class_name = typename_of<T>();
1094 auto &entries_ = entries();
1095 auto normalized_typename = normalize_typename (class_name + "." + valuename);
1096 Entry e { normalized_typename, v };
1097 entries_.push_back (e);
1098 if (JSONIPC_UNLIKELY (g_binding_printer))
1099 g_binding_printer->enum_value<T> (valuename, v);
1100 return *this;
1101 }
1102 static bool
1103 has_names ()
1104 {
1105 return !entries().empty();
1106 }
1107 static const std::string&
1108 get_name (T v)
1109 {
1110 const auto &entries_ = entries();
1111 for (const auto &e : entries_)
1112 if (v == e.value)
1113 return e.name;
1114 static const std::string empty;
1115 return empty;
1116 }
1117 static T
1118 get_value (const std::string &name, T fallback)
1119 {
1120 auto c_isalnum = [] (char c) {
1121 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
1122 };
1123 const auto &entries_ = entries();
1124 for (const auto &e : entries_)
1125 if (name == e.name || // exact match, or
1126 (name.size() < e.name.size() && // name starts at e.name word boundary
1127 !c_isalnum (e.name[e.name.size() - name.size() - 1]) && // and matches the tail of e.name
1128 e.name.compare (e.name.size() - name.size(), name.size(), name) == 0))
1129 return e.value;
1130 return fallback;
1131 }
1133 static EnumValueS
1134 list_values ()
1135 {
1136 EnumValueS enumvalues;
1137 const auto &entries_ = entries();
1138 for (const auto &e : entries_)
1139 enumvalues.push_back ({ int64_t (e.value), e.name });
1140 return enumvalues;
1141 }
1142private:
1143 struct Entry { const std::string name; T value; };
1144 static std::vector<Entry>& entries() { static std::vector<Entry> entries_; return entries_; }
1145 template<typename U> static std::string
1146 typename_of()
1147 {
1148 using Type = typename std::decay<U>::type;
1149 return rtti_typename<Type>();
1150 }
1151};
1152
1153// enum types
1154template<typename T>
1155struct Convert<T, REQUIRESv< std::is_enum<T>::value > > {
1156 using UnderlyingType = typename std::underlying_type<T>::type;
1157 static T
1158 from_json (const JsonValue &value, T fallback = T())
1159 {
1160 if (value.IsString())
1161 {
1162 using EnumType = Enum<T>;
1163 const std::string string = Convert<std::string>::from_json (value);
1164 return EnumType::get_value (string, fallback);
1165 }
1166 return T (Convert<UnderlyingType>::from_json (value, UnderlyingType (fallback)));
1167 }
1168 static JsonValue
1169 to_json (T evalue, JsonAllocator &allocator)
1170 {
1171 using EnumType = Enum<T>;
1172 if (EnumType::has_names())
1173 {
1174 const std::string &name = EnumType::get_name (evalue);
1175 if (!name.empty())
1176 return Convert<std::string>::to_json (name, allocator);
1177 }
1178 return Convert<UnderlyingType>::to_json (UnderlyingType (evalue), allocator);
1179 }
1180};
1181
1182// == Serializable ==
1184template<typename T>
1185struct Serializable final : TypeInfo {
1188 {
1189 make_serializable<T>();
1190 if (JSONIPC_UNLIKELY (g_binding_printer))
1191 g_binding_printer->record_type<T>();
1192 }
1194 template<typename A, REQUIRES< std::is_member_object_pointer<A>::value > = true> Serializable&
1195 set (const char *name, A attribute)
1196 {
1197 using SetterAttributeType = typename FunctionTraits<A>::ReturnType;
1198 Accessors accessors;
1199 accessors.setter = [attribute] (T &obj, const JsonValue &value) -> void { obj.*attribute = from_json<SetterAttributeType> (value); };
1200 accessors.getter = [attribute] (const T &obj, JsonAllocator &a) -> JsonValue { return to_json (obj.*attribute, a); };
1201 AccessorMap &amap = accessormap();
1202 auto it = amap.find (name);
1203 if (it != amap.end())
1204 throw std::runtime_error ("duplicate attribute registration: " + std::string (name));
1205 amap.insert (std::make_pair<std::string, Accessors> (name, std::move (accessors)));
1206 const std::string class_name = rtti_typename<T>();
1207 if (JSONIPC_UNLIKELY (g_binding_printer))
1208 g_binding_printer->field_member<T,SetterAttributeType> (name);
1209 return *this;
1210 }
1211 static bool is_serializable () { return serialize_from_json_() && serialize_to_json_(); }
1212 static JsonValue serialize_to_json (const T &o, JsonAllocator &a) { return serialize_to_json_() (o, a); }
1213 static std::shared_ptr<T> serialize_from_json (const JsonValue &value,
1214 const std::shared_ptr<T> &p = 0) { return serialize_from_json_() (value, p); }
1215private:
1216 struct Accessors {
1217 std::function<void (T&,const JsonValue&)> setter;
1218 std::function<JsonValue (const T&, JsonAllocator&)> getter;
1219 };
1220 using AccessorMap = std::map<std::string, Accessors>;
1221 static AccessorMap& accessormap() { static AccessorMap amap; return amap; }
1222 template<typename U> static void
1223 make_serializable()
1224 {
1225 // implement serialize_from_json by calling all setters
1226 SerializeFromJson sfj = [] (const JsonValue &value, const std::shared_ptr<T> &p) -> std::shared_ptr<T> {
1227 std::shared_ptr<T> obj = p ? p : Scope::make_shared<T>();
1228 if (!obj)
1229 return obj;
1230 AccessorMap &amap = accessormap();
1231 for (const auto &field : value.GetObject())
1232 {
1233 const std::string field_name = field.name.GetString();
1234 auto it = amap.find (field_name);
1235 if (it == amap.end())
1236 continue;
1237 Accessors &accessors = it->second;
1238 accessors.setter (*obj, field.value);
1239 }
1240 return obj;
1241 };
1242 serialize_from_json_() = sfj;
1243 // implement serialize_to_json by calling all getters
1244 SerializeToJson stj = [] (const T &object, JsonAllocator &allocator) -> JsonValue {
1245 JsonValue jobject (rapidjson::kObjectType); // serialized result
1246 AccessorMap &amap = accessormap();
1247 for (auto &it : amap)
1248 {
1249 const std::string field_name = it.first;
1250 Accessors &accessors = it.second;
1251 JsonValue result = accessors.getter (object, allocator);
1252 jobject.AddMember (JsonValue (field_name.c_str(), allocator), result, allocator);
1253 }
1254 return jobject;
1255 };
1256 serialize_to_json_() = stj;
1257 }
1258 using SerializeFromJson = std::function<std::shared_ptr<T> (const JsonValue&, const std::shared_ptr<T>&)>;
1259 using SerializeToJson = std::function<JsonValue (const T&, JsonAllocator&)>;
1260 static SerializeFromJson& serialize_from_json_ () { static SerializeFromJson impl; return impl; }
1261 static SerializeToJson& serialize_to_json_ () { static SerializeToJson impl; return impl; }
1262};
1263
1264// == Class ==
1265template<typename T>
1266struct Class final : TypeInfo {
1267 Class (bool internal = false)
1268 {
1269 auto create_wrapper = [] (const std::shared_ptr<SharedBase> &sptr, size_t &basedepth) -> InstanceMap::Wrapper*
1270 {
1271 /* This is an exhaustive search for the best (most derived) wrapper type for
1272 * an object. We currently use a linear search that can involve as many
1273 * dynamic casts as the inheritance depth of the registered wrappers.
1274 */
1275 const size_t class_depth = Class::base_depth();
1276 if (class_depth > basedepth) {
1278 if (derived_sptr.get()) {
1279 basedepth = class_depth;
1280 return new InstanceMap::InstanceWrapper<T> (derived_sptr);
1281 }
1282 }
1283 return nullptr;
1284 };
1285 InstanceMap::register_wrapper (create_wrapper);
1286 if (!internal && JSONIPC_UNLIKELY (g_binding_printer))
1287 g_binding_printer->class_type<T>();
1288 }
1289 // Inherit base class `B`
1290 template<typename B> Class&
1291 inherit()
1292 {
1293 add_base<B>();
1294 if (JSONIPC_UNLIKELY (g_binding_printer))
1295 g_binding_printer->inherit_type<B>();
1296 return *this;
1297 }
1299 template<typename F, REQUIRES< std::is_member_function_pointer<F>::value > = true> Class&
1300 set (const char *name, const F &method)
1301 {
1302 add_member_function_closure (name, make_closure (method));
1303 if (JSONIPC_UNLIKELY (g_binding_printer))
1304 g_binding_printer->method_member<T,F> (name);
1305 return *this;
1306 }
1308 template<typename R, typename A, typename C, typename VB> Class&
1309 set (const char *name, R (C::*get) () const, VB (C::*set) (A))
1310 {
1312 JSONIPC_ASSERT_RETURN (get && set, *this);
1313 add_member_function_closure (std::string ("get/") + name, make_closure (get));
1314 add_member_function_closure (std::string ("set/") + name, make_closure (set));
1315 if (JSONIPC_UNLIKELY (g_binding_printer))
1316 g_binding_printer->field_accessor<T,R,A> (name);
1317 return *this;
1318 }
1319 template<typename F, REQUIRES< std::is_member_function_pointer<F>::value > = true> Class&
1320 set_d (const char *name, const F &method, const DefaultsList &dflts)
1321 {
1322 constexpr const size_t N_ARGS = CallTraits<F>::N_ARGS;
1323 JSONIPC_ASSERT_RETURN (dflts.size() <= N_ARGS, *this);
1324 add_member_function_closure (name, make_closure (method));
1325 if (JSONIPC_UNLIKELY (g_binding_printer))
1326 g_binding_printer->method_member<T,F> (name);
1327 return *this;
1328 }
1329 static std::string
1330 classname ()
1331 {
1332 return typename_of<T>();
1333 }
1334 static std::shared_ptr<T>
1335 object_from_json (const JsonValue &value)
1336 {
1337 InstanceMap::Wrapper *iw = InstanceMap::scope_lookup_wrapper (value);
1338 if (iw)
1339 {
1340 std::shared_ptr<T> base_sptr = nullptr;
1341 iw->try_upcast (classname(), &base_sptr);
1342 if (base_sptr)
1343 return base_sptr;
1344 }
1345 return nullptr;
1346 }
1347private:
1348 template<typename U> static std::string
1349 typename_of()
1350 {
1351 using Type = typename std::decay<U>::type;
1352 return rtti_typename<Type>();
1353 }
1354 template<typename F>
1356 template<typename F, REQUIRES< HasVoidReturn<F>::value > = true> Closure
1357 make_closure (const F &method)
1358 {
1359 return [method] (const CallbackInfo &cbi) {
1360 const bool HAS_THIS = true;
1361 if (HAS_THIS + CallTraits<F>::N_ARGS != cbi.n_args())
1362 throw Jsonipc::bad_invocation (-32602, "Invalid params: wrong number of arguments");
1363 std::shared_ptr<T> instance = object_from_json (cbi.ntharg (0));
1364 if (!instance)
1365 throw Jsonipc::bad_invocation (-32603, "Internal error: closure without this");
1366 call_from_json (*instance, method, cbi);
1367 };
1368 }
1369 template<typename F, REQUIRES< !HasVoidReturn<F>::value > = true> Closure
1370 make_closure (const F &method)
1371 {
1372 return [method] (CallbackInfo &cbi) {
1373 const bool HAS_THIS = true;
1374 if (HAS_THIS + CallTraits<F>::N_ARGS != cbi.n_args())
1375 throw Jsonipc::bad_invocation (-32602, "Invalid params: wrong number of arguments");
1376 std::shared_ptr<T> instance = object_from_json (cbi.ntharg (0));
1377 if (!instance)
1378 throw Jsonipc::bad_invocation (-32603, "Internal error: closure without this");
1379 JsonValue rv;
1380 rv = to_json (call_from_json (*instance, method, cbi), cbi.allocator());
1381 cbi.set_result (rv);
1382 };
1383 }
1384 void
1385 add_member_function_closure (const std::string &name, Closure &&closure)
1386 {
1387 MethodMap &mmap = methodmap();
1388 auto it = mmap.find (name);
1389 if (it != mmap.end())
1390 throw std::runtime_error ("duplicate method registration: " + name);
1391 mmap.insert (std::make_pair<std::string, Closure> (name.c_str(), std::move (closure)));
1392 }
1393 using MethodMap = std::map<std::string, Closure>;
1394 static MethodMap& methodmap() { static MethodMap methodmap_; return methodmap_; }
1395 struct BaseInfo {
1396 std::string basetypename;
1397 size_t (*base_depth) ();
1398 bool (*upcast_impl) (const std::shared_ptr<T>&, const std::string&, void*) = NULL;
1399 Closure* (*lookup_closure) (const char*) = NULL;
1400 };
1401 using BaseVec = std::vector<BaseInfo>;
1402 template<typename B> void
1403 add_base ()
1404 {
1405 BaseVec &bvec = basevec();
1406 BaseInfo binfo { typename_of<B>(), Class<B>::base_depth, &upcast_impl<B>, &Class<B>::lookup_closure, };
1407 for (const auto &it : bvec)
1408 if (it.basetypename == binfo.basetypename)
1409 throw std::runtime_error ("duplicate base registration: " + binfo.basetypename);
1410 bvec.push_back (binfo);
1411 Class<B> bclass (true); // internal=true; force registration for base_depth
1412 }
1413 static BaseVec& basevec () { static BaseVec basevec_; return basevec_; }
1414 template<typename B> static bool
1415 upcast_impl (const std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
1416 {
1417 std::shared_ptr<B> bptr = sptr;
1418 return Class<B>::try_upcast (bptr, baseclass, sptrB);
1419 }
1420public:
1421 static size_t
1422 base_depth ()
1423 {
1424 const BaseVec &bvec = basevec();
1425 size_t d = 0;
1426 for (const auto &binfo : bvec)
1427 {
1428 const size_t b = binfo.base_depth();
1429 if (b > d)
1430 d = b;
1431 }
1432 return d + 1;
1433 }
1434 static Closure*
1435 lookup_closure (const char *methodname)
1436 {
1437 MethodMap &mmap = methodmap();
1438 auto it = mmap.find (methodname);
1439 if (it != mmap.end())
1440 return &it->second;
1441 const BaseVec &bvec = basevec();
1442 for (const auto &base : bvec)
1443 {
1444 Closure *closure = base.lookup_closure (methodname);
1445 if (closure)
1446 return closure;
1447 }
1448 return nullptr;
1449 }
1450 static bool
1451 try_upcast (std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
1452 {
1453 if (classname() == baseclass)
1454 {
1455 std::shared_ptr<T> *baseptrp = static_cast<std::shared_ptr<T>*> (sptrB);
1456 *baseptrp = sptr;
1457 return true;
1458 }
1459 const BaseVec &bvec = basevec();
1460 for (const auto &it : bvec)
1461 if (it.upcast_impl (sptr, baseclass, sptrB))
1462 return true;
1463 return false;
1464 }
1465};
1466
1468template<typename T, typename Enable = void>
1470template<typename T>
1471struct IsWrappableClass<T, REQUIRESv< std::is_class<T>::value &&
1472 !IsSharedPtr<T>::value &&
1473 !DerivesPair<T>::value &&
1474 !DerivesVector<T>::value >> : std::true_type {};
1475template<>
1477template<typename T>
1479template<typename T>
1481
1483template<typename T>
1484struct Convert<std::shared_ptr<T>, REQUIRESv< IsWrappableClass<T>::value >> {
1485 using ClassType = typename std::remove_cv<T>::type;
1486 static std::shared_ptr<T>
1487 from_json (const JsonValue &value)
1488 {
1489 if (Serializable<ClassType>::is_serializable() && value.IsObject())
1491 else
1493 }
1494 static JsonValue
1495 to_json (const std::shared_ptr<T> &sptr, JsonAllocator &allocator)
1496 {
1498 return sptr ? Serializable<ClassType>::serialize_to_json (*sptr, allocator) : JsonValue (rapidjson::kObjectType);
1499 if (sptr)
1500 {
1501 // Wrap sptr, determine most derived wrapper via dynamic casts
1502 const std::string impltype = rtti_typename (*sptr);
1503 JsonValue result = InstanceMap::scope_wrap_object<ClassType> (sptr, allocator);
1504 return result;
1505 }
1506 return JsonValue(); // null
1507 }
1508};
1509
1511static inline void
1512forget_json_id (size_t id)
1513{
1514 InstanceMap::scope_forget_id (id);
1515}
1516
1518template<typename T>
1520 using ClassType = typename std::remove_cv<T>::type;
1521 static T*
1522 from_json (const JsonValue &value)
1523 {
1524 return &*Convert<std::shared_ptr<T>>::from_json (value);
1525 }
1526 static JsonValue
1527 to_json (const T *obj, JsonAllocator &allocator)
1528 {
1530 return obj ? Serializable<ClassType>::serialize_to_json (*obj, allocator) : JsonValue (rapidjson::kObjectType);
1531 // Caveat: Jsonipc will only auto-convert to most-derived-type iff it is registered and when looking at a shared_ptr<BaseType>
1532 std::shared_ptr<T> sptr;
1533 if constexpr (Has_shared_from_this<T>::value)
1534 {
1535 if (obj)
1536 sptr = std::dynamic_pointer_cast<T> (const_cast<T&> (*obj).shared_from_this());
1537 }
1538 // dprintf (2, "shared_from_this: type<%d>=%s ptr=%p\n", Has_shared_from_this<T>::value, rtti_typename<T>().c_str(), sptr.get());
1539 return Convert<std::shared_ptr<T>>::to_json (sptr, allocator);
1540 }
1541};
1542
1544template<typename T>
1545struct Convert<T, REQUIRESv< IsWrappableClass<T>::value >> {
1546 using ClassType = typename std::remove_cv<T>::type;
1547 static T&
1548 from_json (const JsonValue &value)
1549 {
1550 T *object = Convert<T*>::from_json (value);
1551 if (object)
1552 return *object;
1553 throw Jsonipc::bad_invocation (-32602, "Invalid params: attempt to cast null to reference type");
1554 }
1555 static JsonValue
1556 to_json (const T &object, JsonAllocator &allocator)
1557 {
1558 return Convert<T*>::to_json (&object, allocator);
1559 }
1560};
1561
1562// == IpcDispatcher ==
1564 void
1565 add_method (const std::string &methodname, const Closure &closure)
1566 {
1567 extra_methods[methodname] = closure;
1568 }
1569 // Dispatch JSON message and return result. Requires a live Scope instance in the current thread.
1571 dispatch_message (const std::string &message)
1572 {
1573 rapidjson::Document document;
1574 document.Parse<rapidjson_parse_flags> (message.data(), message.size());
1575 size_t id = 0;
1576 try {
1577 if (document.HasParseError())
1578 return create_error (id, -32700, "Parse error");
1579 const char *methodname = nullptr;
1580 const JsonValue *args = nullptr;
1581 for (const auto &m : document.GetObject())
1582 if (m.name == "id")
1583 id = from_json<size_t> (m.value, 0);
1584 else if (m.name == "method")
1585 methodname = from_json<const char*> (m.value);
1586 else if (m.name == "params" && m.value.IsArray())
1587 args = &m.value;
1588 if (!id || !methodname || !args || !args->IsArray())
1589 return create_error (id, -32600, "Invalid Request");
1590 CallbackInfo cbi (*args);
1591 Closure *closure = cbi.find_closure (methodname);
1592 if (!closure)
1593 {
1594 const auto it = extra_methods.find (methodname);
1595 if (it != extra_methods.end())
1596 closure = &it->second;
1597 else if (strcmp (methodname, "Jsonipc/handshake") == 0)
1598 {
1599 static Closure initialize = [] (CallbackInfo &cbi) { return jsonipc_initialize (cbi); };
1600 closure = &initialize;
1601 }
1602 }
1603 if (!closure)
1604 return create_error (id, -32601, "Method not found: " + cbi.classname ("<unknown-this>") + "['" + methodname + "']");
1605 (*closure) (cbi);
1606 return create_reply (id, cbi.get_result(), !cbi.have_result(), cbi.document());
1607 } catch (const Jsonipc::bad_invocation &exc) {
1608 return create_error (id, exc.code(), exc.what());
1609 }
1610 }
1611private:
1612 std::map<std::string, Closure> extra_methods;
1614 create_reply (size_t id, JsonValue &result, bool skip_result, rapidjson::Document &d)
1615 {
1616 auto &a = d.GetAllocator();
1617 d.SetObject();
1618 d.AddMember ("id", id, a);
1619 d.AddMember ("result", result, a); // move-semantics!
1620 rapidjson::StringBuffer buffer;
1621 StringBufferWriter writer (buffer);
1622 d.Accept (writer);
1623 std::string output { buffer.GetString(), buffer.GetSize() };
1624 return output;
1625 }
1627 create_error (size_t id, int errorcode, const std::string &message)
1628 {
1629 rapidjson::Document d (rapidjson::kObjectType);
1630 auto &a = d.GetAllocator();
1631 d.AddMember ("id", id ? JsonValue (id) : JsonValue(), a);
1632 JsonValue error (rapidjson::kObjectType);
1633 error.AddMember ("code", errorcode, a);
1634 error.AddMember ("message", JsonValue (message.c_str(), a).Move(), a);
1635 d.AddMember ("error", error, a); // moves error to null
1636 rapidjson::StringBuffer buffer;
1637 rapidjson::Writer<rapidjson::StringBuffer> writer (buffer);
1638 d.Accept (writer);
1639 std::string output { buffer.GetString(), buffer.GetSize() };
1640 return output;
1641 }
1642 static std::string*
1643 jsonipc_initialize (CallbackInfo &cbi)
1644 {
1645 cbi.set_result (to_json (0x00000001, cbi.allocator()).Move());
1646 return nullptr; // no error
1647 }
1648};
1649
1650} // Jsonipc
1651
1652JSONIPC_MAP_TO_TYPESCRIPT (void, "void");
1653JSONIPC_MAP_TO_TYPESCRIPT (bool, "boolean");
1654JSONIPC_MAP_TO_TYPESCRIPT (::int8_t, "number");
1655JSONIPC_MAP_TO_TYPESCRIPT (::uint8_t, "number");
1656JSONIPC_MAP_TO_TYPESCRIPT (::int32_t, "number");
1657JSONIPC_MAP_TO_TYPESCRIPT (::uint32_t, "number");
1658JSONIPC_MAP_TO_TYPESCRIPT (::int64_t, "number");
1659JSONIPC_MAP_TO_TYPESCRIPT (::uint64_t, "number");
1660JSONIPC_MAP_TO_TYPESCRIPT (float, "number");
1661JSONIPC_MAP_TO_TYPESCRIPT (double, "number");
1662JSONIPC_MAP_TO_TYPESCRIPT (const char*, "string");
1663JSONIPC_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:366
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...
const char * string_demangle_cxx(const char *mangled_identifier) noexcept
Demangle identifier via libcc.
Definition cxxaux.cc:18
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:1309
Class & set(const char *name, const F &method)
Add a member function pointer.
Definition jsonipc.hh:1300
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:1469
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:1185
Serializable & set(const char *name, A attribute)
Add a member object pointer.
Definition jsonipc.hh:1195
Serializable()
Allow object handles to be streamed to/from Javascript, needs a Scope for temporaries.
Definition jsonipc.hh:1187
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