From fe47783b5f492d1c5a8c112bd51590ff2abe7bd9 Mon Sep 17 00:00:00 2001 From: jbeder Date: Sun, 6 Sep 2009 20:52:45 +0000 Subject: [PATCH] Refactored the operator >> and Node::Read default functions, as well as the conversion functions, to more easily read new types as keys (this uncovered an error, in example 2.11 of the spec) --- include/conversion.h | 50 +++++++++++++++--------------- include/node.h | 1 + include/nodeimpl.h | 11 +------ include/nodereadimpl.h | 65 +++++++++++++++++++++++++++++++++++++++ src/conversion.cpp | 6 ++-- yaml-reader/spectests.cpp | 45 ++++++++++++++++++++++++++- 6 files changed, 137 insertions(+), 41 deletions(-) create mode 100644 include/nodereadimpl.h diff --git a/include/conversion.h b/include/conversion.h index f449816..dfaf9f2 100644 --- a/include/conversion.h +++ b/include/conversion.h @@ -10,36 +10,34 @@ namespace YAML { - template - struct Converter { - static bool Convert(const std::string& input, T& output); - }; - - template - bool Convert(const std::string& input, T& output) { - return Converter::Convert(input, output); - } - - // this is the one to specialize - template - inline bool Converter::Convert(const std::string& input, T& output) { - std::stringstream stream(input); - stream >> output; - return !stream.fail(); - } - - // specializations - template <> - inline bool Converter::Convert(const std::string& input, std::string& output) { + inline bool Convert(const std::string& input, std::string& output) { output = input; return true; } - - template <> - bool Converter::Convert(const std::string& input, bool& output); - template <> - bool Converter<_Null>::Convert(const std::string& input, _Null& output); + bool Convert(const std::string& input, bool& output); + bool Convert(const std::string& input, _Null& output); + +#define YAML_MAKE_STREAM_CONVERT(type) \ + inline bool Convert(const std::string& input, type& output) { \ + std::stringstream stream(input); \ + stream >> output; \ + return !stream.fail(); \ + } + + YAML_MAKE_STREAM_CONVERT(char) + YAML_MAKE_STREAM_CONVERT(unsigned char) + YAML_MAKE_STREAM_CONVERT(int) + YAML_MAKE_STREAM_CONVERT(unsigned int) + YAML_MAKE_STREAM_CONVERT(short) + YAML_MAKE_STREAM_CONVERT(unsigned short) + YAML_MAKE_STREAM_CONVERT(long) + YAML_MAKE_STREAM_CONVERT(unsigned long) + YAML_MAKE_STREAM_CONVERT(float) + YAML_MAKE_STREAM_CONVERT(double) + YAML_MAKE_STREAM_CONVERT(long double) + +#undef YAML_MAKE_STREAM_CONVERT } #endif // CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/node.h b/include/node.h index 808d67f..1503e3b 100644 --- a/include/node.h +++ b/include/node.h @@ -133,5 +133,6 @@ namespace YAML } #include "nodeimpl.h" +#include "nodereadimpl.h" #endif // NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/nodeimpl.h b/include/nodeimpl.h index b5a656e..3133977 100644 --- a/include/nodeimpl.h +++ b/include/nodeimpl.h @@ -9,15 +9,6 @@ namespace YAML { // implementation of templated things - template - inline bool Node::Read(T& value) const { - std::string scalar; - if(!GetScalar(scalar)) - return false; - - return Convert(scalar, value); - } - template inline const T Node::Read() const { T value; @@ -32,7 +23,7 @@ namespace YAML template inline void operator >> (const Node& node, T& value) { - if(!node.Read(value)) + if(!ConvertScalar(node, value)) throw InvalidScalar(node.m_mark); } diff --git a/include/nodereadimpl.h b/include/nodereadimpl.h new file mode 100644 index 0000000..5beaa09 --- /dev/null +++ b/include/nodereadimpl.h @@ -0,0 +1,65 @@ +#pragma once + +namespace YAML +{ + // implementation for Node::Read + // (the goal is to call ConvertScalar if we can, and fall back to operator >> if not) + // thanks to litb from stackoverflow.com + // http://stackoverflow.com/questions/1386183/how-to-call-a-templated-function-if-it-exists-and-something-else-otherwise/1386390#1386390 + + template + struct read_impl; + + // ConvertScalar available + template<> + struct read_impl { + template + static bool read(const Node& node, T& value) { + return ConvertScalar(node, value); + } + }; + + // ConvertScalar not available + template<> + struct read_impl { + template + static bool read(const Node& node, T& value) { + try { + node >> value; + } catch(const Exception&) { + return false; + } + return true; + } + }; + + namespace fallback { + // sizeof > 1 + struct flag { char c[2]; }; + flag Convert(...); + + char (& operator,(flag, flag) )[1]; + + template + void operator,(flag, T const&); + + char (& operator,(char(&)[1], flag) )[1]; + } + + template + inline bool Node::Read(T& value) const { + using namespace fallback; + + return read_impl::read(*this, value); + } + + // the main conversion function + template + inline bool ConvertScalar(const Node& node, T& value) { + std::string scalar; + if(!node.GetScalar(scalar)) + return false; + + return Convert(scalar, value); + } +} diff --git a/src/conversion.cpp b/src/conversion.cpp index 1240e27..3b76ef2 100644 --- a/src/conversion.cpp +++ b/src/conversion.cpp @@ -49,8 +49,7 @@ namespace namespace YAML { - template <> - bool Converter::Convert(const std::string& input, bool& b) + bool Convert(const std::string& input, bool& b) { // we can't use iostream bool extraction operators as they don't // recognize all possible values in the table below (taken from @@ -82,8 +81,7 @@ namespace YAML return false; } - template <> - bool Converter<_Null>::Convert(const std::string& input, _Null& /*output*/) + bool Convert(const std::string& input, _Null& /*output*/) { return input.empty() || input == "~" || input == "null" || input == "Null" || input == "NULL"; } diff --git a/yaml-reader/spectests.cpp b/yaml-reader/spectests.cpp index df71e6c..2e0b3fd 100644 --- a/yaml-reader/spectests.cpp +++ b/yaml-reader/spectests.cpp @@ -290,7 +290,49 @@ namespace Test { YAML_ASSERT(doc["rbi"][0] == "Sammy Sosa"); YAML_ASSERT(doc["rbi"][1] == "Ken Griffey"); return true; - } + } + + struct Pair { + Pair() {} + Pair(const std::string& f, const std::string& s): first(f), second(s) {} + std::string first, second; + }; + + bool operator == (const Pair& p, const Pair& q) { + return p.first == q.first && p.second == q.second; + } + + void operator >> (const YAML::Node& node, Pair& p) { + node[0] >> p.first; + node[1] >> p.second; + } + + TEST MappingBetweenSequences() + { + std::string input = + "? - Detroit Tigers\n" + " - Chicago cubs\n" + ":\n" + " - 2001-07-23\n" + "\n" + "? [ New York Yankees,\n" + " Atlanta Braves ]\n" + ": [ 2001-07-02, 2001-08-12,\n" + " 2001-08-14 ]"; + std::stringstream stream(input); + YAML::Parser parser(stream); + YAML::Node doc; + parser.GetNextDocument(doc); + + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[Pair("Detroit Tigers", "Chicago Cubs")].size() == 1); + YAML_ASSERT(doc[Pair("Detroit Tigers", "Chicago Cubs")][0] == "2001-07-23"); + YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")].size() == 3); + YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")][0] == "2001-07-02"); + YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")][1] == "2001-08-12"); + YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")][2] == "2001-08-14"); + return true; + } } bool RunSpecTests() @@ -306,6 +348,7 @@ namespace Test { RunSpecTest(&Spec::PlayByPlayFeed, "2.8", "Play by Play Feed from a Game", passed); RunSpecTest(&Spec::SingleDocumentWithTwoComments, "2.9", "Single Document with Two Comments", passed); RunSpecTest(&Spec::SimpleAnchor, "2.10", "Node for \"Sammy Sosa\" appears twice in this document", passed); + RunSpecTest(&Spec::MappingBetweenSequences, "2.11", "Mapping between Sequences", passed); return passed; }