[yamlio] Allow parsing an entire mapping as an enumeration

For when we want to change a configuration option from an enum into a
struct.  The need arose when working on D119599.

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D120363
This commit is contained in:
sstwcw
2022-03-14 01:24:26 +00:00
parent 03078ec20b
commit 65a3712af6
3 changed files with 127 additions and 0 deletions

View File

@@ -551,6 +551,34 @@ you can specialize on the class pointer. Examples:
}
};
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<FooBarEnum> {
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
----------------

View File

@@ -62,6 +62,7 @@ struct MappingTraits {
// 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 @@ template <class T> struct has_MappingValidateTraits<T, EmptyContext> {
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
};
// Test if MappingContextTraits<T>::enumInput() is defined on type T.
template <class T, class Context> struct has_MappingEnumInputTraits {
using Signature_validate = void (*)(class IO &, T &);
template <typename U>
static char test(SameType<Signature_validate, &U::enumInput> *);
template <typename U> static double test(...);
static bool const value =
(sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1);
};
// Test if MappingTraits<T>::enumInput() is defined on type T.
template <class T> struct has_MappingEnumInputTraits<T, EmptyContext> {
using Signature_validate = void (*)(class IO &, T &);
template <typename U>
static char test(SameType<Signature_validate, &U::enumInput> *);
template <typename U> static double test(...);
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
};
// Test if SequenceTraits<T> is defined on type T.
template <class T>
struct has_SequenceMethodTraits
@@ -1058,9 +1084,30 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
io.endMapping();
}
template <typename T, typename Context>
std::enable_if_t<!has_MappingEnumInputTraits<T, Context>::value, bool>
yamlizeMappingEnumInput(IO &io, T &Val) {
return false;
}
template <typename T, typename Context>
std::enable_if_t<has_MappingEnumInputTraits<T, Context>::value, bool>
yamlizeMappingEnumInput(IO &io, T &Val) {
if (io.outputting())
return false;
io.beginEnumScalar();
MappingTraits<T>::enumInput(io, Val);
bool Matched = !io.matchEnumFallback();
io.endEnumScalar();
return Matched;
}
template <typename T, typename Context>
std::enable_if_t<unvalidatedMappingTraits<T, Context>::value, void>
yamlize(IO &io, T &Val, bool, Context &Ctx) {
if (yamlizeMappingEnumInput<T, Context>(io, Val))
return;
if (has_FlowTraits<MappingTraits<T>>::value) {
io.beginFlowMapping();
detail::doMapping(io, Val, Ctx);

View File

@@ -238,6 +238,58 @@ TEST(YAMLIO, TestSequenceMapWriteAndRead) {
}
}
//
// 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<FooBarEnum> {
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.
//