From 972b93f95b5cfdc68e914bea4bcf9dbb037ae23c Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 21 Sep 2023 20:08:21 +0100 Subject: Remove void * in class hierarchy handling Replaces the map of typename to function pointer taking void * (which pointed to the std::shared_ptr and cast that to a shared_ptr, thus invoking undefined behaviour) with a map to a small class hierarchy which represents that matching the model class and applying dynamic casts to properly convert as required. --- slicer/slicer/modelParts.h | 7 +++++- slicer/slicer/modelPartsTypes.cpp | 10 ++++----- slicer/slicer/modelPartsTypes.h | 15 +++++++++---- slicer/slicer/modelPartsTypes.impl.h | 43 ++++++++++++++++++++++++++++-------- slicer/tool/parser.cpp | 10 +++++++++ 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/slicer/slicer/modelParts.h b/slicer/slicer/modelParts.h index 98de0d6..d34552e 100644 --- a/slicer/slicer/modelParts.h +++ b/slicer/slicer/modelParts.h @@ -77,6 +77,12 @@ namespace Slicer { class ModelPartForRootBase; class HookCommon; + struct ClassRefBase { + constexpr ClassRefBase() = default; + virtual ~ClassRefBase() = default; + SPECIAL_MEMBERS_DEFAULT(ClassRefBase); + }; + using ModelPartParam = any_ptr; using ModelPartForRootParam = any_ptr; using TypeId = std::optional; @@ -85,7 +91,6 @@ namespace Slicer { using ModelPartHandler = std::function; using ModelPartRootHandler = std::function; using SubPartHandler = std::function; - using ClassRef = std::function; using HookFilter = std::function; constexpr Metadata emptyMetadata; diff --git a/slicer/slicer/modelPartsTypes.cpp b/slicer/slicer/modelPartsTypes.cpp index e78c6c6..c57d987 100644 --- a/slicer/slicer/modelPartsTypes.cpp +++ b/slicer/slicer/modelPartsTypes.cpp @@ -27,7 +27,7 @@ namespace Ice { } namespace Slicer { - using ClassRefMap = std::map>; + using ClassRefMap = std::map>; using ClassNamePair = std::pair; using ClassNameMap = boost::multi_index_container typeName, const ClassRef & cr) + const std::string_view className, const std::optional typeName, const ClassRefBase * cr) { refs->emplace(className, cr); if (typeName) { @@ -206,11 +206,11 @@ namespace Slicer { } } - void - ModelPartForComplexBase::onSubclass(const std::string & name, void * m, const ModelPartHandler & h) + const ClassRefBase * + ModelPartForComplexBase::getSubclassRef(const std::string & name) { if (const auto ref = refs->find(ToModelTypeName(name)); ref != refs->end()) { - return ref->second(m, h); + return ref->second; } throw UnknownType(name); } diff --git a/slicer/slicer/modelPartsTypes.h b/slicer/slicer/modelPartsTypes.h index b30636e..6b6f852 100644 --- a/slicer/slicer/modelPartsTypes.h +++ b/slicer/slicer/modelPartsTypes.h @@ -137,10 +137,10 @@ namespace Slicer { static const ModelPartType type; protected: - void onSubclass(const std::string & name, void * m, const ModelPartHandler &); + const ClassRefBase * getSubclassRef(const std::string & name); static void registerClass( - const std::string_view className, const std::optional typeName, const ClassRef &); + const std::string_view className, const std::optional typeName, const ClassRefBase *); static void unregisterClass(const std::string_view className, const std::optional typeName); static TypeId getTypeId(const std::string & id, const std::string_view className); static std::string demangle(const char * mangled); @@ -196,13 +196,20 @@ namespace Slicer { constinit static const std::string_view className; constinit static const std::optional typeName; - static void CreateModelPart(void *, const ModelPartHandler &); - private: + static const ClassRefBase * const classref; static void registerClass() __attribute__((constructor(210))); static void unregisterClass() __attribute__((destructor(210))); }; + template struct ClassRef { + consteval ClassRef() = default; + virtual ~ClassRef() = default; + SPECIAL_MEMBERS_DEFAULT(ClassRef); + + virtual void onSubClass(std::shared_ptr &, const ModelPartHandler &) const = 0; + }; + template class ModelPartForStruct : public ModelPartForComplex, protected ModelPartModel { public: using element_type = T; diff --git a/slicer/slicer/modelPartsTypes.impl.h b/slicer/slicer/modelPartsTypes.impl.h index f3d1a42..3d1d2be 100644 --- a/slicer/slicer/modelPartsTypes.impl.h +++ b/slicer/slicer/modelPartsTypes.impl.h @@ -448,7 +448,12 @@ namespace Slicer { ModelPartForClass::OnSubclass(const ModelPartHandler & h, const std::string & name) { BOOST_ASSERT(this->Model); - return ModelPartForComplexBase::onSubclass(name, this->Model, h); + const ClassRefBase * refbase = ModelPartForComplexBase::getSubclassRef(name); + BOOST_ASSERT(refbase); + BOOST_ASSERT(dynamic_cast *>(refbase)); + if (auto ref = dynamic_cast *>(refbase)) { + ref->onSubClass(*this->Model, h); + } } template @@ -466,20 +471,40 @@ namespace Slicer { return typeIdProperty; } - template - void - ModelPartForClass::CreateModelPart(void * p, const ModelPartHandler & h) - { - return ::Slicer::ModelPart::CreateFor(static_cast(p), h); - } - template void ModelPartForClass::registerClass() { - ModelPartForComplexBase::registerClass(className, typeName, &ModelPartForClass::CreateModelPart); + ModelPartForComplexBase::registerClass(className, typeName, classref); } + template struct ClassRefImpl : public ClassRef { + consteval ClassRefImpl() = default; + + void + onSubClass(std::shared_ptr & model, const ModelPartHandler & h) const override + { + if constexpr (std::is_same_v) { + ModelPart::CreateFor(&model, h); + } + else { + auto p = std::move(std::dynamic_pointer_cast(model)); + ModelPart::CreateFor(&p, h); + model = std::move(p); + } + } + }; + + template + struct ClassRefImplImpl : + public ClassRefBase, + public ClassRefImpl, + public ClassRefImpl... { + consteval ClassRefImplImpl() = default; + ~ClassRefImplImpl() override = default; + SPECIAL_MEMBERS_DEFAULT(ClassRefImplImpl); + }; + template void ModelPartForClass::unregisterClass() diff --git a/slicer/tool/parser.cpp b/slicer/tool/parser.cpp index 445dda5..8a58484 100644 --- a/slicer/tool/parser.cpp +++ b/slicer/tool/parser.cpp @@ -415,6 +415,16 @@ namespace Slicer { auto name = md.value("slicer:root:"); defineRoot(typeToString(decl), name ? *name : c->name(), decl); + fprintbf(cpp, "static constexpr ClassRefImplImpl<%s", decl->typeId()); + for (const auto & base : decl->definition()->allBases()) { + if (decl != base->declaration()) { + fprintbf(cpp, ", %s", base->declaration()->typeId()); + } + } + fprintbf(cpp, "> ref%d;\n", components); + fprintbf(cpp, "template<>\n"); + fprintbf(cpp, "constinit const ClassRefBase * const ModelPartForClass<%s>::classref{ &ref%d };\n", + decl->typeId(), components); auto typeName = md.value("slicer:typename:"); fprintbf(cpp, "template<>\n"); -- cgit v1.2.3