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);
|
||||
};
|
||||
|
||||
/// 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
|
||||
template <typename T>
|
||||
struct MissingTrait;
|
||||
|
@ -358,6 +367,23 @@ public:
|
|||
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
|
||||
// it subclasses int. Using this wrapper only instantiates the
|
||||
// real has_FlowTraits only if the template type is a class.
|
||||
|
@ -493,6 +519,7 @@ struct missingTraits
|
|||
!has_BlockScalarTraits<T>::value &&
|
||||
!has_MappingTraits<T, Context>::value &&
|
||||
!has_SequenceTraits<T>::value &&
|
||||
!has_CustomMappingTraits<T>::value &&
|
||||
!has_DocumentListTraits<T>::value> {};
|
||||
|
||||
template <typename T, typename Context>
|
||||
|
@ -531,6 +558,7 @@ public:
|
|||
virtual void endMapping() = 0;
|
||||
virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0;
|
||||
virtual void postflightKey(void*) = 0;
|
||||
virtual std::vector<StringRef> keys() = 0;
|
||||
|
||||
virtual void beginFlowMapping() = 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>
|
||||
typename std::enable_if<missingTraits<T, EmptyContext>::value, void>::type
|
||||
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
||||
|
@ -1074,6 +1117,7 @@ private:
|
|||
void endMapping() override;
|
||||
bool preflightKey(const char *, bool, bool, bool &, void *&) override;
|
||||
void postflightKey(void *) override;
|
||||
std::vector<StringRef> keys() override;
|
||||
void beginFlowMapping() override;
|
||||
void endFlowMapping() override;
|
||||
unsigned beginSequence() override;
|
||||
|
@ -1157,7 +1201,7 @@ private:
|
|||
bool isValidKey(StringRef key);
|
||||
|
||||
NameToNode Mapping;
|
||||
llvm::SmallVector<const char*, 6> ValidKeys;
|
||||
llvm::SmallVector<std::string, 6> ValidKeys;
|
||||
};
|
||||
|
||||
class SequenceHNode : public HNode {
|
||||
|
@ -1215,6 +1259,7 @@ public:
|
|||
void endMapping() override;
|
||||
bool preflightKey(const char *key, bool, bool, bool &, void *&) override;
|
||||
void postflightKey(void *) override;
|
||||
std::vector<StringRef> keys() override;
|
||||
void beginFlowMapping() override;
|
||||
void endFlowMapping() override;
|
||||
unsigned beginSequence() override;
|
||||
|
@ -1384,6 +1429,17 @@ operator>>(Input &In, T &Val) {
|
|||
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
|
||||
template <typename T>
|
||||
inline typename std::enable_if<missingTraits<T, EmptyContext>::value,
|
||||
|
@ -1457,6 +1513,21 @@ operator<<(Output &Out, T &Val) {
|
|||
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
|
||||
template <typename T>
|
||||
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 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
|
||||
|
|
|
@ -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,
|
||||
void *&SaveInfo) {
|
||||
UseDefault = false;
|
||||
|
@ -374,8 +386,8 @@ std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) {
|
|||
}
|
||||
|
||||
bool Input::MapHNode::isValidKey(StringRef Key) {
|
||||
for (const char *K : ValidKeys) {
|
||||
if (Key.equals(K))
|
||||
for (std::string &K : ValidKeys) {
|
||||
if (Key == K)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -451,6 +463,10 @@ void Output::endMapping() {
|
|||
StateStack.pop_back();
|
||||
}
|
||||
|
||||
std::vector<StringRef> Output::keys() {
|
||||
report_fatal_error("invalid call");
|
||||
}
|
||||
|
||||
bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
|
||||
bool &UseDefault, void *&) {
|
||||
UseDefault = false;
|
||||
|
|
|
@ -2369,6 +2369,68 @@ TEST(YAMLIO, TestMapWithContext) {
|
|||
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) {
|
||||
// polluting 1 value in the sequence
|
||||
Input yin("---\n- foo: 3\n bar: 5\n1\n- foo: 3\n bar: 5\n...\n");
|
||||
|
|
Loading…
Reference in New Issue