From 3cdfcb6fcc749f460f1239039bb11b1de078f8d4 Mon Sep 17 00:00:00 2001 From: randomdan Date: Thu, 11 Sep 2014 18:45:02 +0000 Subject: Allow serializers to use their own native types. Allow .ice metadata to specify typesafe conversion functions between serializer types and model types --- slicer/slicer/modelParts.h | 86 ++++++++++++++++++----- slicer/slicer/parser.cpp | 124 ++++++++++++++++++++++++++++++++-- slicer/slicer/parser.h | 12 ++++ slicer/test/Jamfile.jam | 1 + slicer/test/do-slicer.cpp | 28 ++++++++ slicer/test/initial/conv-datetime.xml | 4 ++ slicer/test/run-slicer.cpp | 45 ++++++++++++ slicer/test/types.ice | 15 ++++ 8 files changed, 293 insertions(+), 22 deletions(-) create mode 100644 slicer/test/initial/conv-datetime.xml diff --git a/slicer/slicer/modelParts.h b/slicer/slicer/modelParts.h index 6302b9f..41b0617 100644 --- a/slicer/slicer/modelParts.h +++ b/slicer/slicer/modelParts.h @@ -23,29 +23,55 @@ namespace Slicer { UnknownType(const std::string & n); }; - class ValueTarget : public IceUtil::Shared { + template + class TValueTarget { public: - virtual void get(const bool &) const = 0; - virtual void get(const Ice::Byte &) const = 0; - virtual void get(const Ice::Short &) const = 0; - virtual void get(const Ice::Int &) const = 0; - virtual void get(const Ice::Long &) const = 0; - virtual void get(const Ice::Float &) const = 0; - virtual void get(const Ice::Double &) const = 0; - virtual void get(const std::string &) const = 0; + virtual void get(const T &) const = 0; + }; + class ValueTarget : public IceUtil::Shared, + public TValueTarget, + public TValueTarget, + public TValueTarget, + public TValueTarget, + public TValueTarget, + public TValueTarget, + public TValueTarget, + public TValueTarget { + public: + using TValueTarget::get; + using TValueTarget::get; + using TValueTarget::get; + using TValueTarget::get; + using TValueTarget::get; + using TValueTarget::get; + using TValueTarget::get; + using TValueTarget::get; }; typedef IceUtil::Handle ValueTargetPtr; - class ValueSource : public IceUtil::Shared { + template + class TValueSource { + public: + virtual void set(T &) const = 0; + }; + class ValueSource : public IceUtil::Shared, + public TValueSource, + public TValueSource, + public TValueSource, + public TValueSource, + public TValueSource, + public TValueSource, + public TValueSource, + public TValueSource { public: - virtual void set(bool &) const = 0; - virtual void set(Ice::Byte &) const = 0; - virtual void set(Ice::Short &) const = 0; - virtual void set(Ice::Int &) const = 0; - virtual void set(Ice::Long &) const = 0; - virtual void set(Ice::Float &) const = 0; - virtual void set(Ice::Double &) const = 0; - virtual void set(std::string &) const = 0; + using TValueSource::set; + using TValueSource::set; + using TValueSource::set; + using TValueSource::set; + using TValueSource::set; + using TValueSource::set; + using TValueSource::set; + using TValueSource::set; }; typedef IceUtil::Handle ValueSourcePtr; @@ -108,6 +134,30 @@ namespace Slicer { T & Member; }; + template + class ModelPartForConverted : public ModelPart { + public: + typedef T element_type; + + ModelPartForConverted(T & h) : + Member(h) + { + } + ModelPartForConverted(T * h) : + Member(*h) + { + } + virtual void OnEachChild(const ChildHandler &) { } + virtual ModelPartPtr GetChild(const std::string &) override { return NULL; } + virtual void SetValue(ValueSourcePtr s) override; + virtual void GetValue(ValueTargetPtr s) override; + virtual bool HasValue() const override { return true; } + virtual ModelPartType GetType() const { return mpt_Simple; } + + private: + T & Member; + }; + template class ModelPartForOptional : public ModelPart { public: diff --git a/slicer/slicer/parser.cpp b/slicer/slicer/parser.cpp index c2f2df7..6c65c1f 100644 --- a/slicer/slicer/parser.cpp +++ b/slicer/slicer/parser.cpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -16,6 +19,62 @@ namespace Slicer { { } + void + Slicer::defineConversions(Slice::DataMemberPtr dm) const + { + auto type = dm->type(); + auto c = Slice::ContainedPtr::dynamicCast(dm->container()); + auto conversions = getConversions(dm); + BOOST_FOREACH(const auto & conversion, conversions) { + fprintf(cpp, "%s %s(const %s &);\n", + conversion.ExchangeType.c_str(), + conversion.ConvertToExchangeFunc.c_str(), + Slice::typeToString(type).c_str()); + fprintf(cpp, "%s %s(const %s &);\n\n", + Slice::typeToString(type).c_str(), + conversion.ConvertToModelFunc.c_str(), + conversion.ExchangeType.c_str()); + } + if (!conversions.empty()) { + fprintf(cpp, "template<>\nvoid\n"); + fprintf(cpp, "ModelPartForConverted< %s, %s::%s, &%s::%s::%s >::SetValue(ValueSourcePtr vsp)\n", + Slice::typeToString(type).c_str(), + modulePath().c_str(), c->name().c_str(), + modulePath().c_str(), c->name().c_str(), dm->name().c_str()); + fprintf(cpp, "{\n"); + + BOOST_FOREACH(const auto & conversion, conversions) { + fprintf(cpp, "\tif (auto vspt = dynamic_cast *>(vsp.get())) {\n", + conversion.ExchangeType.c_str()); + fprintf(cpp, "\t\t%s tmp;\n", + conversion.ExchangeType.c_str()); + fprintf(cpp, "\t\tvspt->set(tmp);\n"); + fprintf(cpp, "\t\tMember = %s(tmp);\n", + conversion.ConvertToModelFunc.c_str()); + fprintf(cpp, "\t\treturn;\n"); + fprintf(cpp, "\t}\n"); + } + fprintf(cpp, "}\n\n"); + + fprintf(cpp, "template<>\nvoid\n"); + fprintf(cpp, "ModelPartForConverted< %s, %s::%s, &%s::%s::%s >::GetValue(ValueTargetPtr vtp)\n", + Slice::typeToString(type).c_str(), + modulePath().c_str(), c->name().c_str(), + modulePath().c_str(), c->name().c_str(), dm->name().c_str()); + fprintf(cpp, "{\n"); + + BOOST_FOREACH(const auto & conversion, conversions) { + fprintf(cpp, "\tif (auto vtpt = dynamic_cast *>(vtp.get())) {\n", + conversion.ExchangeType.c_str()); + fprintf(cpp, "\t\tvtpt->get(%s(Member));\n", + conversion.ConvertToExchangeFunc.c_str()); + fprintf(cpp, "\t\treturn;\n"); + fprintf(cpp, "\t}\n"); + } + fprintf(cpp, "}\n\n"); + } + } + bool Slicer::visitUnitStart(const Slice::UnitPtr & u) { @@ -40,6 +99,16 @@ namespace Slicer { { fprintf(cpp, "// Begin module %s\n\n", m->name().c_str()); modules.push_back(m); + BOOST_FOREACH(const auto & c, m->structs()) { + BOOST_FOREACH(const auto & dm, c->dataMembers()) { + defineConversions(dm); + } + } + BOOST_FOREACH(const auto & c, m->classes()) { + BOOST_FOREACH(const auto & dm, c->dataMembers()) { + defineConversions(dm); + } + } return true; } @@ -116,6 +185,7 @@ namespace Slicer { t = Slice::ClassDefPtr::dynamicCast(dm->container())->declaration(); } auto name = metaDataValue("slicer:name:", dm->getMetaData()); + auto conversions = metaDataValues("slicer:conversion:", dm->getMetaData()); fprintf(cpp, "\t\tnew "); auto type = dm->type(); createNewModelPartPtrFor(t); @@ -128,13 +198,21 @@ namespace Slicer { if (dm->optional()) { fprintf(cpp, "ModelPartForOptional< "); } - createNewModelPartPtrFor(type); - fprintf(cpp, "< %s", - Slice::typeToString(type).c_str()); + if (!conversions.empty()) { + fprintf(cpp, "ModelPartForConverted< %s, %s::%s, &%s::%s::%s >", + Slice::typeToString(type).c_str(), + modulePath().c_str(), c->name().c_str(), + modulePath().c_str(), c->name().c_str(), dm->name().c_str()); + } + else { + createNewModelPartPtrFor(type); + fprintf(cpp, "< %s >", + Slice::typeToString(type).c_str()); + } if (dm->optional()) { fprintf(cpp, " > "); } - fprintf(cpp, " > >(\"%s\"),\n", + fprintf(cpp, " >(\"%s\"),\n", name ? name->c_str() : dm->name().c_str()); } } @@ -269,6 +347,44 @@ namespace Slicer { return boost::optional(); } + std::list + Slicer::metaDataValues(const std::string & prefix, const std::list & metadata) + { + std::list mds; + BOOST_FOREACH (const auto & md, metadata) { + if (boost::algorithm::starts_with(md, prefix)) { + mds.push_back(md.substr(prefix.length())); + } + } + return mds; + } + + std::vector + Slicer::metaDataSplit(const std::string & metadata) + { + std::vector parts; + boost::algorithm::split(parts, metadata, boost::algorithm::is_any_of(":"), boost::algorithm::token_compress_off); + return parts; + } + + std::vector + Slicer::getConversions(Slice::DataMemberPtr dm) + { + std::vector rtn; + auto conversions = metaDataValues("slicer:conversion:", dm->getMetaData()); + BOOST_FOREACH(const auto & conversion, conversions) { + auto split = metaDataSplit(conversion); + if (split.size() != 3) { + throw std::runtime_error("conversion needs 3 parts type:toModelFunc:toExchangeFunc"); + } + BOOST_FOREACH(auto & p, split) { + boost::algorithm::replace_all(p, ".", "::"); + } + rtn.push_back(ConversionSpec({split[0], split[1], split[2]})); + } + return rtn; + } + void Slicer::Apply(const boost::filesystem::path & ice, const boost::filesystem::path & cpp) { diff --git a/slicer/slicer/parser.h b/slicer/slicer/parser.h index 816738d..64d961f 100644 --- a/slicer/slicer/parser.h +++ b/slicer/slicer/parser.h @@ -10,6 +10,13 @@ namespace Slicer { class Slicer : public Slice::ParserVisitor { public: + class ConversionSpec { + public: + std::string ExchangeType; + std::string ConvertToModelFunc; + std::string ConvertToExchangeFunc; + }; + Slicer(FILE * c); static void Apply(const boost::filesystem::path & ice, const boost::filesystem::path & cpp); @@ -38,7 +45,12 @@ namespace Slicer { std::string modulePath() const; + void defineConversions(Slice::DataMemberPtr dm) const; + static boost::optional metaDataValue(const std::string & prefix, const std::list & metadata); + static std::list metaDataValues(const std::string & prefix, const std::list & metadata); + static std::vector metaDataSplit(const std::string & metadata); + static std::vector getConversions(Slice::DataMemberPtr); FILE * cpp; std::vector modules; diff --git a/slicer/test/Jamfile.jam b/slicer/test/Jamfile.jam index 1426704..4a149a8 100644 --- a/slicer/test/Jamfile.jam +++ b/slicer/test/Jamfile.jam @@ -34,6 +34,7 @@ unit-test do-slicer : common do-slicer.cpp : + -rdynamic dl .. pthread diff --git a/slicer/test/do-slicer.cpp b/slicer/test/do-slicer.cpp index 30c5cfb..7607ff2 100644 --- a/slicer/test/do-slicer.cpp +++ b/slicer/test/do-slicer.cpp @@ -9,6 +9,34 @@ namespace fs = boost::filesystem; +namespace Slicer { + // These are the conversion helpers defined used in types.ice, they're not called in this test, but + // need to exist for the dynamic load to complete + boost::posix_time::ptime + dateTimeToPTime(const ::TestModule::DateTime &) + { + throw std::runtime_error("Not implemented"); + } + + ::TestModule::DateTime + ptimeToDateTime(const boost::posix_time::ptime &) + { + throw std::runtime_error("Not implemented"); + } + + std::string + dateTimeToString(const ::TestModule::DateTime &) + { + throw std::runtime_error("Not implemented"); + } + + ::TestModule::DateTime + stringToDateTime(const std::string &) + { + throw std::runtime_error("Not implemented"); + } +} + int main(int, char ** argv) { diff --git a/slicer/test/initial/conv-datetime.xml b/slicer/test/initial/conv-datetime.xml new file mode 100644 index 0000000..b4678b4 --- /dev/null +++ b/slicer/test/initial/conv-datetime.xml @@ -0,0 +1,4 @@ + + +
2014-Dec-25 12:13:14
+
diff --git a/slicer/test/run-slicer.cpp b/slicer/test/run-slicer.cpp index 0044c64..73d5be8 100644 --- a/slicer/test/run-slicer.cpp +++ b/slicer/test/run-slicer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,49 @@ #include "helpers.h" namespace fs = boost::filesystem; +#define SHORT(x) boost::numeric_cast< ::Ice::Short >(x) + +namespace Slicer { + boost::posix_time::ptime + dateTimeToPTime(const ::TestModule::DateTime &) + { + throw std::runtime_error("Not implemented"); + } + + ::TestModule::DateTime + ptimeToDateTime(const boost::posix_time::ptime &) + { + throw std::runtime_error("Not implemented"); + } + + std::string + dateTimeToString(const ::TestModule::DateTime & in) + { + char buf[BUFSIZ]; + struct tm tm({ in.second, in.minute, in.hour, in.day, in.month, in.year, 0, 0, 0 +#ifdef _BSD_SOURCE + , 0, 0 +#endif + }); + mktime(&tm); + auto len = strftime(buf, BUFSIZ, "%Y-%b-%d %H:%M:%S", &tm); + return std::string(buf, len); + } + + ::TestModule::DateTime + stringToDateTime(const std::string & in) + { + struct tm tm; + memset(&tm, 0, sizeof(struct tm)); + auto end = strptime(in.c_str(), "%Y-%b-%d %H:%M:%S", &tm); + if (!end || *end) { + throw std::runtime_error("Invalid date string: " + in); + } + return ::TestModule::DateTime({ + SHORT(tm.tm_year), SHORT(tm.tm_mon), SHORT(tm.tm_mday), + SHORT(tm.tm_hour), SHORT(tm.tm_min), SHORT(tm.tm_sec)}); + } +} template void @@ -200,6 +244,7 @@ main(int, char ** argv) verifyByFile(root, tmpf, "optionals-areset.xml", checkOptionals_areset); verifyByFile(root, tmpf, "inherit-a.xml"); verifyByFile(root, tmpf, "inherit-b.xml"); + verifyByFile(root, tmpf, "conv-datetime.xml"); verifyByFile(root, tmpf, "builtins2.json", checkBuiltIns_valuesCorrect); verifyByFile(root, tmpf, "optionals-areset2.json", checkOptionals_areset); diff --git a/slicer/test/types.ice b/slicer/test/types.ice index d2fe1ab..1f484d3 100644 --- a/slicer/test/types.ice +++ b/slicer/test/types.ice @@ -1,4 +1,19 @@ +[["cpp:include:boost/date_time/posix_time/posix_time_types.hpp"]] + module TestModule { + struct DateTime { + short year; + short month; + short day; + short hour; + short minute; + short second; + }; + class DateTimeContainer { + [ "slicer:conversion:boost.posix_time.ptime:ptimeToDateTime:dateTimeToPTime", + "slicer:conversion:std.string:stringToDateTime:dateTimeToString" ] + DateTime dt; + }; class BuiltIns { bool mbool; byte mbyte; -- cgit v1.2.3