Index: include/llvm/Support/YAMLTraits.h =================================================================== --- include/llvm/Support/YAMLTraits.h +++ include/llvm/Support/YAMLTraits.h @@ -27,6 +27,8 @@ namespace llvm { namespace yaml { +struct EmptyContext {}; + /// This class should be specialized by any type that needs to be converted /// to/from a YAML mapping. For example: /// @@ -550,40 +552,58 @@ void *getContext(); void setContext(void *); - template - void mapRequired(const char* Key, T& Val) { - this->processKey(Key, Val, true); + template void mapRequired(const char *Key, T &Val) { + EmptyContext Ctx; + this->processKey(Key, Val, true, Ctx); + } + template + void mapRequired(const char *Key, T &Val, Context &Ctx) { + this->processKey(Key, Val, true, Ctx); + } + + template void mapOptional(const char *Key, T &Val) { + EmptyContext Ctx; + mapOptionalWithContext(Key, Val, Ctx); } template - typename std::enable_if::value,void>::type - mapOptional(const char* Key, T& Val) { + void mapOptional(const char *Key, T &Val, const T &Default) { + EmptyContext Ctx; + mapOptionalWithContext(Key, Val, Default, Ctx); + } + + template + typename std::enable_if::value, void>::type + mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { // omit key/value instead of outputting empty sequence - if ( this->canElideEmptySequence() && !(Val.begin() != Val.end()) ) + if (this->canElideEmptySequence() && !(Val.begin() != Val.end())) return; - this->processKey(Key, Val, false); + this->processKey(Key, Val, false, Ctx); } - template - void mapOptional(const char* Key, Optional &Val) { - processKeyWithDefault(Key, Val, Optional(), /*Required=*/false); + template + void mapOptionalWithContext(const char *Key, Optional &Val, Context &Ctx) { + this->processKeyWithDefault(Key, Val, Optional(), /*Required=*/false, + Ctx); } - template - typename std::enable_if::value,void>::type - mapOptional(const char* Key, T& Val) { - this->processKey(Key, Val, false); + template + typename std::enable_if::value, void>::type + mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { + this->processKey(Key, Val, false, Ctx); } - template - void mapOptional(const char* Key, T& Val, const T& Default) { - this->processKeyWithDefault(Key, Val, Default, false); + template + void mapOptionalWithContext(const char *Key, T &Val, const T &Default, + Context &Ctx) { + this->processKeyWithDefault(Key, Val, Default, false, Ctx); } private: - template + template void processKeyWithDefault(const char *Key, Optional &Val, - const Optional &DefaultValue, bool Required) { + const Optional &DefaultValue, bool Required, + Context &Ctx) { assert(DefaultValue.hasValue() == false && "Optional shouldn't have a value!"); void *SaveInfo; @@ -593,7 +613,7 @@ Val = T(); if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { - yamlize(*this, Val.getValue(), Required); + yamlize(*this, Val.getValue(), Required, Ctx); this->postflightKey(SaveInfo); } else { if (UseDefault) @@ -601,15 +621,15 @@ } } - template - void processKeyWithDefault(const char *Key, T &Val, const T& DefaultValue, - bool Required) { + template + void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue, + bool Required, Context &Ctx) { void *SaveInfo; bool UseDefault; const bool sameAsDefault = outputting() && Val == DefaultValue; if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo) ) { - yamlize(*this, Val, Required); + yamlize(*this, Val, Required, Ctx); this->postflightKey(SaveInfo); } else { @@ -618,12 +638,12 @@ } } - template - void processKey(const char *Key, T &Val, bool Required) { + template + void processKey(const char *Key, T &Val, bool Required, Context &Ctx) { void *SaveInfo; bool UseDefault; if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) { - yamlize(*this, Val, Required); + yamlize(*this, Val, Required, Ctx); this->postflightKey(SaveInfo); } } @@ -632,17 +652,17 @@ void *Ctxt; }; -template -typename std::enable_if::value,void>::type -yamlize(IO &io, T &Val, bool) { +template +typename std::enable_if::value, void>::type +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { io.beginEnumScalar(); ScalarEnumerationTraits::enumeration(io, Val); io.endEnumScalar(); } -template -typename std::enable_if::value,void>::type -yamlize(IO &io, T &Val, bool) { +template +typename std::enable_if::value, void>::type +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { bool DoClear; if ( io.beginBitSetScalar(DoClear) ) { if ( DoClear ) @@ -652,9 +672,9 @@ } } -template -typename std::enable_if::value,void>::type -yamlize(IO &io, T &Val, bool) { +template +typename std::enable_if::value, void>::type +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { if ( io.outputting() ) { std::string Storage; llvm::raw_string_ostream Buffer(Storage); @@ -674,7 +694,7 @@ template typename std::enable_if::value, void>::type -yamlize(IO &YamlIO, T &Val, bool) { +yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { if (YamlIO.outputting()) { std::string Storage; llvm::raw_string_ostream Buffer(Storage); @@ -691,9 +711,18 @@ } } -template +template +void do_mapping(IO &io, T &Val, Context &Ctx) { + MappingTraits::mapping(io, Val, Ctx); +} + +template void do_mapping(IO &io, T &Val, EmptyContext &Ctx) { + MappingTraits::mapping(io, Val); +} + +template typename std::enable_if::value, void>::type -yamlize(IO &io, T &Val, bool) { +yamlize(IO &io, T &Val, bool, Context &Ctx) { if (has_FlowTraits>::value) io.beginFlowMapping(); else @@ -705,7 +734,7 @@ assert(Err.empty() && "invalid struct trying to be written as yaml"); } } - MappingTraits::mapping(io, Val); + do_mapping(io, Val, Ctx); if (!io.outputting()) { StringRef Err = MappingTraits::validate(io, Val); if (!Err.empty()) @@ -717,36 +746,36 @@ io.endMapping(); } -template +template typename std::enable_if::value, void>::type -yamlize(IO &io, T &Val, bool) { +yamlize(IO &io, T &Val, bool, Context &Ctx) { if (has_FlowTraits>::value) { io.beginFlowMapping(); - MappingTraits::mapping(io, Val); + do_mapping(io, Val, Ctx); io.endFlowMapping(); } else { io.beginMapping(); - MappingTraits::mapping(io, Val); + do_mapping(io, Val, Ctx); io.endMapping(); } } -template +template typename std::enable_if::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)]; } -template -typename std::enable_if::value,void>::type -yamlize(IO &io, T &Seq, bool) { +template +typename std::enable_if::value, void>::type +yamlize(IO &io, T &Seq, bool, Context &Ctx) { if ( has_FlowTraits< SequenceTraits >::value ) { unsigned incnt = io.beginFlowSequence(); unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; for(unsigned i=0; i < count; ++i) { void *SaveInfo; if ( io.preflightFlowElement(i, SaveInfo) ) { - yamlize(io, SequenceTraits::element(io, Seq, i), true); + yamlize(io, SequenceTraits::element(io, Seq, i), true, Ctx); io.postflightFlowElement(SaveInfo); } } @@ -758,7 +787,7 @@ for(unsigned i=0; i < count; ++i) { void *SaveInfo; if ( io.preflightElement(i, SaveInfo) ) { - yamlize(io, SequenceTraits::element(io, Seq, i), true); + yamlize(io, SequenceTraits::element(io, Seq, i), true, Ctx); io.postflightElement(SaveInfo); } } @@ -1241,8 +1270,9 @@ typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docList) { int i = 0; + EmptyContext Ctx; while ( yin.setCurrentDocument() ) { - yamlize(yin, DocumentListTraits::element(yin, docList, i), true); + yamlize(yin, DocumentListTraits::element(yin, docList, i), true, Ctx); if ( yin.error() ) return yin; yin.nextDocument(); @@ -1256,8 +1286,9 @@ inline typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docMap) { + EmptyContext Ctx; yin.setCurrentDocument(); - yamlize(yin, docMap, true); + yamlize(yin, docMap, true, Ctx); return yin; } @@ -1267,8 +1298,9 @@ inline typename std::enable_if::value, Input &>::type operator>>(Input &yin, T &docSeq) { + EmptyContext Ctx; if (yin.setCurrentDocument()) - yamlize(yin, docSeq, true); + yamlize(yin, docSeq, true, Ctx); return yin; } @@ -1277,8 +1309,9 @@ inline typename std::enable_if::value, Input &>::type operator>>(Input &In, T &Val) { + EmptyContext Ctx; if (In.setCurrentDocument()) - yamlize(In, Val, true); + yamlize(In, Val, true, Ctx); return In; } @@ -1296,11 +1329,13 @@ inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &docList) { + EmptyContext Ctx; yout.beginDocuments(); const size_t count = DocumentListTraits::size(yout, docList); for(size_t i=0; i < count; ++i) { if ( yout.preflightDocument(i) ) { - yamlize(yout, DocumentListTraits::element(yout, docList, i), true); + yamlize(yout, DocumentListTraits::element(yout, docList, i), true, + Ctx); yout.postflightDocument(); } } @@ -1313,9 +1348,10 @@ inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &map) { + EmptyContext Ctx; yout.beginDocuments(); if ( yout.preflightDocument(0) ) { - yamlize(yout, map, true); + yamlize(yout, map, true, Ctx); yout.postflightDocument(); } yout.endDocuments(); @@ -1327,9 +1363,10 @@ inline typename std::enable_if::value, Output &>::type operator<<(Output &yout, T &seq) { + EmptyContext Ctx; yout.beginDocuments(); if ( yout.preflightDocument(0) ) { - yamlize(yout, seq, true); + yamlize(yout, seq, true, Ctx); yout.postflightDocument(); } yout.endDocuments(); @@ -1341,9 +1378,10 @@ inline typename std::enable_if::value, Output &>::type operator<<(Output &Out, T &Val) { + EmptyContext Ctx; Out.beginDocuments(); if (Out.preflightDocument(0)) { - yamlize(Out, Val, true); + yamlize(Out, Val, true, Ctx); Out.postflightDocument(); } Out.endDocuments(); Index: unittests/Support/YAMLIOTest.cpp =================================================================== --- unittests/Support/YAMLIOTest.cpp +++ unittests/Support/YAMLIOTest.cpp @@ -2299,3 +2299,76 @@ out.clear(); } } + +namespace llvm { +namespace yaml { +struct MappingContext { + int A = 0; +}; +struct SimpleMap { + int B = 0; + int C = 0; +}; + +struct NestedMap { + NestedMap(MappingContext &Context) : Context(Context) {} + SimpleMap Simple; + MappingContext &Context; +}; + +template <> struct MappingTraits { + static void mapping(IO &io, SimpleMap &sm) { + io.mapRequired("B", sm.B); + io.mapRequired("C", sm.C); + } + + static void mapping(IO &io, SimpleMap &sm, MappingContext &Context) { + mapping(io, sm); + ++Context.A; + io.mapRequired("Context", Context.A); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, NestedMap &nm) { + io.mapRequired("Simple", nm.Simple, nm.Context); + } +}; +} +} + +TEST(YAMLIO, TestMapWithContext) { + llvm::yaml::MappingContext Context; + llvm::yaml::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(); +}