[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:
@@ -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
|
||||
----------------
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user