From 32601953e178bf2615487ca77a85e8871d1d7f11 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 25 Sep 2024 17:11:59 +0200 Subject: [PATCH 1/6] jsonipc/jsonipc.hh: always use dynamic cast from SharedBase for wrappers Signed-off-by: Tim Janik --- jsonipc/jsonipc.hh | 56 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/jsonipc/jsonipc.hh b/jsonipc/jsonipc.hh index 50129966..0d823a5f 100644 --- a/jsonipc/jsonipc.hh +++ b/jsonipc/jsonipc.hh @@ -25,6 +25,16 @@ namespace Jsonipc { +#ifdef JSONIPC_CUSTOM_SHARED_BASE +using SharedBase = JSONIPC_CUSTOM_SHARED_BASE; +#else +/// Common base type for polymorphic classes managed by `std::shared_ptr<>`. +struct JsonipcSharedBase : public virtual std::enable_shared_from_this { + virtual ~JsonipcSharedBase() {} +}; +using SharedBase = JsonipcSharedBase; +#endif + // == Json types == using JsonValue = rapidjson::GenericValue, rapidjson::MemoryPoolAllocator >; using JsonAllocator = rapidjson::MemoryPoolAllocator; @@ -546,7 +556,13 @@ public: virtual void try_upcast (const std::string &baseclass, void *sptrB) = 0; virtual std::string classname () = 0; }; -private: + using CreateWrapper = Wrapper* (*) (const std::shared_ptr &sptr, size_t &basedepth); + static std::vector& wrapper_creators() { static std::vector wrapper_creators_; return wrapper_creators_; } + static void + register_wrapper (CreateWrapper createwrapper) + { + wrapper_creators().push_back (createwrapper); + } template class InstanceWrapper : public Wrapper { std::shared_ptr sptr_; @@ -686,7 +702,19 @@ public: if (it == imap->typeid_map_.end()) { thisid = next_counter(); - wrapper = new InstanceWrapper (sptr); + if constexpr (std::is_base_of_v) { + std::vector &wcreators = wrapper_creators(); // using CreateWrapper = Wrapper* (*) (const std::shared_ptr &sptr, size_t &basedepth); + size_t basedepth = 0; + for (size_t i = 0; i < wcreators.size(); i++) { + Wrapper *w = wcreators[i] (sptr, basedepth); + if (w) { + delete wrapper; + wrapper = w; + } + } + } + if (!wrapper) + wrapper = new InstanceWrapper (sptr); imap->wmap_[thisid] = wrapper; imap->typeid_map_[tkey] = thisid; } @@ -1353,7 +1381,27 @@ can_wrap_object_from_base (const std::string &rttiname, WrapObjectFromBase *hand // == Class == template struct Class final : TypeInfo { - Class () : TypeInfo (ClassPrinter::create (ClassPrinter::CLASSES)) {} + Class () : + TypeInfo (ClassPrinter::create (ClassPrinter::CLASSES)) + { + auto create_wrapper = [] (const std::shared_ptr &sptr, size_t &basedepth) -> InstanceMap::Wrapper* + { + /* This is an exhaustive search for the best (most derived) wrapper type for + * an object. We currently use a linear search that can involve as many + * dynamic casts as the inheritance depth of the registered wrappers. + */ + const size_t class_depth = Class::base_depth(); + if (class_depth > basedepth) { + std::shared_ptr derived_sptr = std::dynamic_pointer_cast (sptr); + if (derived_sptr.get()) { + basedepth = class_depth; + return new InstanceMap::InstanceWrapper (derived_sptr); + } + } + return nullptr; + }; + InstanceMap::register_wrapper (create_wrapper); + } // Inherit base class `B` template Class& inherit() @@ -1637,7 +1685,7 @@ struct Convert, REQUIRESv< IsWrappableClass::value >> { { // try to call the most derived wrapper Class from the RTTI type of sptr const std::string impltype = rtti_typename (*sptr); - WrapObjectFromBase *wrap_object_from_base = can_wrap_object_from_base (impltype); + WrapObjectFromBase *wrap_object_from_base = nullptr; // FIXME: can_wrap_object_from_base (impltype); JsonValue result; if (wrap_object_from_base) result = wrap_object_from_base (rtti_typename(), const_cast*> (&sptr), allocator); From 7ea91646c1f4babe85a1973cf4c999e45107d389 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 25 Sep 2024 17:11:59 +0200 Subject: [PATCH 2/6] ase/jsonapi.hh: use Ase::SharedBase as JSONIPC_CUSTOM_SHARED_BASE Signed-off-by: Tim Janik --- ase/jsonapi.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ase/jsonapi.hh b/ase/jsonapi.hh index f1be0e67..553ec8d1 100644 --- a/ase/jsonapi.hh +++ b/ase/jsonapi.hh @@ -4,6 +4,8 @@ #include #include + +#define JSONIPC_CUSTOM_SHARED_BASE ::Ase::SharedBase #include namespace Ase { From c9b0177f90dcf30f0fa842cfc567dbd470c79d20 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 25 Sep 2024 18:07:34 +0200 Subject: [PATCH 3/6] jsonipc/jsonipc.hh: use Wrapper->classname() in wrapper_to_json() Signed-off-by: Tim Janik --- jsonipc/jsonipc.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jsonipc/jsonipc.hh b/jsonipc/jsonipc.hh index 0d823a5f..64d188d3 100644 --- a/jsonipc/jsonipc.hh +++ b/jsonipc/jsonipc.hh @@ -680,13 +680,13 @@ public: JSONIPC_ASSERT_RETURN (typeid_map_.size() == 0); // deleters shouldn't re-add } virtual JsonValue - wrapper_to_json (Wrapper *wrapper, const size_t thisid, const std::string &wraptype, JsonAllocator &allocator) + wrapper_to_json (Wrapper *wrapper, const size_t thisid, JsonAllocator &allocator) { if (!wrapper) return JsonValue(); // null JsonValue jobject (rapidjson::kObjectType); jobject.AddMember ("$id", thisid, allocator); - jobject.AddMember ("$class", JsonValue (wraptype.c_str(), allocator), allocator); + jobject.AddMember ("$class", JsonValue (wrapper->classname().c_str(), allocator), allocator); return jobject; } template static JsonValue @@ -733,7 +733,7 @@ public: * Class is unregisterd. In this case, ptr0x123 can be wrapped multiple * times through different base classes. */ - return imap->wrapper_to_json (wrapper, thisid, rtti_typename(), allocator); + return imap->wrapper_to_json (wrapper, thisid, allocator); } virtual Wrapper* wrapper_from_json (const JsonValue &value) From 8c62b7b9ef991a5f93afef58d1ed136af23466e3 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 25 Sep 2024 18:07:34 +0200 Subject: [PATCH 4/6] ase/serialize: use Wrapper->classname() in wrapper_to_json() Signed-off-by: Tim Janik --- ase/serialize.cc | 6 +++--- ase/serialize.hh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ase/serialize.cc b/ase/serialize.cc index 8dfb2887..3cb854e2 100644 --- a/ase/serialize.cc +++ b/ase/serialize.cc @@ -9,11 +9,11 @@ namespace Ase { // == Writ::InstanceMap == Jsonipc::JsonValue -Writ::InstanceMap::wrapper_to_json (Wrapper *wrapper, const size_t thisid, const std::string &wraptype, Jsonipc::JsonAllocator &allocator) +Writ::InstanceMap::wrapper_to_json (Wrapper *wrapper, const size_t thisid, Jsonipc::JsonAllocator &allocator) { - warning ("Ase::Writ: object pointer is not persistent: (%s*) {\"$id\":%d}", wraptype, thisid); + warning ("Ase::Writ: object pointer is not persistent: (%s*) {\"$id\":%d}", wrapper->classname(), thisid); //return Jsonipc::JsonValue(); // null - return this->Jsonipc::InstanceMap::wrapper_to_json (wrapper, thisid, wraptype, allocator); + return this->Jsonipc::InstanceMap::wrapper_to_json (wrapper, thisid, allocator); } Jsonipc::InstanceMap::Wrapper* diff --git a/ase/serialize.hh b/ase/serialize.hh index 117603cb..743c4041 100644 --- a/ase/serialize.hh +++ b/ase/serialize.hh @@ -73,7 +73,7 @@ class Writ { bool in_load_ = false, in_save_ = false, skip_zero_ = false, skip_emptystring_ = false, relaxed_ = false; ValueP dummy_; struct InstanceMap : Jsonipc::InstanceMap { - Jsonipc::JsonValue wrapper_to_json (Wrapper*, size_t, const std::string&, Jsonipc::JsonAllocator&) override; + Jsonipc::JsonValue wrapper_to_json (Wrapper*, size_t, Jsonipc::JsonAllocator&) override; Wrapper* wrapper_from_json (const Jsonipc::JsonValue&) override; } instance_map_; struct LinkEntry { ValueP value; Serializable *sp = nullptr; int64 id = 0; }; From f020a8394a677a0a91dd6a18489d5b255689740c Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Thu, 26 Sep 2024 19:24:34 +0200 Subject: [PATCH 5/6] jsonipc/jsonipc.hh: remove unused wrap_object_from_base Signed-off-by: Tim Janik --- jsonipc/jsonipc.hh | 65 +++------------------------------------------- 1 file changed, 3 insertions(+), 62 deletions(-) diff --git a/jsonipc/jsonipc.hh b/jsonipc/jsonipc.hh index 64d188d3..604ef9c1 100644 --- a/jsonipc/jsonipc.hh +++ b/jsonipc/jsonipc.hh @@ -1361,23 +1361,6 @@ private: static SerializeToJson& serialize_to_json_ () { static SerializeToJson impl; return impl; } }; -// == Helper for known derived classes by RTTI typename == -using WrapObjectFromBase = JsonValue (const std::string&, void*, JsonAllocator&); - -// This *MUST* use `extern inline` for the ODR to apply to its `static` variable -extern inline WrapObjectFromBase* -can_wrap_object_from_base (const std::string &rttiname, WrapObjectFromBase *handler = nullptr) -{ - static std::map downcastwrappers; - if (handler) - { - downcastwrappers[rttiname] = handler; - return handler; - } - auto it = downcastwrappers.find (rttiname); - return it != downcastwrappers.end() ? it->second : nullptr; -} - // == Class == template struct Class final : TypeInfo { @@ -1544,7 +1527,6 @@ private: std::string basetypename; size_t (*base_depth) (); bool (*upcast_impl) (const std::shared_ptr&, const std::string&, void*) = NULL; - bool (*downcast_impl) (const std::string&, void*, std::shared_ptr*) = NULL; Closure* (*lookup_closure) (const char*) = NULL; }; using BaseVec = std::vector; @@ -1552,29 +1534,15 @@ private: add_base () { BaseVec &bvec = basevec(); - BaseInfo binfo { typename_of(), Class::base_depth, &upcast_impl, &Class::template downcast_impl, &Class::lookup_closure, }; + BaseInfo binfo { typename_of(), Class::base_depth, &upcast_impl, &Class::lookup_closure, }; for (const auto &it : bvec) if (it.basetypename == binfo.basetypename) throw std::runtime_error ("duplicate base registration: " + binfo.basetypename); - if (bvec.empty()) - can_wrap_object_from_base (classname(), wrap_object_from_base); bvec.push_back (binfo); Class bclass; printer_->set_depth_func (this->base_depth); } static BaseVec& basevec () { static BaseVec basevec_; return basevec_; } - static JsonValue - wrap_object_from_base (const std::string &baseclass, void *sptrB, JsonAllocator &allocator) - { - std::shared_ptr sptr; - downcast_impl (baseclass, sptrB, &sptr); - if (sptr) - { - JSONIPC_ASSERT_RETURN (rtti_typename (*sptr) == rtti_typename(), JsonValue()); // null - return InstanceMap::scope_wrap_object (sptr, allocator); - } - return JsonValue(); // null - } template static bool upcast_impl (const std::shared_ptr &sptr, const std::string &baseclass, void *sptrB) { @@ -1626,27 +1594,6 @@ public: return true; return false; } - template static bool - downcast_impl (const std::string &baseclass, void *sptrB, std::shared_ptr *sptrD) - { - if (classname() == baseclass) - { - std::shared_ptr *bptr = static_cast*> (sptrB); - *sptrD = std::dynamic_pointer_cast (*bptr); - return true; - } - BaseVec &bvec = basevec(); - for (const auto &it : bvec) - { - std::shared_ptr sptr; - if (it.downcast_impl (baseclass, sptrB, &sptr)) - { - *sptrD = std::dynamic_pointer_cast (sptr); - return true; - } - } - return false; - } }; /// Template class to identify wrappable classes @@ -1683,15 +1630,9 @@ struct Convert, REQUIRESv< IsWrappableClass::value >> { return sptr ? Serializable::serialize_to_json (*sptr, allocator) : JsonValue (rapidjson::kObjectType); if (sptr) { - // try to call the most derived wrapper Class from the RTTI type of sptr + // Wrap sptr, determine most derived wrapper via dynamic casts const std::string impltype = rtti_typename (*sptr); - WrapObjectFromBase *wrap_object_from_base = nullptr; // FIXME: can_wrap_object_from_base (impltype); - JsonValue result; - if (wrap_object_from_base) - result = wrap_object_from_base (rtti_typename(), const_cast*> (&sptr), allocator); - // fallback to wrap sptr as baseclass T - if (result.IsNull()) - result = InstanceMap::scope_wrap_object (sptr, allocator); + JsonValue result = InstanceMap::scope_wrap_object (sptr, allocator); return result; } return JsonValue(); // null From 553749fb327284343528392b6c7eee0a93964ee9 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Sep 2024 02:43:19 +0200 Subject: [PATCH 6/6] ase/: remove all JSONIPC_INHERIT() macros, now unused Signed-off-by: Tim Janik --- ase/clapdevice.cc | 2 -- ase/clip.cc | 2 -- ase/crawler.cc | 2 -- ase/gadget.cc | 2 -- ase/internal.hh | 5 ----- ase/monitor.cc | 2 -- ase/nativedevice.cc | 2 -- ase/project.cc | 2 -- ase/server.cc | 2 -- ase/track.cc | 2 -- 10 files changed, 23 deletions(-) diff --git a/ase/clapdevice.cc b/ase/clapdevice.cc index 8f326bd2..a824faaa 100644 --- a/ase/clapdevice.cc +++ b/ase/clapdevice.cc @@ -104,8 +104,6 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl { }; // == ClapDeviceImpl == -JSONIPC_INHERIT (ClapDeviceImpl, Device); - ClapDeviceImpl::ClapDeviceImpl (ClapPluginHandleP claphandle) : handle_ (claphandle) { diff --git a/ase/clip.cc b/ase/clip.cc index a4cf68d7..2f5fa3fb 100644 --- a/ase/clip.cc +++ b/ase/clip.cc @@ -36,8 +36,6 @@ ClipNote::operator== (const ClipNote &o) const } // == ClipImpl == -JSONIPC_INHERIT (ClipImpl, Clip); - ClipImpl::ClipImpl (TrackImpl &parent) { track_ = &parent; diff --git a/ase/crawler.cc b/ase/crawler.cc index 9d7924a4..d94dd3a7 100644 --- a/ase/crawler.cc +++ b/ase/crawler.cc @@ -25,8 +25,6 @@ ResourceCrawler::ResourceCrawler() : {} // == FileCrawler == -JSONIPC_INHERIT (FileCrawler, ResourceCrawler); - FileCrawler::FileCrawler (const String &cwd, bool constraindir, bool constrainfile) : cwd_ ("/"), constraindir_ (constraindir), constrainfile_ (constrainfile) { diff --git a/ase/gadget.cc b/ase/gadget.cc index 165eacae..03ad0a12 100644 --- a/ase/gadget.cc +++ b/ase/gadget.cc @@ -15,8 +15,6 @@ Gadget::Gadget() : {} // == GadgetImpl == -JSONIPC_INHERIT (GadgetImpl, Gadget); - GadgetImpl::~GadgetImpl() {} diff --git a/ase/internal.hh b/ase/internal.hh index f5b32f86..6d5291e0 100644 --- a/ase/internal.hh +++ b/ase/internal.hh @@ -73,11 +73,6 @@ using Ase::String; /// Create a Ase::StringVector, from a const char* C-style array. #define STRING_VECTOR_FROM_ARRAY(ConstCharArray) ASE_STRING_VECTOR_FROM_ARRAY(ConstCharArray) -/// Register `IMPL` with Jsonipc and indicate it inherits from `INTERFACE`. -#define JSONIPC_INHERIT(IMPL, INTERFACE) \ - [[maybe_unused]] static bool ASE_CPP_PASTE2 (ase_inherit__, __COUNTER__) = \ - ( Jsonipc::Class< IMPL >().inherit< INTERFACE >() , 0 ) - /// Register `func` as an integrity test. #define TEST_INTEGRITY(FUNC) static void FUNC() __attribute__ ((__cold__, __unused__)); \ static ::Ase::Test::IntegrityCheck ASE_CPP_PASTE2 (__Ase__Test__IntegrityCheck__line, __LINE__) { #FUNC, FUNC, 'I' } diff --git a/ase/monitor.cc b/ase/monitor.cc index 5ed5d545..f8fa5651 100644 --- a/ase/monitor.cc +++ b/ase/monitor.cc @@ -7,8 +7,6 @@ namespace Ase { // == MonitorImpl == -JSONIPC_INHERIT (MonitorImpl, Monitor); - MonitorImpl::MonitorImpl() {} diff --git a/ase/nativedevice.cc b/ase/nativedevice.cc index b09e8dc8..a574aad4 100644 --- a/ase/nativedevice.cc +++ b/ase/nativedevice.cc @@ -10,8 +10,6 @@ namespace Ase { // == NativeDeviceImpl == -JSONIPC_INHERIT (NativeDeviceImpl, NativeDevice); - NativeDeviceImpl::NativeDeviceImpl (const String &aseid, AudioProcessor::StaticInfo static_info, AudioProcessorP aproc) : proc_ (aproc), combo_ (std::dynamic_pointer_cast (aproc)), info_ (extract_info (aseid, static_info)) { diff --git a/ase/project.cc b/ase/project.cc index 7cdaeaff..fc7a6242 100644 --- a/ase/project.cc +++ b/ase/project.cc @@ -41,8 +41,6 @@ Project::last_project() } // == ProjectImpl == -JSONIPC_INHERIT (ProjectImpl, Project); - using StringPairS = std::vector>; struct ProjectImpl::PStorage { diff --git a/ase/server.cc b/ase/server.cc index 28ede0ae..b111562c 100644 --- a/ase/server.cc +++ b/ase/server.cc @@ -18,8 +18,6 @@ namespace Ase { // == ServerImpl == -JSONIPC_INHERIT (ServerImpl, Server); - static constexpr size_t telemetry_size = 4 * 1024 * 1024; ServerImpl *SERVER = nullptr; diff --git a/ase/track.cc b/ase/track.cc index 6fbbfbb4..d51aa045 100644 --- a/ase/track.cc +++ b/ase/track.cc @@ -14,8 +14,6 @@ namespace Ase { // == TrackImpl == -JSONIPC_INHERIT (TrackImpl, Track); - TrackImpl::TrackImpl (ProjectImpl &project, bool masterflag) { gadget_flags (masterflag ? MASTER_TRACK : 0);