diff options
Diffstat (limited to 'llvm')
| -rw-r--r-- | llvm/include/llvm/Support/YAMLTraits.h | 96 | ||||
| -rw-r--r-- | llvm/lib/Support/YAMLTraits.cpp | 20 | ||||
| -rw-r--r-- | llvm/unittests/Support/YAMLIOTest.cpp | 62 |
3 files changed, 175 insertions, 3 deletions
diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h index 38acb36942b..6860eef9a22 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -209,6 +209,15 @@ struct DocumentListTraits { // static T::value_type& element(IO &io, T &seq, size_t index); }; +/// This class should be specialized by any type that needs to be converted +/// to/from a YAML mapping in the case where the names of the keys are not known +/// in advance, e.g. a string map. +template <typename T> +struct CustomMappingTraits { + // static void inputOne(IO &io, StringRef key, T &elem); + // static void output(IO &io, T &elem); +}; + // Only used for better diagnostics of missing traits template <typename T> struct MissingTrait; @@ -358,6 +367,23 @@ public: static bool const value = (sizeof(test<SequenceTraits<T>>(nullptr)) == 1); }; +// Test if CustomMappingTraits<T> is defined on type T. +template <class T> +struct has_CustomMappingTraits +{ + typedef void (*Signature_input)(IO &io, StringRef key, T &v); + + template <typename U> + static char test(SameType<Signature_input, &U::inputOne>*); + + template <typename U> + static double test(...); + +public: + static bool const value = + (sizeof(test<CustomMappingTraits<T>>(nullptr)) == 1); +}; + // has_FlowTraits<int> will cause an error with some compilers because // it subclasses int. Using this wrapper only instantiates the // real has_FlowTraits only if the template type is a class. @@ -493,6 +519,7 @@ struct missingTraits !has_BlockScalarTraits<T>::value && !has_MappingTraits<T, Context>::value && !has_SequenceTraits<T>::value && + !has_CustomMappingTraits<T>::value && !has_DocumentListTraits<T>::value> {}; template <typename T, typename Context> @@ -531,6 +558,7 @@ public: virtual void endMapping() = 0; virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0; virtual void postflightKey(void*) = 0; + virtual std::vector<StringRef> keys() = 0; virtual void beginFlowMapping() = 0; virtual void endFlowMapping() = 0; @@ -819,6 +847,21 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { } template <typename T> +typename std::enable_if<has_CustomMappingTraits<T>::value, void>::type +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + if ( io.outputting() ) { + io.beginMapping(); + CustomMappingTraits<T>::output(io, Val); + io.endMapping(); + } else { + io.beginMapping(); + for (StringRef key : io.keys()) + CustomMappingTraits<T>::inputOne(io, key, Val); + io.endMapping(); + } +} + +template <typename T> typename std::enable_if<missingTraits<T, EmptyContext>::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; @@ -1074,6 +1117,7 @@ private: void endMapping() override; bool preflightKey(const char *, bool, bool, bool &, void *&) override; void postflightKey(void *) override; + std::vector<StringRef> keys() override; void beginFlowMapping() override; void endFlowMapping() override; unsigned beginSequence() override; @@ -1157,7 +1201,7 @@ private: bool isValidKey(StringRef key); NameToNode Mapping; - llvm::SmallVector<const char*, 6> ValidKeys; + llvm::SmallVector<std::string, 6> ValidKeys; }; class SequenceHNode : public HNode { @@ -1215,6 +1259,7 @@ public: void endMapping() override; bool preflightKey(const char *key, bool, bool, bool &, void *&) override; void postflightKey(void *) override; + std::vector<StringRef> keys() override; void beginFlowMapping() override; void endFlowMapping() override; unsigned beginSequence() override; @@ -1384,6 +1429,17 @@ operator>>(Input &In, T &Val) { return In; } +// Define non-member operator>> so that Input can stream in a string map. +template <typename T> +inline +typename std::enable_if<has_CustomMappingTraits<T>::value, Input &>::type +operator>>(Input &In, T &Val) { + EmptyContext Ctx; + if (In.setCurrentDocument()) + yamlize(In, Val, true, Ctx); + return In; +} + // Provide better error message about types missing a trait specialization template <typename T> inline typename std::enable_if<missingTraits<T, EmptyContext>::value, @@ -1457,6 +1513,21 @@ operator<<(Output &Out, T &Val) { return Out; } +// Define non-member operator<< so that Output can stream out a string map. +template <typename T> +inline +typename std::enable_if<has_CustomMappingTraits<T>::value, Output &>::type +operator<<(Output &Out, T &Val) { + EmptyContext Ctx; + Out.beginDocuments(); + if (Out.preflightDocument(0)) { + yamlize(Out, Val, true, Ctx); + Out.postflightDocument(); + } + Out.endDocuments(); + return Out; +} + // Provide better error message about types missing a trait specialization template <typename T> inline typename std::enable_if<missingTraits<T, EmptyContext>::value, @@ -1476,6 +1547,18 @@ template <typename T> struct SequenceTraitsImpl { } }; +/// Implementation of CustomMappingTraits for std::map<std::string, T>. +template <typename T> struct StdMapStringCustomMappingTraitsImpl { + typedef std::map<std::string, T> map_type; + static void inputOne(IO &io, StringRef key, map_type &v) { + io.mapRequired(key.str().c_str(), v[key]); + } + static void output(IO &io, map_type &v) { + for (auto &p : v) + io.mapRequired(p.first.c_str(), p.second); + } +}; + } // end namespace yaml } // end namespace llvm @@ -1530,4 +1613,15 @@ template <typename T> struct SequenceTraitsImpl { } \ } +/// Utility for declaring that std::map<std::string, _type> should be considered +/// a YAML map. +#define LLVM_YAML_IS_STRING_MAP(_type) \ + namespace llvm { \ + namespace yaml { \ + template <> \ + struct CustomMappingTraits<std::map<std::string, _type>> \ + : public StdMapStringCustomMappingTraitsImpl<_type> {}; \ + } \ + } + #endif // LLVM_SUPPORT_YAMLTRAITS_H diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp index 99d2070cb6e..51c016b1d55 100644 --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -118,6 +118,18 @@ void Input::beginMapping() { } } +std::vector<StringRef> Input::keys() { + MapHNode *MN = dyn_cast<MapHNode>(CurrentNode); + std::vector<StringRef> Ret; + if (!MN) { + setError(CurrentNode, "not a mapping"); + return Ret; + } + for (auto &P : MN->Mapping) + Ret.push_back(P.first()); + return Ret; +} + bool Input::preflightKey(const char *Key, bool Required, bool, bool &UseDefault, void *&SaveInfo) { UseDefault = false; @@ -374,8 +386,8 @@ std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) { } bool Input::MapHNode::isValidKey(StringRef Key) { - for (const char *K : ValidKeys) { - if (Key.equals(K)) + for (std::string &K : ValidKeys) { + if (Key == K) return true; } return false; @@ -451,6 +463,10 @@ void Output::endMapping() { StateStack.pop_back(); } +std::vector<StringRef> Output::keys() { + report_fatal_error("invalid call"); +} + bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault, bool &UseDefault, void *&) { UseDefault = false; diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp index c3e18d33235..dc7c5d47cba 100644 --- a/llvm/unittests/Support/YAMLIOTest.cpp +++ b/llvm/unittests/Support/YAMLIOTest.cpp @@ -2369,6 +2369,68 @@ TEST(YAMLIO, TestMapWithContext) { out.clear(); } +LLVM_YAML_IS_STRING_MAP(int) + +TEST(YAMLIO, TestCustomMapping) { + std::map<std::string, int> x; + x["foo"] = 1; + x["bar"] = 2; + + std::string out; + llvm::raw_string_ostream ostr(out); + Output xout(ostr, nullptr, 0); + + xout << x; + ostr.flush(); + EXPECT_EQ("---\n" + "bar: 2\n" + "foo: 1\n" + "...\n", + out); + + Input yin(out); + std::map<std::string, int> y; + yin >> y; + EXPECT_EQ(2ul, y.size()); + EXPECT_EQ(1, y["foo"]); + EXPECT_EQ(2, y["bar"]); +} + +LLVM_YAML_IS_STRING_MAP(FooBar) + +TEST(YAMLIO, TestCustomMappingStruct) { + std::map<std::string, FooBar> x; + x["foo"].foo = 1; + x["foo"].bar = 2; + x["bar"].foo = 3; + x["bar"].bar = 4; + + std::string out; + llvm::raw_string_ostream ostr(out); + Output xout(ostr, nullptr, 0); + + xout << x; + ostr.flush(); + EXPECT_EQ("---\n" + "bar: \n" + " foo: 3\n" + " bar: 4\n" + "foo: \n" + " foo: 1\n" + " bar: 2\n" + "...\n", + out); + + Input yin(out); + std::map<std::string, FooBar> y; + yin >> y; + EXPECT_EQ(2ul, y.size()); + EXPECT_EQ(1, y["foo"].foo); + EXPECT_EQ(2, y["foo"].bar); + EXPECT_EQ(3, y["bar"].foo); + EXPECT_EQ(4, y["bar"].bar); +} + TEST(YAMLIO, InvalidInput) { // polluting 1 value in the sequence Input yin("---\n- foo: 3\n bar: 5\n1\n- foo: 3\n bar: 5\n...\n"); |

