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