diff options
-rw-r--r-- | slicer/slicer/common.ice | 5 | ||||
-rw-r--r-- | slicer/slicer/modelParts.h | 7 | ||||
-rw-r--r-- | slicer/slicer/modelPartsTypes.cpp | 16 | ||||
-rw-r--r-- | slicer/slicer/modelPartsTypes.h | 16 | ||||
-rw-r--r-- | slicer/slicer/modelPartsTypes.impl.h | 44 | ||||
-rw-r--r-- | slicer/slicer/slicer.cpp | 8 | ||||
-rw-r--r-- | slicer/test/Jamfile.jam | 5 | ||||
-rw-r--r-- | slicer/test/initial/inherit-base.xml | 4 | ||||
-rw-r--r-- | slicer/test/initial/inherit-nosuchtype.xml | 5 | ||||
-rw-r--r-- | slicer/test/initial/inherit-same.xml | 4 | ||||
-rw-r--r-- | slicer/test/initial/inherit-wronghier.xml | 5 | ||||
-rw-r--r-- | slicer/test/serializers.cpp | 34 | ||||
-rw-r--r-- | slicer/tool/parser.cpp | 10 |
13 files changed, 141 insertions, 22 deletions
diff --git a/slicer/slicer/common.ice b/slicer/slicer/common.ice index 60a4f92..ad3ff1f 100644 --- a/slicer/slicer/common.ice +++ b/slicer/slicer/common.ice @@ -29,6 +29,11 @@ module Slicer { string type; }; ["cpp:ice_print"] + exception IncorrectType extends DeserializerError { + string type; + string target; + }; + ["cpp:ice_print"] exception InvalidEnumerationValue extends SerializerError { int value; string type; 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<ModelPart>; using ModelPartForRootParam = any_ptr<ModelPartForRootBase>; using TypeId = std::optional<std::string>; @@ -85,7 +91,6 @@ namespace Slicer { using ModelPartHandler = std::function<void(ModelPartParam)>; using ModelPartRootHandler = std::function<void(ModelPartForRootParam)>; using SubPartHandler = std::function<void(ModelPartParam, const Metadata &)>; - using ClassRef = std::function<void(void *, const ModelPartHandler &)>; using HookFilter = std::function<bool(const HookCommon *)>; constexpr Metadata emptyMetadata; diff --git a/slicer/slicer/modelPartsTypes.cpp b/slicer/slicer/modelPartsTypes.cpp index e78c6c6..ffaec04 100644 --- a/slicer/slicer/modelPartsTypes.cpp +++ b/slicer/slicer/modelPartsTypes.cpp @@ -27,7 +27,7 @@ namespace Ice { } namespace Slicer { - using ClassRefMap = std::map<std::string, ClassRef, std::less<>>; + using ClassRefMap = std::map<std::string, const ClassRefBase *, std::less<>>; using ClassNamePair = std::pair<std::string_view, std::string>; using ClassNameMap = boost::multi_index_container<ClassNamePair, boost::multi_index::indexed_by< @@ -74,6 +74,12 @@ namespace Slicer { return name; } + [[noreturn]] void + ModelPartForComplexBase::throwIncorrectType(const std::string & name, const std::type_info & target) + { + throw IncorrectType(name, demangle(target.name())); + } + #define Roots(Type, Name, NameLen) \ template<> CONSTSTR(NameLen) Slicer::ModelPartForRoot<Type>::rootName {#Name}; \ template<> \ @@ -186,7 +192,7 @@ namespace Slicer { void ModelPartForComplexBase::registerClass( - const std::string_view className, const std::optional<std::string_view> typeName, const ClassRef & cr) + const std::string_view className, const std::optional<std::string_view> typeName, const ClassRefBase * cr) { refs->emplace(className, cr); if (typeName) { @@ -206,11 +212,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..f581c49 100644 --- a/slicer/slicer/modelPartsTypes.h +++ b/slicer/slicer/modelPartsTypes.h @@ -137,16 +137,17 @@ 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<std::string_view> typeName, const ClassRef &); + const std::string_view className, const std::optional<std::string_view> typeName, const ClassRefBase *); static void unregisterClass(const std::string_view className, const std::optional<std::string_view> typeName); static TypeId getTypeId(const std::string & id, const std::string_view className); static std::string demangle(const char * mangled); static const std::string & ToExchangeTypeName(const std::string &); static std::string_view ToModelTypeName(const std::string &); + [[noreturn]] static void throwIncorrectType(const std::string & name, const std::type_info & target); }; template<typename T> class Hooks; @@ -196,13 +197,20 @@ namespace Slicer { constinit static const std::string_view className; constinit static const std::optional<const std::string_view> 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<typename T> struct ClassRef { + consteval ClassRef() = default; + virtual ~ClassRef() = default; + SPECIAL_MEMBERS_DEFAULT(ClassRef); + + virtual void onSubClass(std::shared_ptr<T> &, const ModelPartHandler &) const = 0; + }; + template<typename T> class ModelPartForStruct : public ModelPartForComplex<T>, protected ModelPartModel<T> { public: using element_type = T; diff --git a/slicer/slicer/modelPartsTypes.impl.h b/slicer/slicer/modelPartsTypes.impl.h index f3d1a42..60f221d 100644 --- a/slicer/slicer/modelPartsTypes.impl.h +++ b/slicer/slicer/modelPartsTypes.impl.h @@ -448,7 +448,13 @@ namespace Slicer { ModelPartForClass<T>::OnSubclass(const ModelPartHandler & h, const std::string & name) { BOOST_ASSERT(this->Model); - return ModelPartForComplexBase::onSubclass(name, this->Model, h); + if (const ClassRefBase * refbase = ModelPartForComplexBase::getSubclassRef(name); + auto ref = dynamic_cast<const ClassRef<T> *>(refbase)) [[likely]] { + ref->onSubClass(*this->Model, h); + } + else { + ModelPartForComplexBase::throwIncorrectType(name, typeid(T)); + } } template<typename T> @@ -468,18 +474,38 @@ namespace Slicer { template<typename T> void - ModelPartForClass<T>::CreateModelPart(void * p, const ModelPartHandler & h) - { - return ::Slicer::ModelPart::CreateFor(static_cast<element_type *>(p), h); - } - - template<typename T> - void ModelPartForClass<T>::registerClass() { - ModelPartForComplexBase::registerClass(className, typeName, &ModelPartForClass<T>::CreateModelPart); + ModelPartForComplexBase::registerClass(className, typeName, classref); } + template<typename Inst, typename T> struct ClassRefImpl : public ClassRef<T> { + consteval ClassRefImpl() = default; + + void + onSubClass(std::shared_ptr<T> & model, const ModelPartHandler & h) const override + { + if constexpr (std::is_same_v<T, Inst>) { + ModelPart::CreateFor(&model, h); + } + else { + auto p = std::move(std::dynamic_pointer_cast<Inst>(model)); + ModelPart::CreateFor(&p, h); + model = std::move(p); + } + } + }; + + template<typename Inst, typename... Bases> + struct ClassRefImplImpl : + public ClassRefBase, + public ClassRefImpl<Inst, Inst>, + public ClassRefImpl<Inst, Bases>... { + consteval ClassRefImplImpl() = default; + ~ClassRefImplImpl() override = default; + SPECIAL_MEMBERS_DEFAULT(ClassRefImplImpl); + }; + template<typename T> void ModelPartForClass<T>::unregisterClass() diff --git a/slicer/slicer/slicer.cpp b/slicer/slicer/slicer.cpp index ff9a948..5a253b0 100644 --- a/slicer/slicer/slicer.cpp +++ b/slicer/slicer/slicer.cpp @@ -13,6 +13,14 @@ namespace Slicer { InvalidEnumerationSymbolMsg::write(s, symbol, type); } + AdHocFormatter(IncorrectTypeMsg, "Type [%?] cannot be used as a [%?]"); + + void + IncorrectType::ice_print(std::ostream & s) const + { + IncorrectTypeMsg::write(s, type, target); + } + AdHocFormatter(InvalidEnumerationValueMsg, "Invalid enumeration symbol [%?] for type [%?]"); void diff --git a/slicer/test/Jamfile.jam b/slicer/test/Jamfile.jam index 4ed71fb..20cc95e 100644 --- a/slicer/test/Jamfile.jam +++ b/slicer/test/Jamfile.jam @@ -1,4 +1,5 @@ import ./slicer.jam ; +import sequence ; lib dl ; lib stdc++fs ; @@ -81,7 +82,7 @@ run compilation.cpp run serializers.cpp helpers.cpp - : : : + : -- : [ sequence.insertion-sort [ glob-tree-ex initial included expected : *.json *.xml ] ] : <library>types <implicit-dependency>types <library>common @@ -128,7 +129,7 @@ run <use>../xml//slicer-xml <use>../json//slicer-json ] - : : : + : -- : [ sequence.insertion-sort [ glob-tree-ex initial included expected : *.json *.xml ] ] : <library>benchmark <library>stdc++fs <library>common diff --git a/slicer/test/initial/inherit-base.xml b/slicer/test/initial/inherit-base.xml new file mode 100644 index 0000000..45456e2 --- /dev/null +++ b/slicer/test/initial/inherit-base.xml @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<D12 slicer-typeid="::TestModule::Base2"> + <a>3</a> +</D12> diff --git a/slicer/test/initial/inherit-nosuchtype.xml b/slicer/test/initial/inherit-nosuchtype.xml new file mode 100644 index 0000000..057bea5 --- /dev/null +++ b/slicer/test/initial/inherit-nosuchtype.xml @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<D12 slicer-typeid="::TestModule::NoSuchType"> + <a>3</a> + <b>3</b> +</D12> diff --git a/slicer/test/initial/inherit-same.xml b/slicer/test/initial/inherit-same.xml new file mode 100644 index 0000000..77a22ec --- /dev/null +++ b/slicer/test/initial/inherit-same.xml @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<D1 slicer-typeid="::TestModule::D1"> + <a>3</a> +</D1> diff --git a/slicer/test/initial/inherit-wronghier.xml b/slicer/test/initial/inherit-wronghier.xml new file mode 100644 index 0000000..d1ce693 --- /dev/null +++ b/slicer/test/initial/inherit-wronghier.xml @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<D12 slicer-typeid="::TestModule::D3"> + <a>3</a> + <b>3</b> +</D12> diff --git a/slicer/test/serializers.cpp b/slicer/test/serializers.cpp index 57779be..ae9a30e 100644 --- a/slicer/test/serializers.cpp +++ b/slicer/test/serializers.cpp @@ -1,4 +1,5 @@ #define BOOST_TEST_MODULE execute_serializers +#include <boost/test/data/test_case.hpp> #include <boost/test/unit_test.hpp> #include "classes.h" @@ -36,7 +37,6 @@ #include <string> #include <types.h> #include <utility> -#include <variant> #include <vector> #include <xml.h> // IWYU pragma: no_forward_declare Slicer::InvalidEnumerationSymbol @@ -677,3 +677,35 @@ BOOST_AUTO_TEST_CASE(sequence_element_in_same_slice_link_bug) BOOST_CHECK_NO_THROW( Slicer::ModelPart::Make<Slicer::ModelPartForSequence<TestModule::Dates>>(nullptr, [](auto &&) {})); } + +BOOST_AUTO_TEST_CASE(typeid_specifies_same) +{ + std::ifstream in {rootDir / "initial/inherit-same.xml"}; + auto d1 = Slicer::DeserializeAny<Slicer::XmlStreamDeserializer, TestModule::D1Ptr>(in); + BOOST_REQUIRE(d1); + BOOST_CHECK_EQUAL(d1->a, 3); + BOOST_CHECK_EQUAL(typeid(*d1).name(), typeid(TestModule::D1).name()); +} + +BOOST_DATA_TEST_CASE(typeid_specifies_bad, + boost::unit_test::data::make<std::filesystem::path>({ + "inherit-base.xml", + "inherit-wronghier.xml", + }), + path) +{ + std::ifstream in {rootDir / "initial" / path}; + BOOST_CHECK_THROW(std::ignore = (Slicer::DeserializeAny<Slicer::XmlStreamDeserializer, TestModule::D12Ptr>(in)), + Slicer::IncorrectType); +} + +BOOST_DATA_TEST_CASE(typeid_specifies_no_such_type, + boost::unit_test::data::make<std::filesystem::path>({ + "inherit-nosuchtype.xml", + }), + path) +{ + std::ifstream in {rootDir / "initial" / path}; + BOOST_CHECK_THROW(std::ignore = (Slicer::DeserializeAny<Slicer::XmlStreamDeserializer, TestModule::D12Ptr>(in)), + Slicer::UnknownType); +} 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"); |