summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2014-09-11 18:45:02 +0000
committerrandomdan <randomdan@localhost>2014-09-11 18:45:02 +0000
commite1bb743c5ea232e6d248ebad5289cf7d40bca88e (patch)
tree4639077bd545a78ddf3191b468d0f19f2edf37b1
parentSlicer core to operate on any types passed to constructor, not just boost paths (diff)
downloadslicer-e1bb743c5ea232e6d248ebad5289cf7d40bca88e.tar.bz2
slicer-e1bb743c5ea232e6d248ebad5289cf7d40bca88e.tar.xz
slicer-e1bb743c5ea232e6d248ebad5289cf7d40bca88e.zip
Allow serializers to use their own native types.
Allow .ice metadata to specify typesafe conversion functions between serializer types and model types
-rw-r--r--slicer/slicer/modelParts.h86
-rw-r--r--slicer/slicer/parser.cpp124
-rw-r--r--slicer/slicer/parser.h12
-rw-r--r--slicer/test/Jamfile.jam1
-rw-r--r--slicer/test/do-slicer.cpp28
-rw-r--r--slicer/test/initial/conv-datetime.xml4
-rw-r--r--slicer/test/run-slicer.cpp45
-rw-r--r--slicer/test/types.ice15
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;