[YAMLIO] Add the ability to map with context.

mapping a yaml field to an object in code has always been
a stateless operation.  You could still pass state by using the
`setContext` function of the YAMLIO object, but this represented
global state for the entire yaml input.  In order to have
context-sensitive state, it is necessary to pass this state in
at the granularity of an individual mapping.

This patch adds support for this type of context-sensitive state.
You simply pass an additional argument of type T to the
`mapRequired` or `mapOptional` functions, and provided you have
specialized a `MappingContextTraits<U, T>` class with the
appropriate mapping function, you can pass this context into
the mapping function.

Reviewed By: chandlerc
Differential Revision: https://reviews.llvm.org/D24162

llvm-svn: 280977
This commit is contained in:
Zachary Turner 2016-09-08 18:22:44 +00:00
parent ba7c5cfbbb
commit 35377f88f5
3 changed files with 262 additions and 101 deletions

View File

@ -27,6 +27,8 @@
namespace llvm { namespace llvm {
namespace yaml { namespace yaml {
struct EmptyContext {};
/// This class should be specialized by any type that needs to be converted /// This class should be specialized by any type that needs to be converted
/// to/from a YAML mapping. For example: /// to/from a YAML mapping. For example:
/// ///
@ -49,6 +51,28 @@ struct MappingTraits {
// static const bool flow = true; // static const bool flow = true;
}; };
/// This class is similar to MappingTraits<T> but allows you to pass in
/// additional context for each map operation. For example:
///
/// struct MappingContextTraits<MyStruct, MyContext> {
/// static void mapping(IO &io, MyStruct &s, MyContext &c) {
/// io.mapRequired("name", s.name);
/// io.mapRequired("size", s.size);
/// io.mapOptional("age", s.age);
/// ++c.TimesMapped;
/// }
/// };
template <class T, class Context> struct MappingContextTraits {
// Must provide:
// static void mapping(IO &io, T &fields, Context &Ctx);
// Optionally may provide:
// static StringRef validate(IO &io, T &fields, Context &Ctx);
//
// The optional flow flag will cause generated YAML to use a flow mapping
// (e.g. { a: 0, b: 1 }):
// static const bool flow = true;
};
/// This class should be specialized by any integral type that converts /// This class should be specialized by any integral type that converts
/// to/from a YAML scalar where there is a one-to-one mapping between /// to/from a YAML scalar where there is a one-to-one mapping between
/// in-memory values and a string in YAML. For example: /// in-memory values and a string in YAML. For example:
@ -258,11 +282,9 @@ public:
(sizeof(test<BlockScalarTraits<T>>(nullptr, nullptr)) == 1); (sizeof(test<BlockScalarTraits<T>>(nullptr, nullptr)) == 1);
}; };
// Test if MappingTraits<T> is defined on type T. // Test if MappingContextTraits<T> is defined on type T.
template <class T> template <class T, class Context> struct has_MappingTraits {
struct has_MappingTraits typedef void (*Signature_mapping)(class IO &, T &, Context &);
{
typedef void (*Signature_mapping)(class IO&, T&);
template <typename U> template <typename U>
static char test(SameType<Signature_mapping, &U::mapping>*); static char test(SameType<Signature_mapping, &U::mapping>*);
@ -271,14 +293,26 @@ struct has_MappingTraits
static double test(...); static double test(...);
public: public:
static bool const value = (sizeof(test<MappingTraits<T> >(nullptr)) == 1); static bool const value =
(sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1);
}; };
// Test if MappingTraits<T>::validate() is defined on type T. // Test if MappingTraits<T> is defined on type T.
template <class T> template <class T> struct has_MappingTraits<T, EmptyContext> {
struct has_MappingValidateTraits typedef void (*Signature_mapping)(class IO &, T &);
{
typedef StringRef (*Signature_validate)(class IO&, T&); template <typename U>
static char test(SameType<Signature_mapping, &U::mapping> *);
template <typename U> static double test(...);
public:
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
};
// Test if MappingContextTraits<T>::validate() is defined on type T.
template <class T, class Context> struct has_MappingValidateTraits {
typedef StringRef (*Signature_validate)(class IO &, T &, Context &);
template <typename U> template <typename U>
static char test(SameType<Signature_validate, &U::validate>*); static char test(SameType<Signature_validate, &U::validate>*);
@ -287,7 +321,21 @@ struct has_MappingValidateTraits
static double test(...); static double test(...);
public: public:
static bool const value = (sizeof(test<MappingTraits<T> >(nullptr)) == 1); static bool const value =
(sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1);
};
// Test if MappingTraits<T>::validate() is defined on type T.
template <class T> struct has_MappingValidateTraits<T, EmptyContext> {
typedef StringRef (*Signature_validate)(class IO &, T &);
template <typename U>
static char test(SameType<Signature_validate, &U::validate> *);
template <typename U> static double test(...);
public:
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
}; };
// Test if SequenceTraits<T> is defined on type T. // Test if SequenceTraits<T> is defined on type T.
@ -432,25 +480,29 @@ inline bool needsQuotes(StringRef S) {
return false; return false;
} }
template<typename T> template <typename T, typename Context>
struct missingTraits : public std::integral_constant<bool, struct missingTraits
!has_ScalarEnumerationTraits<T>::value : public std::integral_constant<bool,
&& !has_ScalarBitSetTraits<T>::value !has_ScalarEnumerationTraits<T>::value &&
&& !has_ScalarTraits<T>::value !has_ScalarBitSetTraits<T>::value &&
&& !has_BlockScalarTraits<T>::value !has_ScalarTraits<T>::value &&
&& !has_MappingTraits<T>::value !has_BlockScalarTraits<T>::value &&
&& !has_SequenceTraits<T>::value !has_MappingTraits<T, Context>::value &&
&& !has_DocumentListTraits<T>::value > {}; !has_SequenceTraits<T>::value &&
!has_DocumentListTraits<T>::value> {};
template<typename T> template <typename T, typename Context>
struct validatedMappingTraits : public std::integral_constant<bool, struct validatedMappingTraits
has_MappingTraits<T>::value : public std::integral_constant<
&& has_MappingValidateTraits<T>::value> {}; bool, has_MappingTraits<T, Context>::value &&
has_MappingValidateTraits<T, Context>::value> {};
template <typename T, typename Context>
struct unvalidatedMappingTraits
: public std::integral_constant<
bool, has_MappingTraits<T, Context>::value &&
!has_MappingValidateTraits<T, Context>::value> {};
template<typename T>
struct unvalidatedMappingTraits : public std::integral_constant<bool,
has_MappingTraits<T>::value
&& !has_MappingValidateTraits<T>::value> {};
// Base class for Input and Output. // Base class for Input and Output.
class IO { class IO {
public: public:
@ -512,9 +564,10 @@ public:
template <typename FBT, typename T> template <typename FBT, typename T>
void enumFallback(T &Val) { void enumFallback(T &Val) {
if (matchEnumFallback()) { if (matchEnumFallback()) {
EmptyContext Context;
// FIXME: Force integral conversion to allow strong typedefs to convert. // FIXME: Force integral conversion to allow strong typedefs to convert.
FBT Res = static_cast<typename FBT::BaseType>(Val); FBT Res = static_cast<typename FBT::BaseType>(Val);
yamlize(*this, Res, true); yamlize(*this, Res, true, Context);
Val = static_cast<T>(static_cast<typename FBT::BaseType>(Res)); Val = static_cast<T>(static_cast<typename FBT::BaseType>(Res));
} }
} }
@ -550,40 +603,58 @@ public:
void *getContext(); void *getContext();
void setContext(void *); void setContext(void *);
template <typename T> template <typename T> void mapRequired(const char *Key, T &Val) {
void mapRequired(const char* Key, T& Val) { EmptyContext Ctx;
this->processKey(Key, Val, true); this->processKey(Key, Val, true, Ctx);
}
template <typename T, typename Context>
void mapRequired(const char *Key, T &Val, Context &Ctx) {
this->processKey(Key, Val, true, Ctx);
}
template <typename T> void mapOptional(const char *Key, T &Val) {
EmptyContext Ctx;
mapOptionalWithContext(Key, Val, Ctx);
} }
template <typename T> template <typename T>
typename std::enable_if<has_SequenceTraits<T>::value,void>::type void mapOptional(const char *Key, T &Val, const T &Default) {
mapOptional(const char* Key, T& Val) { EmptyContext Ctx;
mapOptionalWithContext(Key, Val, Default, Ctx);
}
template <typename T, typename Context>
typename std::enable_if<has_SequenceTraits<T>::value, void>::type
mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) {
// omit key/value instead of outputting empty sequence // omit key/value instead of outputting empty sequence
if ( this->canElideEmptySequence() && !(Val.begin() != Val.end()) ) if (this->canElideEmptySequence() && !(Val.begin() != Val.end()))
return; return;
this->processKey(Key, Val, false); this->processKey(Key, Val, false, Ctx);
} }
template <typename T> template <typename T, typename Context>
void mapOptional(const char* Key, Optional<T> &Val) { void mapOptionalWithContext(const char *Key, Optional<T> &Val, Context &Ctx) {
processKeyWithDefault(Key, Val, Optional<T>(), /*Required=*/false); this->processKeyWithDefault(Key, Val, Optional<T>(), /*Required=*/false,
Ctx);
} }
template <typename T> template <typename T, typename Context>
typename std::enable_if<!has_SequenceTraits<T>::value,void>::type typename std::enable_if<!has_SequenceTraits<T>::value, void>::type
mapOptional(const char* Key, T& Val) { mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) {
this->processKey(Key, Val, false); this->processKey(Key, Val, false, Ctx);
} }
template <typename T> template <typename T, typename Context>
void mapOptional(const char* Key, T& Val, const T& Default) { void mapOptionalWithContext(const char *Key, T &Val, const T &Default,
this->processKeyWithDefault(Key, Val, Default, false); Context &Ctx) {
this->processKeyWithDefault(Key, Val, Default, false, Ctx);
} }
private: private:
template <typename T> template <typename T, typename Context>
void processKeyWithDefault(const char *Key, Optional<T> &Val, void processKeyWithDefault(const char *Key, Optional<T> &Val,
const Optional<T> &DefaultValue, bool Required) { const Optional<T> &DefaultValue, bool Required,
Context &Ctx) {
assert(DefaultValue.hasValue() == false && assert(DefaultValue.hasValue() == false &&
"Optional<T> shouldn't have a value!"); "Optional<T> shouldn't have a value!");
void *SaveInfo; void *SaveInfo;
@ -593,7 +664,7 @@ private:
Val = T(); Val = T();
if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, if (this->preflightKey(Key, Required, sameAsDefault, UseDefault,
SaveInfo)) { SaveInfo)) {
yamlize(*this, Val.getValue(), Required); yamlize(*this, Val.getValue(), Required, Ctx);
this->postflightKey(SaveInfo); this->postflightKey(SaveInfo);
} else { } else {
if (UseDefault) if (UseDefault)
@ -601,15 +672,15 @@ private:
} }
} }
template <typename T> template <typename T, typename Context>
void processKeyWithDefault(const char *Key, T &Val, const T& DefaultValue, void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue,
bool Required) { bool Required, Context &Ctx) {
void *SaveInfo; void *SaveInfo;
bool UseDefault; bool UseDefault;
const bool sameAsDefault = outputting() && Val == DefaultValue; const bool sameAsDefault = outputting() && Val == DefaultValue;
if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault, if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault,
SaveInfo) ) { SaveInfo) ) {
yamlize(*this, Val, Required); yamlize(*this, Val, Required, Ctx);
this->postflightKey(SaveInfo); this->postflightKey(SaveInfo);
} }
else { else {
@ -618,12 +689,12 @@ private:
} }
} }
template <typename T> template <typename T, typename Context>
void processKey(const char *Key, T &Val, bool Required) { void processKey(const char *Key, T &Val, bool Required, Context &Ctx) {
void *SaveInfo; void *SaveInfo;
bool UseDefault; bool UseDefault;
if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) { if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) {
yamlize(*this, Val, Required); yamlize(*this, Val, Required, Ctx);
this->postflightKey(SaveInfo); this->postflightKey(SaveInfo);
} }
} }
@ -632,17 +703,28 @@ private:
void *Ctxt; void *Ctxt;
}; };
template<typename T> namespace detail {
typename std::enable_if<has_ScalarEnumerationTraits<T>::value,void>::type template <typename T, typename Context>
yamlize(IO &io, T &Val, bool) { void doMapping(IO &io, T &Val, Context &Ctx) {
MappingContextTraits<T, Context>::mapping(io, Val, Ctx);
}
template <typename T> void doMapping(IO &io, T &Val, EmptyContext &Ctx) {
MappingTraits<T>::mapping(io, Val);
}
}
template <typename T>
typename std::enable_if<has_ScalarEnumerationTraits<T>::value, void>::type
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
io.beginEnumScalar(); io.beginEnumScalar();
ScalarEnumerationTraits<T>::enumeration(io, Val); ScalarEnumerationTraits<T>::enumeration(io, Val);
io.endEnumScalar(); io.endEnumScalar();
} }
template<typename T> template <typename T>
typename std::enable_if<has_ScalarBitSetTraits<T>::value,void>::type typename std::enable_if<has_ScalarBitSetTraits<T>::value, void>::type
yamlize(IO &io, T &Val, bool) { yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
bool DoClear; bool DoClear;
if ( io.beginBitSetScalar(DoClear) ) { if ( io.beginBitSetScalar(DoClear) ) {
if ( DoClear ) if ( DoClear )
@ -652,9 +734,9 @@ yamlize(IO &io, T &Val, bool) {
} }
} }
template<typename T> template <typename T>
typename std::enable_if<has_ScalarTraits<T>::value,void>::type typename std::enable_if<has_ScalarTraits<T>::value, void>::type
yamlize(IO &io, T &Val, bool) { yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
if ( io.outputting() ) { if ( io.outputting() ) {
std::string Storage; std::string Storage;
llvm::raw_string_ostream Buffer(Storage); llvm::raw_string_ostream Buffer(Storage);
@ -674,7 +756,7 @@ yamlize(IO &io, T &Val, bool) {
template <typename T> template <typename T>
typename std::enable_if<has_BlockScalarTraits<T>::value, void>::type typename std::enable_if<has_BlockScalarTraits<T>::value, void>::type
yamlize(IO &YamlIO, T &Val, bool) { yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
if (YamlIO.outputting()) { if (YamlIO.outputting()) {
std::string Storage; std::string Storage;
llvm::raw_string_ostream Buffer(Storage); llvm::raw_string_ostream Buffer(Storage);
@ -691,9 +773,9 @@ yamlize(IO &YamlIO, T &Val, bool) {
} }
} }
template<typename T> template <typename T, typename Context>
typename std::enable_if<validatedMappingTraits<T>::value, void>::type typename std::enable_if<validatedMappingTraits<T, Context>::value, void>::type
yamlize(IO &io, T &Val, bool) { yamlize(IO &io, T &Val, bool, Context &Ctx) {
if (has_FlowTraits<MappingTraits<T>>::value) if (has_FlowTraits<MappingTraits<T>>::value)
io.beginFlowMapping(); io.beginFlowMapping();
else else
@ -705,7 +787,7 @@ yamlize(IO &io, T &Val, bool) {
assert(Err.empty() && "invalid struct trying to be written as yaml"); assert(Err.empty() && "invalid struct trying to be written as yaml");
} }
} }
MappingTraits<T>::mapping(io, Val); detail::doMapping(io, Val, Ctx);
if (!io.outputting()) { if (!io.outputting()) {
StringRef Err = MappingTraits<T>::validate(io, Val); StringRef Err = MappingTraits<T>::validate(io, Val);
if (!Err.empty()) if (!Err.empty())
@ -717,36 +799,36 @@ yamlize(IO &io, T &Val, bool) {
io.endMapping(); io.endMapping();
} }
template<typename T> template <typename T, typename Context>
typename std::enable_if<unvalidatedMappingTraits<T>::value, void>::type typename std::enable_if<unvalidatedMappingTraits<T, Context>::value, void>::type
yamlize(IO &io, T &Val, bool) { yamlize(IO &io, T &Val, bool, Context &Ctx) {
if (has_FlowTraits<MappingTraits<T>>::value) { if (has_FlowTraits<MappingTraits<T>>::value) {
io.beginFlowMapping(); io.beginFlowMapping();
MappingTraits<T>::mapping(io, Val); detail::doMapping(io, Val, Ctx);
io.endFlowMapping(); io.endFlowMapping();
} else { } else {
io.beginMapping(); io.beginMapping();
MappingTraits<T>::mapping(io, Val); detail::doMapping(io, Val, Ctx);
io.endMapping(); io.endMapping();
} }
} }
template<typename T> template <typename T>
typename std::enable_if<missingTraits<T>::value, void>::type typename std::enable_if<missingTraits<T, EmptyContext>::value, void>::type
yamlize(IO &io, T &Val, bool) { yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)];
} }
template<typename T> template <typename T, typename Context>
typename std::enable_if<has_SequenceTraits<T>::value,void>::type typename std::enable_if<has_SequenceTraits<T>::value, void>::type
yamlize(IO &io, T &Seq, bool) { yamlize(IO &io, T &Seq, bool, Context &Ctx) {
if ( has_FlowTraits< SequenceTraits<T> >::value ) { if ( has_FlowTraits< SequenceTraits<T> >::value ) {
unsigned incnt = io.beginFlowSequence(); unsigned incnt = io.beginFlowSequence();
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt; unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
for(unsigned i=0; i < count; ++i) { for(unsigned i=0; i < count; ++i) {
void *SaveInfo; void *SaveInfo;
if ( io.preflightFlowElement(i, SaveInfo) ) { if ( io.preflightFlowElement(i, SaveInfo) ) {
yamlize(io, SequenceTraits<T>::element(io, Seq, i), true); yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx);
io.postflightFlowElement(SaveInfo); io.postflightFlowElement(SaveInfo);
} }
} }
@ -758,7 +840,7 @@ yamlize(IO &io, T &Seq, bool) {
for(unsigned i=0; i < count; ++i) { for(unsigned i=0; i < count; ++i) {
void *SaveInfo; void *SaveInfo;
if ( io.preflightElement(i, SaveInfo) ) { if ( io.preflightElement(i, SaveInfo) ) {
yamlize(io, SequenceTraits<T>::element(io, Seq, i), true); yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx);
io.postflightElement(SaveInfo); io.postflightElement(SaveInfo);
} }
} }
@ -1241,8 +1323,9 @@ inline
typename std::enable_if<has_DocumentListTraits<T>::value, Input &>::type typename std::enable_if<has_DocumentListTraits<T>::value, Input &>::type
operator>>(Input &yin, T &docList) { operator>>(Input &yin, T &docList) {
int i = 0; int i = 0;
EmptyContext Ctx;
while ( yin.setCurrentDocument() ) { while ( yin.setCurrentDocument() ) {
yamlize(yin, DocumentListTraits<T>::element(yin, docList, i), true); yamlize(yin, DocumentListTraits<T>::element(yin, docList, i), true, Ctx);
if ( yin.error() ) if ( yin.error() )
return yin; return yin;
yin.nextDocument(); yin.nextDocument();
@ -1253,11 +1336,12 @@ operator>>(Input &yin, T &docList) {
// Define non-member operator>> so that Input can stream in a map as a document. // Define non-member operator>> so that Input can stream in a map as a document.
template <typename T> template <typename T>
inline inline typename std::enable_if<has_MappingTraits<T, EmptyContext>::value,
typename std::enable_if<has_MappingTraits<T>::value, Input &>::type Input &>::type
operator>>(Input &yin, T &docMap) { operator>>(Input &yin, T &docMap) {
EmptyContext Ctx;
yin.setCurrentDocument(); yin.setCurrentDocument();
yamlize(yin, docMap, true); yamlize(yin, docMap, true, Ctx);
return yin; return yin;
} }
@ -1267,8 +1351,9 @@ template <typename T>
inline inline
typename std::enable_if<has_SequenceTraits<T>::value, Input &>::type typename std::enable_if<has_SequenceTraits<T>::value, Input &>::type
operator>>(Input &yin, T &docSeq) { operator>>(Input &yin, T &docSeq) {
EmptyContext Ctx;
if (yin.setCurrentDocument()) if (yin.setCurrentDocument())
yamlize(yin, docSeq, true); yamlize(yin, docSeq, true, Ctx);
return yin; return yin;
} }
@ -1277,15 +1362,16 @@ template <typename T>
inline inline
typename std::enable_if<has_BlockScalarTraits<T>::value, Input &>::type typename std::enable_if<has_BlockScalarTraits<T>::value, Input &>::type
operator>>(Input &In, T &Val) { operator>>(Input &In, T &Val) {
EmptyContext Ctx;
if (In.setCurrentDocument()) if (In.setCurrentDocument())
yamlize(In, Val, true); yamlize(In, Val, true, Ctx);
return In; return In;
} }
// Provide better error message about types missing a trait specialization // Provide better error message about types missing a trait specialization
template <typename T> template <typename T>
inline inline typename std::enable_if<missingTraits<T, EmptyContext>::value,
typename std::enable_if<missingTraits<T>::value, Input &>::type Input &>::type
operator>>(Input &yin, T &docSeq) { operator>>(Input &yin, T &docSeq) {
char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)];
return yin; return yin;
@ -1296,11 +1382,13 @@ template <typename T>
inline inline
typename std::enable_if<has_DocumentListTraits<T>::value, Output &>::type typename std::enable_if<has_DocumentListTraits<T>::value, Output &>::type
operator<<(Output &yout, T &docList) { operator<<(Output &yout, T &docList) {
EmptyContext Ctx;
yout.beginDocuments(); yout.beginDocuments();
const size_t count = DocumentListTraits<T>::size(yout, docList); const size_t count = DocumentListTraits<T>::size(yout, docList);
for(size_t i=0; i < count; ++i) { for(size_t i=0; i < count; ++i) {
if ( yout.preflightDocument(i) ) { if ( yout.preflightDocument(i) ) {
yamlize(yout, DocumentListTraits<T>::element(yout, docList, i), true); yamlize(yout, DocumentListTraits<T>::element(yout, docList, i), true,
Ctx);
yout.postflightDocument(); yout.postflightDocument();
} }
} }
@ -1310,12 +1398,13 @@ operator<<(Output &yout, T &docList) {
// Define non-member operator<< so that Output can stream out a map. // Define non-member operator<< so that Output can stream out a map.
template <typename T> template <typename T>
inline inline typename std::enable_if<has_MappingTraits<T, EmptyContext>::value,
typename std::enable_if<has_MappingTraits<T>::value, Output &>::type Output &>::type
operator<<(Output &yout, T &map) { operator<<(Output &yout, T &map) {
EmptyContext Ctx;
yout.beginDocuments(); yout.beginDocuments();
if ( yout.preflightDocument(0) ) { if ( yout.preflightDocument(0) ) {
yamlize(yout, map, true); yamlize(yout, map, true, Ctx);
yout.postflightDocument(); yout.postflightDocument();
} }
yout.endDocuments(); yout.endDocuments();
@ -1327,9 +1416,10 @@ template <typename T>
inline inline
typename std::enable_if<has_SequenceTraits<T>::value, Output &>::type typename std::enable_if<has_SequenceTraits<T>::value, Output &>::type
operator<<(Output &yout, T &seq) { operator<<(Output &yout, T &seq) {
EmptyContext Ctx;
yout.beginDocuments(); yout.beginDocuments();
if ( yout.preflightDocument(0) ) { if ( yout.preflightDocument(0) ) {
yamlize(yout, seq, true); yamlize(yout, seq, true, Ctx);
yout.postflightDocument(); yout.postflightDocument();
} }
yout.endDocuments(); yout.endDocuments();
@ -1341,9 +1431,10 @@ template <typename T>
inline inline
typename std::enable_if<has_BlockScalarTraits<T>::value, Output &>::type typename std::enable_if<has_BlockScalarTraits<T>::value, Output &>::type
operator<<(Output &Out, T &Val) { operator<<(Output &Out, T &Val) {
EmptyContext Ctx;
Out.beginDocuments(); Out.beginDocuments();
if (Out.preflightDocument(0)) { if (Out.preflightDocument(0)) {
yamlize(Out, Val, true); yamlize(Out, Val, true, Ctx);
Out.postflightDocument(); Out.postflightDocument();
} }
Out.endDocuments(); Out.endDocuments();
@ -1352,8 +1443,8 @@ operator<<(Output &Out, T &Val) {
// Provide better error message about types missing a trait specialization // Provide better error message about types missing a trait specialization
template <typename T> template <typename T>
inline inline typename std::enable_if<missingTraits<T, EmptyContext>::value,
typename std::enable_if<missingTraits<T>::value, Output &>::type Output &>::type
operator<<(Output &yout, T &seq) { operator<<(Output &yout, T &seq) {
char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)];
return yout; return yout;

View File

@ -257,7 +257,8 @@ std::unique_ptr<Module> MIRParserImpl::parse() {
bool MIRParserImpl::parseMachineFunction(yaml::Input &In, Module &M, bool MIRParserImpl::parseMachineFunction(yaml::Input &In, Module &M,
bool NoLLVMIR) { bool NoLLVMIR) {
auto MF = llvm::make_unique<yaml::MachineFunction>(); auto MF = llvm::make_unique<yaml::MachineFunction>();
yaml::yamlize(In, *MF, false); yaml::EmptyContext Ctx;
yaml::yamlize(In, *MF, false, Ctx);
if (In.error()) if (In.error())
return true; return true;
auto FunctionName = MF->Name; auto FunctionName = MF->Name;

View File

@ -2299,3 +2299,72 @@ TEST(YAMLIO, TestWrapFlow) {
out.clear(); out.clear();
} }
} }
struct MappingContext {
int A = 0;
};
struct SimpleMap {
int B = 0;
int C = 0;
};
struct NestedMap {
NestedMap(MappingContext &Context) : Context(Context) {}
SimpleMap Simple;
MappingContext &Context;
};
namespace llvm {
namespace yaml {
template <> struct MappingContextTraits<SimpleMap, MappingContext> {
static void mapping(IO &io, SimpleMap &sm, MappingContext &Context) {
io.mapRequired("B", sm.B);
io.mapRequired("C", sm.C);
++Context.A;
io.mapRequired("Context", Context.A);
}
};
template <> struct MappingTraits<NestedMap> {
static void mapping(IO &io, NestedMap &nm) {
io.mapRequired("Simple", nm.Simple, nm.Context);
}
};
}
}
TEST(YAMLIO, TestMapWithContext) {
MappingContext Context;
NestedMap Nested(Context);
std::string out;
llvm::raw_string_ostream ostr(out);
Output yout(ostr, nullptr, 15);
yout << Nested;
ostr.flush();
EXPECT_EQ(1, Context.A);
EXPECT_EQ("---\n"
"Simple: \n"
" B: 0\n"
" C: 0\n"
" Context: 1\n"
"...\n",
out);
out.clear();
Nested.Simple.B = 2;
Nested.Simple.C = 3;
yout << Nested;
ostr.flush();
EXPECT_EQ(2, Context.A);
EXPECT_EQ("---\n"
"Simple: \n"
" B: 2\n"
" C: 3\n"
" Context: 2\n"
"...\n",
out);
out.clear();
}