From 908748b28d2ee12eca011ec595fecbc3ea7db2b2 Mon Sep 17 00:00:00 2001 From: randomdan Date: Thu, 26 Jun 2014 13:20:58 +0000 Subject: Adds support for classes with inheritance Adds more unit tests and verifies written XML is the same as the initial --- slicer/slicer/modelParts.cpp | 31 +++++++ slicer/slicer/modelParts.h | 34 ++++++-- slicer/slicer/parser.cpp | 137 +++++++++++++++++-------------- slicer/slicer/parser.h | 5 +- slicer/test/Jamfile.jam | 1 + slicer/test/initial/builtins.xml | 8 +- slicer/test/initial/optionals-areset.xml | 42 +++++----- slicer/test/initial/optionals-notset.xml | 5 +- slicer/test/run-slicer.cpp | 6 ++ slicer/test/types.ice | 19 +++++ slicer/xml/serializer.cpp | 9 ++ 11 files changed, 202 insertions(+), 95 deletions(-) diff --git a/slicer/slicer/modelParts.cpp b/slicer/slicer/modelParts.cpp index 9d33d51..4c05513 100644 --- a/slicer/slicer/modelParts.cpp +++ b/slicer/slicer/modelParts.cpp @@ -1,14 +1,45 @@ #include "modelParts.h" namespace Slicer { + IncorrectElementName::IncorrectElementName(const std::string & n) : + std::invalid_argument(n) + { + } + + UnknownType::UnknownType(const std::string & n) : + std::invalid_argument(n) + { + } + + ClassRefMap * & + classRefMap() + { + static ClassRefMap * refs = new ClassRefMap(); + return refs; + } + void ModelPart::Create() { } + void ModelPart::Complete() { } + + ModelPartPtr + ModelPart::GetSubclassModelPart(const std::string &) + { + return this; + } + + TypeId + ModelPart::GetTypeId() const + { + return TypeId(); + } + void ModelPart::SetValue(ValueSourcePtr) { diff --git a/slicer/slicer/modelParts.h b/slicer/slicer/modelParts.h index 9c2bf45..28ab86d 100644 --- a/slicer/slicer/modelParts.h +++ b/slicer/slicer/modelParts.h @@ -15,8 +15,12 @@ namespace Slicer { class IncorrectElementName : public std::invalid_argument { public: - IncorrectElementName() : - std::invalid_argument("") { } + IncorrectElementName(const std::string & n); + }; + + class UnknownType : public std::invalid_argument { + public: + UnknownType(const std::string & n); }; class ValueTarget : public IceUtil::Shared { @@ -49,15 +53,22 @@ namespace Slicer { typedef IceUtil::Handle ModelPartPtr; typedef std::map ModelParts; + typedef IceUtil::Optional TypeId; typedef boost::function ChildHandler; + typedef boost::function ClassRef; + typedef std::map ClassRefMap; + ClassRefMap * & classRefMap(); + class ModelPart : public IceUtil::Shared { public: virtual ~ModelPart() = default; virtual void OnEachChild(const ChildHandler &) = 0; - virtual ModelPartPtr GetChild(const std::string &) = 0; + virtual ModelPartPtr GetChild(const std::string & memberName) = 0; + virtual ModelPartPtr GetSubclassModelPart(const std::string &); + virtual TypeId GetTypeId() const; virtual void Create(); virtual void Complete(); virtual void SetValue(ValueSourcePtr); @@ -165,7 +176,7 @@ namespace Slicer { }; typedef IceUtil::Handle HookPtr; - template + template class Hook : public HookBase { public: Hook(const std::string & n) : @@ -239,8 +250,19 @@ namespace Slicer { return ModelObject.get(); } + virtual ModelPartPtr GetSubclassModelPart(const std::string & name) override + { + auto ref = classRefMap()->find(name); + if (ref == classRefMap()->end()) { + throw UnknownType(name); + } + return ref->second(&this->ModelObject); + } + virtual bool HasValue() const override { return ModelObject; } + virtual TypeId GetTypeId() const override; + private: T & ModelObject; }; @@ -288,7 +310,7 @@ namespace Slicer { virtual ModelPartPtr GetChild(const std::string & name) override { if (!name.empty() && name != rootName) { - throw IncorrectElementName(); + throw IncorrectElementName(rootName); } ModelPartForClass::Create(); return new ModelPartForClass(ModelObject); @@ -406,7 +428,7 @@ namespace Slicer { ModelPartPtr GetChild(const std::string & name) override { if (!name.empty() && name != pairName) { - throw IncorrectElementName(); + throw IncorrectElementName(pairName); } return new ModelPartForDictionaryElementInserter(dictionary); } diff --git a/slicer/slicer/parser.cpp b/slicer/slicer/parser.cpp index c874f57..8e2bfa9 100644 --- a/slicer/slicer/parser.cpp +++ b/slicer/slicer/parser.cpp @@ -11,7 +11,8 @@ namespace fs = boost::filesystem; namespace Slicer { Slicer::Slicer(FILE * c) : - cpp(c) + cpp(c), + classNo(0) { } @@ -47,47 +48,43 @@ namespace Slicer { { if (c->hasMetaData("slicer:ignore")) { return false; } + auto decl = c->declaration(); fprintf(cpp, "// Class %s\n", c->name().c_str()); fprintf(cpp, "template<>\n"); fprintf(cpp, "ModelPartForComplex< %s::%s >::Hooks ", modulePath().c_str(), c->name().c_str()); fprintf(cpp, "ModelPartForComplex< %s::%s >::hooks {\n", modulePath().c_str(), c->name().c_str()); - BOOST_FOREACH (const auto & dm, c->allDataMembers()) { - auto name = metaDataValue("slicer:name:", dm->getMetaData()); - fprintf(cpp, "\t\t{ "); - auto type = dm->type(); - fprintf(cpp, "new ModelPartForClass< %s::%sPtr >::Hook< ", - modulePath().c_str(), c->name().c_str()); - if (dm->optional()) { - fprintf(cpp, "::IceUtil::Optional< %s >", - Slice::typeToString(type).c_str()); - } - else { - fprintf(cpp, "%s", - Slice::typeToString(type).c_str()); - } - fprintf(cpp, ", &%s::%s::%s, ", - modulePath().c_str(), c->name().c_str(), dm->name().c_str()); - if (dm->optional()) { - fprintf(cpp, "ModelPartForOptional< "); - } - createNewModelPartPtrFor(type); - fprintf(cpp, "< %s", - Slice::typeToString(type).c_str()); - if (dm->optional()) { - fprintf(cpp, " > "); - } - fprintf(cpp, " > >(\"%s\") },\n", - name ? name->c_str() : dm->name().c_str()); - } - fprintf(cpp, "\t};\n"); + visitComplexDataMembers(decl, c->allDataMembers()); + fprintf(cpp, "\t};\n\n"); fprintf(cpp, "template<>\n"); auto name = metaDataValue("slicer:root:", c->getMetaData()); - fprintf(cpp, "std::string ModelPartForClassRoot< %s::%sPtr >::rootName(\"%s\");\n\n", - modulePath().c_str(), c->name().c_str(), + fprintf(cpp, "std::string ModelPartForClassRoot< %s >::rootName(\"%s\");\n\n", + typeToString(decl).c_str(), name ? name->c_str() : c->name().c_str()); + + fprintf(cpp, "static void registerClass_%u() __attribute__ ((constructor(210)));\n", classNo); + fprintf(cpp, "static void registerClass_%u()\n{\n", classNo); + fprintf(cpp, "\tSlicer::classRefMap()->insert({ \"%s::%s\", [](void * p){ return new ModelPartForClass< %s >(*static_cast< %s *>(p)); } });\n", + modulePath().c_str(), c->name().c_str(), + typeToString(decl).c_str(), + typeToString(decl).c_str()); + fprintf(cpp, "}\n\n"); + fprintf(cpp, "static void unregisterClass_%u() __attribute__ ((destructor(210)));\n", classNo); + fprintf(cpp, "static void unregisterClass_%u()\n{\n", classNo); + fprintf(cpp, "\tSlicer::classRefMap()->erase(\"%s::%s\");\n", + modulePath().c_str(), c->name().c_str()); + fprintf(cpp, "}\n\n"); + + fprintf(cpp, "template<>\nTypeId\nModelPartForClass< %s >::GetTypeId() const\n{\n", + typeToString(decl).c_str()); + fprintf(cpp, "\tauto id = ModelObject->ice_id();\n"); + fprintf(cpp, "\treturn (id == \"%s::%s\") ? TypeId() : id;\n}\n\n", + modulePath().c_str(), c->name().c_str()); + + classNo += 1; + return true; } @@ -102,22 +99,43 @@ namespace Slicer { modulePath().c_str(), c->name().c_str()); fprintf(cpp, "ModelPartForComplex< %s::%s >::hooks {\n", modulePath().c_str(), c->name().c_str()); - BOOST_FOREACH (const auto & dm, c->dataMembers()) { + visitComplexDataMembers(c, c->dataMembers()); + fprintf(cpp, "\t};\n\n"); + + return true; + } + + void + Slicer::visitComplexDataMembers(Slice::TypePtr it, const Slice::DataMemberList & dataMembers) const + { + BOOST_FOREACH (const auto & dm, dataMembers) { + auto c = Slice::ContainedPtr::dynamicCast(dm->container()); + auto t = Slice::TypePtr::dynamicCast(dm->container()); + if (!t) { + t = Slice::ClassDefPtr::dynamicCast(dm->container())->declaration(); + } auto name = metaDataValue("slicer:name:", dm->getMetaData()); - fprintf(cpp, "\t\t{ "); + fprintf(cpp, "\t\tnew "); auto type = dm->type(); - fprintf(cpp, "new ModelPartForStruct< %s::%s >::Hook< %s, &%s::%s::%s, ", - modulePath().c_str(), c->name().c_str(), - Slice::typeToString(type).c_str(), - modulePath().c_str(), c->name().c_str(), dm->name().c_str()); + createNewModelPartPtrFor(t); + fprintf(cpp, "< %s >::Hook< %s", + typeToString(it).c_str(), + Slice::typeToString(type, dm->optional()).c_str()); + fprintf(cpp, ", %s::%s, &%s::%s::%s, ", + modulePath().c_str(), c->name().c_str(), + modulePath().c_str(), c->name().c_str(), dm->name().c_str()); + if (dm->optional()) { + fprintf(cpp, "ModelPartForOptional< "); + } createNewModelPartPtrFor(type); - fprintf(cpp, "< %s > >(\"%s\") },\n", - Slice::typeToString(type).c_str(), + fprintf(cpp, "< %s", + Slice::typeToString(type).c_str()); + if (dm->optional()) { + fprintf(cpp, " > "); + } + fprintf(cpp, " > >(\"%s\"),\n", name ? name->c_str() : dm->name().c_str()); } - fprintf(cpp, "\t};\n\n"); - - return true; } void @@ -137,13 +155,11 @@ namespace Slicer { else { fprintf(cpp, "\t(void)name;\n"); } - fprintf(cpp, "\tsequence.push_back(%s());\n", - Slice::typeToString(s->type()).c_str()); + fprintf(cpp, "\tsequence.push_back(typename element_type::value_type());\n"); fprintf(cpp, "\treturn new "); auto etype = s->type(); createNewModelPartPtrFor(etype); - fprintf(cpp, "< %s >(sequence.back());\n}\n\n", - Slice::typeToString(etype).c_str()); + fprintf(cpp, "(sequence.back());\n}\n\n"); fprintf(cpp, "template<>\n"); fprintf(cpp, "ModelPartPtr\n"); fprintf(cpp, "ModelPartForSequence< %s::%s >::elementModelPart(typename %s::%s::value_type & e) const {\n", @@ -151,8 +167,7 @@ namespace Slicer { modulePath().c_str(), s->name().c_str()); fprintf(cpp, "\treturn new "); createNewModelPartPtrFor(etype); - fprintf(cpp, "< %s >(e);\n}\n\n", - Slice::typeToString(etype).c_str()); + fprintf(cpp, "(e);\n}\n\n"); fprintf(cpp, "template<>\n"); auto ename = metaDataValue("slicer:element:", s->getMetaData()); fprintf(cpp, "std::string ModelPartForSequence< %s::%s >::elementName(\"%s\");\n\n", @@ -179,23 +194,25 @@ namespace Slicer { modulePath().c_str(), d->name().c_str()); auto kname = metaDataValue("slicer:key:", d->getMetaData()); auto vname = metaDataValue("slicer:value:", d->getMetaData()); - fprintf(cpp, "\t\t{ "); + fprintf(cpp, "\t\t"); auto ktype = d->keyType(); - fprintf(cpp, "new ModelPartForDictionaryElement< %s::%s >::Hook< %s*, &ModelPartForDictionaryElement< %s::%s >::key, ", + fprintf(cpp, "new ModelPartForDictionaryElement< %s::%s >::Hook< %s*, ModelPartForDictionaryElement< %s::%s >, &ModelPartForDictionaryElement< %s::%s >::key, ", modulePath().c_str(), d->name().c_str(), Slice::typeToString(ktype).c_str(), + modulePath().c_str(), d->name().c_str(), modulePath().c_str(), d->name().c_str()); createNewModelPartPtrFor(ktype); - fprintf(cpp, "< %s > >(\"%s\") },\n\t\t{ ", + fprintf(cpp, "< %s > >(\"%s\"),\n\t\t", Slice::typeToString(ktype).c_str(), kname ? kname->c_str() : "key"); auto vtype = d->valueType(); - fprintf(cpp, "new ModelPartForDictionaryElement< %s::%s >::Hook< %s*, &ModelPartForDictionaryElement< %s::%s >::value, ", + fprintf(cpp, "new ModelPartForDictionaryElement< %s::%s >::Hook< %s*, ModelPartForDictionaryElement< %s::%s >, &ModelPartForDictionaryElement< %s::%s >::value, ", modulePath().c_str(), d->name().c_str(), Slice::typeToString(vtype).c_str(), + modulePath().c_str(), d->name().c_str(), modulePath().c_str(), d->name().c_str()); createNewModelPartPtrFor(vtype); - fprintf(cpp, "< %s > >(\"%s\") },\n", + fprintf(cpp, "< %s > >(\"%s\"),\n", Slice::typeToString(vtype).c_str(), vname ? vname->c_str() : "value"); fprintf(cpp, "\t};\n"); @@ -213,24 +230,24 @@ namespace Slicer { Slicer::createNewModelPartPtrFor(const Slice::TypePtr & type) const { if (auto builtin = Slice::BuiltinPtr::dynamicCast(type)) { - fprintf(cpp, "ModelPartForSimple "); + fprintf(cpp, "ModelPartForSimple"); } else if (auto complexClass = Slice::ClassDeclPtr::dynamicCast(type)) { - fprintf(cpp, "ModelPartForClass "); + fprintf(cpp, "ModelPartForClass"); } else if (auto complexStruct = Slice::StructPtr::dynamicCast(type)) { - fprintf(cpp, "ModelPartForStruct "); + fprintf(cpp, "ModelPartForStruct"); } else if (auto sequence = Slice::SequencePtr::dynamicCast(type)) { - fprintf(cpp, "ModelPartForSequence "); + fprintf(cpp, "ModelPartForSequence"); } else if (auto dictionary = Slice::DictionaryPtr::dynamicCast(type)) { - fprintf(cpp, "ModelPartForDictionary "); + fprintf(cpp, "ModelPartForDictionary"); } } std::string - Slicer::modulePath() + Slicer::modulePath() const { std::string path; BOOST_FOREACH (const auto & m, modules) { diff --git a/slicer/slicer/parser.h b/slicer/slicer/parser.h index 90aa05a..816738d 100644 --- a/slicer/slicer/parser.h +++ b/slicer/slicer/parser.h @@ -34,12 +34,15 @@ namespace Slicer { private: void createNewModelPartPtrFor(const Slice::TypePtr & type) const; - std::string modulePath(); + void visitComplexDataMembers(Slice::TypePtr t, const Slice::DataMemberList &) const; + + std::string modulePath() const; static boost::optional metaDataValue(const std::string & prefix, const std::list & metadata); FILE * cpp; std::vector modules; + unsigned int classNo; }; } diff --git a/slicer/test/Jamfile.jam b/slicer/test/Jamfile.jam index 88c981f..ebc6c25 100644 --- a/slicer/test/Jamfile.jam +++ b/slicer/test/Jamfile.jam @@ -26,6 +26,7 @@ lib common : : : boost_filesystem boost_system + ../../libmisc ; unit-test do-slicer : diff --git a/slicer/test/initial/builtins.xml b/slicer/test/initial/builtins.xml index 650d187..d77126e 100644 --- a/slicer/test/initial/builtins.xml +++ b/slicer/test/initial/builtins.xml @@ -1,11 +1,11 @@ - + true 4 - 3.14159265359 - 3.14 + 40 80 800 - 40 + 3.125 + 3.0625 Sample text diff --git a/slicer/test/initial/optionals-areset.xml b/slicer/test/initial/optionals-areset.xml index e4cdf42..aa2a97a 100644 --- a/slicer/test/initial/optionals-areset.xml +++ b/slicer/test/initial/optionals-areset.xml @@ -1,14 +1,14 @@ - + 4 - - 1 - 2 - 1 2 + + 1 + 2 + 3 @@ -19,20 +19,20 @@ 6 - - - 10 - - 11 - 12 - - - - 13 - - 14 - 15 - - - + + + 10 + + 11 + 12 + + + + 13 + + 14 + 15 + + + diff --git a/slicer/test/initial/optionals-notset.xml b/slicer/test/initial/optionals-notset.xml index 8fbb701..8c679d1 100644 --- a/slicer/test/initial/optionals-notset.xml +++ b/slicer/test/initial/optionals-notset.xml @@ -1,3 +1,2 @@ - - - + + diff --git a/slicer/test/run-slicer.cpp b/slicer/test/run-slicer.cpp index 897d9ee..35db875 100644 --- a/slicer/test/run-slicer.cpp +++ b/slicer/test/run-slicer.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include "helpers.h" namespace fs = boost::filesystem; @@ -26,6 +28,8 @@ verify(const fs::path & root, const fs::path & tmp, const fs::path & infile, con fprintf(stderr, "%s : Check2\n", input.string().c_str()); if (check) check(*p); fprintf(stderr, "%s : OK\n", input.string().c_str()); + + system(stringbf("diff -w %s %s", input, output)); } void @@ -85,6 +89,8 @@ main(int, char ** argv) verify(root, tmp, "builtins.xml"); verify(root, tmp, "optionals-notset.xml", checkOptionals_notset); verify(root, tmp, "optionals-areset.xml", checkOptionals_areset); + verify(root, tmp, "inherit-a.xml"); + verify(root, tmp, "inherit-b.xml"); return 0; } diff --git a/slicer/test/types.ice b/slicer/test/types.ice index 46f834f..bdcd4be 100644 --- a/slicer/test/types.ice +++ b/slicer/test/types.ice @@ -38,4 +38,23 @@ module TestModule { StructType str; int simp; }; + class Base { + int a; + }; + class D1 extends Base { + int b; + }; + class D2 extends Base { + int c; + }; + class D3 extends D2 { + int d; + }; + sequence BaseSeq; + dictionary BaseMap; + class InheritanceCont { + Base b; + BaseSeq bs; + BaseMap bm; + }; }; diff --git a/slicer/xml/serializer.cpp b/slicer/xml/serializer.cpp index 1ff6058..b08e274 100644 --- a/slicer/xml/serializer.cpp +++ b/slicer/xml/serializer.cpp @@ -167,6 +167,10 @@ namespace Slicer { if (auto element = dynamic_cast(node)) { auto smp = mp->GetChild(element->get_name()); if (smp) { + auto typeAttr = element->get_attribute("slicer-typeid"); + if (typeAttr) { + smp = smp->GetSubclassModelPart(typeAttr->get_value()); + } smp->Create(); auto attrs(element->get_attributes()); if (!attrs.empty()) { @@ -208,6 +212,11 @@ namespace Slicer { } else if (mp) { auto element = n->add_child(name); + auto typeId = mp->GetTypeId(); + if (typeId) { + element->set_attribute("slicer-typeid", *typeId); + mp = mp->GetSubclassModelPart(*typeId); + } mp->GetValue(new XmlContentValueTarget(element)); mp->OnEachChild(boost::bind(&Xml::ModelTreeIterate, element, _1, _2)); } -- cgit v1.2.3