diff --git a/llvm/docs/YamlIO.rst b/llvm/docs/YamlIO.rst --- a/llvm/docs/YamlIO.rst +++ b/llvm/docs/YamlIO.rst @@ -551,6 +551,34 @@ } }; +There are circumstances where we want to allow the entire mapping to be +read as an enumeration. For example, say some configuration option +started as an enumeration. Then it got more complex so it is now a +mapping. But it is necessary to support the old configuration files. +In that case, add a function ``enumInput`` like for +``ScalarEnumerationTraits::enumeration``. Examples: + +.. code-block:: c++ + + struct FooBarEnum { + int Foo; + int Bar; + bool operator==(const FooBarEnum &R) const { + return Foo == R.Foo && Bar == R.Bar; + } + }; + + template <> struct MappingTraits { + static void enumInput(IO &io, FooBarEnum &Val) { + io.enumCase(Val, "OnlyFoo", FooBarEnum({1, 0})); + io.enumCase(Val, "OnlyBar", FooBarEnum({0, 1})); + } + static void mapping(IO &io, FooBarEnum &Val) { + io.mapOptional("Foo", Val.Foo); + io.mapOptional("Bar", Val.Bar); + } + }; + No Normalization ---------------- diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -62,6 +62,7 @@ // static void mapping(IO &io, T &fields); // Optionally may provide: // static std::string validate(IO &io, T &fields); + // static void enumInput(IO &io, T &value); // // The optional flow flag will cause generated YAML to use a flow mapping // (e.g. { a: 0, b: 1 }): @@ -445,6 +446,31 @@ static bool const value = (sizeof(test>(nullptr)) == 1); }; +// Test if MappingContextTraits::enumInput() is defined on type T. +template struct has_MappingEnumInputTraits { + using Signature_validate = void (*)(class IO &, T &); + + template + static char test(SameType *); + + template static double test(...); + + static bool const value = + (sizeof(test>(nullptr)) == 1); +}; + +// Test if MappingTraits::enumInput() is defined on type T. +template struct has_MappingEnumInputTraits { + using Signature_validate = void (*)(class IO &, T &); + + template + static char test(SameType *); + + template static double test(...); + + static bool const value = (sizeof(test>(nullptr)) == 1); +}; + // Test if SequenceTraits is defined on type T. template struct has_SequenceMethodTraits @@ -1058,9 +1084,30 @@ io.endMapping(); } +template +std::enable_if_t::value, bool> +yamlizeMappingEnumInput(IO &io, T &Val) { + return false; +} + +template +std::enable_if_t::value, bool> +yamlizeMappingEnumInput(IO &io, T &Val) { + if (io.outputting()) + return false; + + io.beginEnumScalar(); + MappingTraits::enumInput(io, Val); + bool Matched = !io.matchEnumFallback(); + io.endEnumScalar(); + return Matched; +} + template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, Context &Ctx) { + if (yamlizeMappingEnumInput(io, Val)) + return; if (has_FlowTraits>::value) { io.beginFlowMapping(); detail::doMapping(io, Val, Ctx); diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp --- a/llvm/unittests/Support/YAMLIOTest.cpp +++ b/llvm/unittests/Support/YAMLIOTest.cpp @@ -238,6 +238,58 @@ } } +// +// Test reading the entire struct as an enum. +// + +struct FooBarEnum { + int Foo; + int Bar; + bool operator==(const FooBarEnum &R) const { + return Foo == R.Foo && Bar == R.Bar; + } +}; + +namespace llvm { +namespace yaml { +template <> struct MappingTraits { + static void enumInput(IO &io, FooBarEnum &Val) { + io.enumCase(Val, "OnlyFoo", FooBarEnum({1, 0})); + io.enumCase(Val, "OnlyBar", FooBarEnum({0, 1})); + } + static void mapping(IO &io, FooBarEnum &Val) { + io.mapOptional("Foo", Val.Foo); + io.mapOptional("Bar", Val.Bar); + } +}; +} // namespace yaml +} // namespace llvm + +TEST(YAMLIO, TestMapEnumRead) { + FooBarEnum Doc; + { + Input Yin("OnlyFoo"); + Yin >> Doc; + EXPECT_FALSE(Yin.error()); + EXPECT_EQ(Doc.Foo, 1); + EXPECT_EQ(Doc.Bar, 0); + } + { + Input Yin("OnlyBar"); + Yin >> Doc; + EXPECT_FALSE(Yin.error()); + EXPECT_EQ(Doc.Foo, 0); + EXPECT_EQ(Doc.Bar, 1); + } + { + Input Yin("{Foo: 3, Bar: 5}"); + Yin >> Doc; + EXPECT_FALSE(Yin.error()); + EXPECT_EQ(Doc.Foo, 3); + EXPECT_EQ(Doc.Bar, 5); + } +} + // // Test YAML filename handling. //