forked from OSchip/llvm-project
Support: Add YAML I/O support for custom mappings.
This will be used to YAMLify parts of the module summary. Differential Revision: https://reviews.llvm.org/D28014 llvm-svn: 290935
This commit is contained in:
parent
5f0793b36c
commit
87dd2ab000
|
@ -209,6 +209,15 @@ struct DocumentListTraits {
|
||||||
// static T::value_type& element(IO &io, T &seq, size_t index);
|
// static T::value_type& element(IO &io, T &seq, size_t index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This class should be specialized by any type that needs to be converted
|
||||||
|
/// to/from a YAML mapping in the case where the names of the keys are not known
|
||||||
|
/// in advance, e.g. a string map.
|
||||||
|
template <typename T>
|
||||||
|
struct CustomMappingTraits {
|
||||||
|
// static void inputOne(IO &io, StringRef key, T &elem);
|
||||||
|
// static void output(IO &io, T &elem);
|
||||||
|
};
|
||||||
|
|
||||||
// Only used for better diagnostics of missing traits
|
// Only used for better diagnostics of missing traits
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct MissingTrait;
|
struct MissingTrait;
|
||||||
|
@ -358,6 +367,23 @@ public:
|
||||||
static bool const value = (sizeof(test<SequenceTraits<T>>(nullptr)) == 1);
|
static bool const value = (sizeof(test<SequenceTraits<T>>(nullptr)) == 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Test if CustomMappingTraits<T> is defined on type T.
|
||||||
|
template <class T>
|
||||||
|
struct has_CustomMappingTraits
|
||||||
|
{
|
||||||
|
typedef void (*Signature_input)(IO &io, StringRef key, T &v);
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static char test(SameType<Signature_input, &U::inputOne>*);
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static double test(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool const value =
|
||||||
|
(sizeof(test<CustomMappingTraits<T>>(nullptr)) == 1);
|
||||||
|
};
|
||||||
|
|
||||||
// has_FlowTraits<int> will cause an error with some compilers because
|
// has_FlowTraits<int> will cause an error with some compilers because
|
||||||
// it subclasses int. Using this wrapper only instantiates the
|
// it subclasses int. Using this wrapper only instantiates the
|
||||||
// real has_FlowTraits only if the template type is a class.
|
// real has_FlowTraits only if the template type is a class.
|
||||||
|
@ -493,6 +519,7 @@ struct missingTraits
|
||||||
!has_BlockScalarTraits<T>::value &&
|
!has_BlockScalarTraits<T>::value &&
|
||||||
!has_MappingTraits<T, Context>::value &&
|
!has_MappingTraits<T, Context>::value &&
|
||||||
!has_SequenceTraits<T>::value &&
|
!has_SequenceTraits<T>::value &&
|
||||||
|
!has_CustomMappingTraits<T>::value &&
|
||||||
!has_DocumentListTraits<T>::value> {};
|
!has_DocumentListTraits<T>::value> {};
|
||||||
|
|
||||||
template <typename T, typename Context>
|
template <typename T, typename Context>
|
||||||
|
@ -531,6 +558,7 @@ public:
|
||||||
virtual void endMapping() = 0;
|
virtual void endMapping() = 0;
|
||||||
virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0;
|
virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0;
|
||||||
virtual void postflightKey(void*) = 0;
|
virtual void postflightKey(void*) = 0;
|
||||||
|
virtual std::vector<StringRef> keys() = 0;
|
||||||
|
|
||||||
virtual void beginFlowMapping() = 0;
|
virtual void beginFlowMapping() = 0;
|
||||||
virtual void endFlowMapping() = 0;
|
virtual void endFlowMapping() = 0;
|
||||||
|
@ -818,6 +846,21 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<has_CustomMappingTraits<T>::value, void>::type
|
||||||
|
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
||||||
|
if ( io.outputting() ) {
|
||||||
|
io.beginMapping();
|
||||||
|
CustomMappingTraits<T>::output(io, Val);
|
||||||
|
io.endMapping();
|
||||||
|
} else {
|
||||||
|
io.beginMapping();
|
||||||
|
for (StringRef key : io.keys())
|
||||||
|
CustomMappingTraits<T>::inputOne(io, key, Val);
|
||||||
|
io.endMapping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<missingTraits<T, EmptyContext>::value, void>::type
|
typename std::enable_if<missingTraits<T, EmptyContext>::value, void>::type
|
||||||
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
||||||
|
@ -1074,6 +1117,7 @@ private:
|
||||||
void endMapping() override;
|
void endMapping() override;
|
||||||
bool preflightKey(const char *, bool, bool, bool &, void *&) override;
|
bool preflightKey(const char *, bool, bool, bool &, void *&) override;
|
||||||
void postflightKey(void *) override;
|
void postflightKey(void *) override;
|
||||||
|
std::vector<StringRef> keys() override;
|
||||||
void beginFlowMapping() override;
|
void beginFlowMapping() override;
|
||||||
void endFlowMapping() override;
|
void endFlowMapping() override;
|
||||||
unsigned beginSequence() override;
|
unsigned beginSequence() override;
|
||||||
|
@ -1157,7 +1201,7 @@ private:
|
||||||
bool isValidKey(StringRef key);
|
bool isValidKey(StringRef key);
|
||||||
|
|
||||||
NameToNode Mapping;
|
NameToNode Mapping;
|
||||||
llvm::SmallVector<const char*, 6> ValidKeys;
|
llvm::SmallVector<std::string, 6> ValidKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SequenceHNode : public HNode {
|
class SequenceHNode : public HNode {
|
||||||
|
@ -1215,6 +1259,7 @@ public:
|
||||||
void endMapping() override;
|
void endMapping() override;
|
||||||
bool preflightKey(const char *key, bool, bool, bool &, void *&) override;
|
bool preflightKey(const char *key, bool, bool, bool &, void *&) override;
|
||||||
void postflightKey(void *) override;
|
void postflightKey(void *) override;
|
||||||
|
std::vector<StringRef> keys() override;
|
||||||
void beginFlowMapping() override;
|
void beginFlowMapping() override;
|
||||||
void endFlowMapping() override;
|
void endFlowMapping() override;
|
||||||
unsigned beginSequence() override;
|
unsigned beginSequence() override;
|
||||||
|
@ -1384,6 +1429,17 @@ operator>>(Input &In, T &Val) {
|
||||||
return In;
|
return In;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define non-member operator>> so that Input can stream in a string map.
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename std::enable_if<has_CustomMappingTraits<T>::value, Input &>::type
|
||||||
|
operator>>(Input &In, T &Val) {
|
||||||
|
EmptyContext Ctx;
|
||||||
|
if (In.setCurrentDocument())
|
||||||
|
yamlize(In, Val, true, Ctx);
|
||||||
|
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 typename std::enable_if<missingTraits<T, EmptyContext>::value,
|
inline typename std::enable_if<missingTraits<T, EmptyContext>::value,
|
||||||
|
@ -1457,6 +1513,21 @@ operator<<(Output &Out, T &Val) {
|
||||||
return Out;
|
return Out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define non-member operator<< so that Output can stream out a string map.
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename std::enable_if<has_CustomMappingTraits<T>::value, Output &>::type
|
||||||
|
operator<<(Output &Out, T &Val) {
|
||||||
|
EmptyContext Ctx;
|
||||||
|
Out.beginDocuments();
|
||||||
|
if (Out.preflightDocument(0)) {
|
||||||
|
yamlize(Out, Val, true, Ctx);
|
||||||
|
Out.postflightDocument();
|
||||||
|
}
|
||||||
|
Out.endDocuments();
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
// 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 typename std::enable_if<missingTraits<T, EmptyContext>::value,
|
inline typename std::enable_if<missingTraits<T, EmptyContext>::value,
|
||||||
|
@ -1476,6 +1547,18 @@ template <typename T> struct SequenceTraitsImpl {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Implementation of CustomMappingTraits for std::map<std::string, T>.
|
||||||
|
template <typename T> struct StdMapStringCustomMappingTraitsImpl {
|
||||||
|
typedef std::map<std::string, T> map_type;
|
||||||
|
static void inputOne(IO &io, StringRef key, map_type &v) {
|
||||||
|
io.mapRequired(key.str().c_str(), v[key]);
|
||||||
|
}
|
||||||
|
static void output(IO &io, map_type &v) {
|
||||||
|
for (auto &p : v)
|
||||||
|
io.mapRequired(p.first.c_str(), p.second);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // end namespace yaml
|
} // end namespace yaml
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
|
@ -1530,4 +1613,15 @@ template <typename T> struct SequenceTraitsImpl {
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility for declaring that std::map<std::string, _type> should be considered
|
||||||
|
/// a YAML map.
|
||||||
|
#define LLVM_YAML_IS_STRING_MAP(_type) \
|
||||||
|
namespace llvm { \
|
||||||
|
namespace yaml { \
|
||||||
|
template <> \
|
||||||
|
struct CustomMappingTraits<std::map<std::string, _type>> \
|
||||||
|
: public StdMapStringCustomMappingTraitsImpl<_type> {}; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
#endif // LLVM_SUPPORT_YAMLTRAITS_H
|
#endif // LLVM_SUPPORT_YAMLTRAITS_H
|
||||||
|
|
|
@ -118,6 +118,18 @@ void Input::beginMapping() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<StringRef> Input::keys() {
|
||||||
|
MapHNode *MN = dyn_cast<MapHNode>(CurrentNode);
|
||||||
|
std::vector<StringRef> Ret;
|
||||||
|
if (!MN) {
|
||||||
|
setError(CurrentNode, "not a mapping");
|
||||||
|
return Ret;
|
||||||
|
}
|
||||||
|
for (auto &P : MN->Mapping)
|
||||||
|
Ret.push_back(P.first());
|
||||||
|
return Ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool Input::preflightKey(const char *Key, bool Required, bool, bool &UseDefault,
|
bool Input::preflightKey(const char *Key, bool Required, bool, bool &UseDefault,
|
||||||
void *&SaveInfo) {
|
void *&SaveInfo) {
|
||||||
UseDefault = false;
|
UseDefault = false;
|
||||||
|
@ -374,8 +386,8 @@ std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Input::MapHNode::isValidKey(StringRef Key) {
|
bool Input::MapHNode::isValidKey(StringRef Key) {
|
||||||
for (const char *K : ValidKeys) {
|
for (std::string &K : ValidKeys) {
|
||||||
if (Key.equals(K))
|
if (Key == K)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -451,6 +463,10 @@ void Output::endMapping() {
|
||||||
StateStack.pop_back();
|
StateStack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<StringRef> Output::keys() {
|
||||||
|
report_fatal_error("invalid call");
|
||||||
|
}
|
||||||
|
|
||||||
bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
|
bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
|
||||||
bool &UseDefault, void *&) {
|
bool &UseDefault, void *&) {
|
||||||
UseDefault = false;
|
UseDefault = false;
|
||||||
|
|
|
@ -2369,6 +2369,68 @@ TEST(YAMLIO, TestMapWithContext) {
|
||||||
out.clear();
|
out.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLVM_YAML_IS_STRING_MAP(int)
|
||||||
|
|
||||||
|
TEST(YAMLIO, TestCustomMapping) {
|
||||||
|
std::map<std::string, int> x;
|
||||||
|
x["foo"] = 1;
|
||||||
|
x["bar"] = 2;
|
||||||
|
|
||||||
|
std::string out;
|
||||||
|
llvm::raw_string_ostream ostr(out);
|
||||||
|
Output xout(ostr, nullptr, 0);
|
||||||
|
|
||||||
|
xout << x;
|
||||||
|
ostr.flush();
|
||||||
|
EXPECT_EQ("---\n"
|
||||||
|
"bar: 2\n"
|
||||||
|
"foo: 1\n"
|
||||||
|
"...\n",
|
||||||
|
out);
|
||||||
|
|
||||||
|
Input yin(out);
|
||||||
|
std::map<std::string, int> y;
|
||||||
|
yin >> y;
|
||||||
|
EXPECT_EQ(2ul, y.size());
|
||||||
|
EXPECT_EQ(1, y["foo"]);
|
||||||
|
EXPECT_EQ(2, y["bar"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVM_YAML_IS_STRING_MAP(FooBar)
|
||||||
|
|
||||||
|
TEST(YAMLIO, TestCustomMappingStruct) {
|
||||||
|
std::map<std::string, FooBar> x;
|
||||||
|
x["foo"].foo = 1;
|
||||||
|
x["foo"].bar = 2;
|
||||||
|
x["bar"].foo = 3;
|
||||||
|
x["bar"].bar = 4;
|
||||||
|
|
||||||
|
std::string out;
|
||||||
|
llvm::raw_string_ostream ostr(out);
|
||||||
|
Output xout(ostr, nullptr, 0);
|
||||||
|
|
||||||
|
xout << x;
|
||||||
|
ostr.flush();
|
||||||
|
EXPECT_EQ("---\n"
|
||||||
|
"bar: \n"
|
||||||
|
" foo: 3\n"
|
||||||
|
" bar: 4\n"
|
||||||
|
"foo: \n"
|
||||||
|
" foo: 1\n"
|
||||||
|
" bar: 2\n"
|
||||||
|
"...\n",
|
||||||
|
out);
|
||||||
|
|
||||||
|
Input yin(out);
|
||||||
|
std::map<std::string, FooBar> y;
|
||||||
|
yin >> y;
|
||||||
|
EXPECT_EQ(2ul, y.size());
|
||||||
|
EXPECT_EQ(1, y["foo"].foo);
|
||||||
|
EXPECT_EQ(2, y["foo"].bar);
|
||||||
|
EXPECT_EQ(3, y["bar"].foo);
|
||||||
|
EXPECT_EQ(4, y["bar"].bar);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(YAMLIO, InvalidInput) {
|
TEST(YAMLIO, InvalidInput) {
|
||||||
// polluting 1 value in the sequence
|
// polluting 1 value in the sequence
|
||||||
Input yin("---\n- foo: 3\n bar: 5\n1\n- foo: 3\n bar: 5\n...\n");
|
Input yin("---\n- foo: 3\n bar: 5\n1\n- foo: 3\n bar: 5\n...\n");
|
||||||
|
|
Loading…
Reference in New Issue