diff options
author | randomdan <randomdan@localhost> | 2014-09-11 18:45:02 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2014-09-11 18:45:02 +0000 |
commit | 3cdfcb6fcc749f460f1239039bb11b1de078f8d4 (patch) | |
tree | 4639077bd545a78ddf3191b468d0f19f2edf37b1 /slicer | |
parent | Slicer core to operate on any types passed to constructor, not just boost paths (diff) | |
download | slicer-3cdfcb6fcc749f460f1239039bb11b1de078f8d4.tar.bz2 slicer-3cdfcb6fcc749f460f1239039bb11b1de078f8d4.tar.xz slicer-3cdfcb6fcc749f460f1239039bb11b1de078f8d4.zip |
Allow serializers to use their own native types.
Allow .ice metadata to specify typesafe conversion functions between serializer types and model types
Diffstat (limited to 'slicer')
-rw-r--r-- | slicer/slicer/modelParts.h | 86 | ||||
-rw-r--r-- | slicer/slicer/parser.cpp | 124 | ||||
-rw-r--r-- | slicer/slicer/parser.h | 12 | ||||
-rw-r--r-- | slicer/test/Jamfile.jam | 1 | ||||
-rw-r--r-- | slicer/test/do-slicer.cpp | 28 | ||||
-rw-r--r-- | slicer/test/initial/conv-datetime.xml | 4 | ||||
-rw-r--r-- | slicer/test/run-slicer.cpp | 45 | ||||
-rw-r--r-- | slicer/test/types.ice | 15 |
8 files changed, 293 insertions, 22 deletions
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 <typename T> + 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<bool>, + public TValueTarget<Ice::Byte>, + public TValueTarget<Ice::Short>, + public TValueTarget<Ice::Int>, + public TValueTarget<Ice::Long>, + public TValueTarget<Ice::Float>, + public TValueTarget<Ice::Double>, + public TValueTarget<std::string> { + public: + using TValueTarget<bool>::get; + using TValueTarget<Ice::Byte>::get; + using TValueTarget<Ice::Short>::get; + using TValueTarget<Ice::Int>::get; + using TValueTarget<Ice::Long>::get; + using TValueTarget<Ice::Float>::get; + using TValueTarget<Ice::Double>::get; + using TValueTarget<std::string>::get; }; typedef IceUtil::Handle<ValueTarget> ValueTargetPtr; - class ValueSource : public IceUtil::Shared { + template <typename T> + class TValueSource { + public: + virtual void set(T &) const = 0; + }; + class ValueSource : public IceUtil::Shared, + public TValueSource<bool>, + public TValueSource<Ice::Byte>, + public TValueSource<Ice::Short>, + public TValueSource<Ice::Int>, + public TValueSource<Ice::Long>, + public TValueSource<Ice::Float>, + public TValueSource<Ice::Double>, + public TValueSource<std::string> { 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<bool>::set; + using TValueSource<Ice::Byte>::set; + using TValueSource<Ice::Short>::set; + using TValueSource<Ice::Int>::set; + using TValueSource<Ice::Long>::set; + using TValueSource<Ice::Float>::set; + using TValueSource<Ice::Double>::set; + using TValueSource<std::string>::set; }; typedef IceUtil::Handle<ValueSource> ValueSourcePtr; @@ -108,6 +134,30 @@ namespace Slicer { T & Member; }; + template<typename T, typename M, T M::* MV> + 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<typename T> 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 <Slice/Preprocessor.h> #include <boost/foreach.hpp> #include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/replace.hpp> #include <Slice/CPlusPlusUtil.h> #include <boost/shared_ptr.hpp> #include <boost/filesystem/convenience.hpp> @@ -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<TValueSource< %s > *>(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<TValueTarget< %s > *>(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::string>(); } + std::list<std::string> + Slicer::metaDataValues(const std::string & prefix, const std::list<std::string> & metadata) + { + std::list<std::string> 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<std::string> + Slicer::metaDataSplit(const std::string & metadata) + { + std::vector<std::string> parts; + boost::algorithm::split(parts, metadata, boost::algorithm::is_any_of(":"), boost::algorithm::token_compress_off); + return parts; + } + + std::vector<Slicer::ConversionSpec> + Slicer::getConversions(Slice::DataMemberPtr dm) + { + std::vector<ConversionSpec> 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<std::string> metaDataValue(const std::string & prefix, const std::list<std::string> & metadata); + static std::list<std::string> metaDataValues(const std::string & prefix, const std::list<std::string> & metadata); + static std::vector<std::string> metaDataSplit(const std::string & metadata); + static std::vector<ConversionSpec> getConversions(Slice::DataMemberPtr); FILE * cpp; std::vector<Slice::ModulePtr> 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 : + <linkflags>-rdynamic <library>dl <include>.. <library>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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<DateTimeContainer> + <dt>2014-Dec-25 12:13:14</dt> +</DateTimeContainer> 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 <json/serializer.h> #include <boost/filesystem/convenience.hpp> #include <boost/filesystem/operations.hpp> +#include <boost/numeric/conversion/cast.hpp> #include <boost/format.hpp> #include <boost/function.hpp> #include <boost/assert.hpp> @@ -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<typename T, typename SerializerIn> void @@ -200,6 +244,7 @@ main(int, char ** argv) verifyByFile<TestModule::Optionals, Slicer::XmlFile>(root, tmpf, "optionals-areset.xml", checkOptionals_areset); verifyByFile<TestModule::InheritanceCont, Slicer::XmlFile>(root, tmpf, "inherit-a.xml"); verifyByFile<TestModule::InheritanceCont, Slicer::XmlFile>(root, tmpf, "inherit-b.xml"); + verifyByFile<TestModule::DateTimeContainer, Slicer::XmlFile>(root, tmpf, "conv-datetime.xml"); verifyByFile<TestModule::BuiltIns, Slicer::JsonFile>(root, tmpf, "builtins2.json", checkBuiltIns_valuesCorrect); verifyByFile<TestModule::Optionals, Slicer::JsonFile>(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; |