Skip to content

Commit

Permalink
Merge branch 'Jsonipc-dynamic-cast-from-SharedBase'
Browse files Browse the repository at this point in the history
* Branch commit log:
  ase/: remove all JSONIPC_INHERIT() macros, now unused
  jsonipc/jsonipc.hh: remove unused wrap_object_from_base
  ase/serialize: use Wrapper->classname() in wrapper_to_json()
  jsonipc/jsonipc.hh: use Wrapper->classname() in wrapper_to_json()
  ase/jsonapi.hh: use Ase::SharedBase as JSONIPC_CUSTOM_SHARED_BASE
  jsonipc/jsonipc.hh: always use dynamic cast from SharedBase for wrappers

Signed-off-by: Tim Janik <timj@gnu.org>
  • Loading branch information
tim-janik committed Nov 19, 2024
2 parents 01a1166 + 553749f commit f0e7a8b
Show file tree
Hide file tree
Showing 14 changed files with 63 additions and 95 deletions.
2 changes: 0 additions & 2 deletions ase/clapdevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ struct ClapPropertyImpl : public Property, public virtual EmittableImpl {
};

// == ClapDeviceImpl ==
JSONIPC_INHERIT (ClapDeviceImpl, Device);

ClapDeviceImpl::ClapDeviceImpl (ClapPluginHandleP claphandle) :
handle_ (claphandle)
{
Expand Down
2 changes: 0 additions & 2 deletions ase/clip.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ ClipNote::operator== (const ClipNote &o) const
}

// == ClipImpl ==
JSONIPC_INHERIT (ClipImpl, Clip);

ClipImpl::ClipImpl (TrackImpl &parent)
{
track_ = &parent;
Expand Down
2 changes: 0 additions & 2 deletions ase/crawler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 0 additions & 2 deletions ase/gadget.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ Gadget::Gadget() :
{}

// == GadgetImpl ==
JSONIPC_INHERIT (GadgetImpl, Gadget);

GadgetImpl::~GadgetImpl()
{}

Expand Down
5 changes: 0 additions & 5 deletions ase/internal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
Expand Down
2 changes: 2 additions & 0 deletions ase/jsonapi.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <ase/websocket.hh>
#include <ase/value.hh>

#define JSONIPC_CUSTOM_SHARED_BASE ::Ase::SharedBase
#include <jsonipc/jsonipc.hh>

namespace Ase {
Expand Down
2 changes: 0 additions & 2 deletions ase/monitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
namespace Ase {

// == MonitorImpl ==
JSONIPC_INHERIT (MonitorImpl, Monitor);

MonitorImpl::MonitorImpl()
{}

Expand Down
2 changes: 0 additions & 2 deletions ase/nativedevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<AudioCombo> (aproc)), info_ (extract_info (aseid, static_info))
{
Expand Down
2 changes: 0 additions & 2 deletions ase/project.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ Project::last_project()
}

// == ProjectImpl ==
JSONIPC_INHERIT (ProjectImpl, Project);

using StringPairS = std::vector<std::tuple<String,String>>;

struct ProjectImpl::PStorage {
Expand Down
6 changes: 3 additions & 3 deletions ase/serialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down
2 changes: 1 addition & 1 deletion ase/serialize.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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; };
Expand Down
2 changes: 0 additions & 2 deletions ase/server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
namespace Ase {

// == ServerImpl ==
JSONIPC_INHERIT (ServerImpl, Server);

static constexpr size_t telemetry_size = 4 * 1024 * 1024;

ServerImpl *SERVER = nullptr;
Expand Down
2 changes: 0 additions & 2 deletions ase/track.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
namespace Ase {

// == TrackImpl ==
JSONIPC_INHERIT (TrackImpl, Track);

TrackImpl::TrackImpl (ProjectImpl &project, bool masterflag)
{
gadget_flags (masterflag ? MASTER_TRACK : 0);
Expand Down
125 changes: 57 additions & 68 deletions jsonipc/jsonipc.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<JsonipcSharedBase> {
virtual ~JsonipcSharedBase() {}
};
using SharedBase = JsonipcSharedBase;
#endif

// == Json types ==
using JsonValue = rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >;
using JsonAllocator = rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>;
Expand Down Expand Up @@ -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<SharedBase> &sptr, size_t &basedepth);
static std::vector<CreateWrapper>& wrapper_creators() { static std::vector<CreateWrapper> wrapper_creators_; return wrapper_creators_; }
static void
register_wrapper (CreateWrapper createwrapper)
{
wrapper_creators().push_back (createwrapper);
}
template<typename T>
class InstanceWrapper : public Wrapper {
std::shared_ptr<T> sptr_;
Expand Down Expand Up @@ -664,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<typename T> static JsonValue
Expand All @@ -686,7 +702,19 @@ public:
if (it == imap->typeid_map_.end())
{
thisid = next_counter();
wrapper = new InstanceWrapper<T> (sptr);
if constexpr (std::is_base_of_v<SharedBase, T>) {
std::vector<CreateWrapper> &wcreators = wrapper_creators(); // using CreateWrapper = Wrapper* (*) (const std::shared_ptr<SharedBase> &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<T> (sptr);
imap->wmap_[thisid] = wrapper;
imap->typeid_map_[tkey] = thisid;
}
Expand All @@ -705,7 +733,7 @@ public:
* Class<MostDerived> is unregisterd. In this case, ptr0x123 can be wrapped multiple
* times through different base classes.
*/
return imap->wrapper_to_json (wrapper, thisid, rtti_typename<T>(), allocator);
return imap->wrapper_to_json (wrapper, thisid, allocator);
}
virtual Wrapper*
wrapper_from_json (const JsonValue &value)
Expand Down Expand Up @@ -1333,27 +1361,30 @@ 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<std::string, WrapObjectFromBase*> downcastwrappers;
if (handler)
{
downcastwrappers[rttiname] = handler;
return handler;
}
auto it = downcastwrappers.find (rttiname);
return it != downcastwrappers.end() ? it->second : nullptr;
}

// == Class ==
template<typename T>
struct Class final : TypeInfo {
Class () : TypeInfo (ClassPrinter::create<T> (ClassPrinter::CLASSES)) {}
Class () :
TypeInfo (ClassPrinter::create<T> (ClassPrinter::CLASSES))
{
auto create_wrapper = [] (const std::shared_ptr<SharedBase> &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<T> derived_sptr = std::dynamic_pointer_cast<T> (sptr);
if (derived_sptr.get()) {
basedepth = class_depth;
return new InstanceMap::InstanceWrapper<T> (derived_sptr);
}
}
return nullptr;
};
InstanceMap::register_wrapper (create_wrapper);
}
// Inherit base class `B`
template<typename B> Class&
inherit()
Expand Down Expand Up @@ -1496,37 +1527,22 @@ private:
std::string basetypename;
size_t (*base_depth) ();
bool (*upcast_impl) (const std::shared_ptr<T>&, const std::string&, void*) = NULL;
bool (*downcast_impl) (const std::string&, void*, std::shared_ptr<T>*) = NULL;
Closure* (*lookup_closure) (const char*) = NULL;
};
using BaseVec = std::vector<BaseInfo>;
template<typename B> void
add_base ()
{
BaseVec &bvec = basevec();
BaseInfo binfo { typename_of<B>(), Class<B>::base_depth, &upcast_impl<B>, &Class<B>::template downcast_impl<T>, &Class<B>::lookup_closure, };
BaseInfo binfo { typename_of<B>(), Class<B>::base_depth, &upcast_impl<B>, &Class<B>::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<B> 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<T> sptr;
downcast_impl<T> (baseclass, sptrB, &sptr);
if (sptr)
{
JSONIPC_ASSERT_RETURN (rtti_typename (*sptr) == rtti_typename<T>(), JsonValue()); // null
return InstanceMap::scope_wrap_object<T> (sptr, allocator);
}
return JsonValue(); // null
}
template<typename B> static bool
upcast_impl (const std::shared_ptr<T> &sptr, const std::string &baseclass, void *sptrB)
{
Expand Down Expand Up @@ -1578,27 +1594,6 @@ public:
return true;
return false;
}
template<typename D> static bool
downcast_impl (const std::string &baseclass, void *sptrB, std::shared_ptr<D> *sptrD)
{
if (classname() == baseclass)
{
std::shared_ptr<T> *bptr = static_cast<std::shared_ptr<T>*> (sptrB);
*sptrD = std::dynamic_pointer_cast<D> (*bptr);
return true;
}
BaseVec &bvec = basevec();
for (const auto &it : bvec)
{
std::shared_ptr<T> sptr;
if (it.downcast_impl (baseclass, sptrB, &sptr))
{
*sptrD = std::dynamic_pointer_cast<D> (sptr);
return true;
}
}
return false;
}
};

/// Template class to identify wrappable classes
Expand Down Expand Up @@ -1635,15 +1630,9 @@ struct Convert<std::shared_ptr<T>, REQUIRESv< IsWrappableClass<T>::value >> {
return sptr ? Serializable<ClassType>::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 = can_wrap_object_from_base (impltype);
JsonValue result;
if (wrap_object_from_base)
result = wrap_object_from_base (rtti_typename<ClassType>(), const_cast<std::shared_ptr<ClassType>*> (&sptr), allocator);
// fallback to wrap sptr as baseclass T
if (result.IsNull())
result = InstanceMap::scope_wrap_object<ClassType> (sptr, allocator);
JsonValue result = InstanceMap::scope_wrap_object<ClassType> (sptr, allocator);
return result;
}
return JsonValue(); // null
Expand Down

0 comments on commit f0e7a8b

Please sign in to comment.