Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ os:
compiler:
- clang
- gcc
env:
- YAML_CPP_SUPPORT_MERGE_KEYS=ON
- YAML_CPP_SUPPORT_MERGE_KEYS=OFF
before_install:
- |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
Expand All @@ -17,7 +20,7 @@ before_install:
before_script:
- mkdir build
- cd build
- cmake ..
- cmake .. -DYAML_CPP_SUPPORT_MERGE_KEYS=$YAML_CPP_SUPPORT_MERGE_KEYS
script:
- make
- test/run-tests
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ enable_testing()
option(YAML_CPP_BUILD_TESTS "Enable testing" ON)
option(YAML_CPP_BUILD_TOOLS "Enable parse tools" ON)
option(YAML_CPP_BUILD_CONTRIB "Enable contrib stuff in library" ON)
option(YAML_CPP_SUPPORT_MERGE_KEYS "Support YAML merge keys ('<<') in yaml-cpp's executable targets. Use '#define YAML_CPP_SUPPORT_MERGE_KEYS' instead when linking from another project." OFF)

## Build options
# --> General
Expand Down Expand Up @@ -97,6 +98,10 @@ else()
add_definitions(-DYAML_CPP_NO_CONTRIB)
endif()

if (YAML_CPP_SUPPORT_MERGE_KEYS)
add_definitions(-DYAML_CPP_SUPPORT_MERGE_KEYS)
endif()

set(library_sources
${sources}
${public_headers}
Expand Down
40 changes: 39 additions & 1 deletion include/yaml-cpp/node/detail/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,23 @@ class node {
// NOTE: this returns a non-const node so that the top-level Node can wrap
// it, and returns a pointer so that it can be NULL (if there is no such
// key).
return static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
node* value = static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
if (!value || value->type() == NodeType::Undefined) {
return get_value_from_merge_key(key, value, pMemory);
}
#endif
return value;
}
template <typename Key>
node& get(const Key& key, shared_memory_holder pMemory) {
node& value = m_pRef->get(key, pMemory);
value.add_dependency(*this);
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
if (value.type() == NodeType::Undefined) {
return *get_value_from_merge_key(key, &value, pMemory);
}
#endif
return value;
}
template <typename Key>
Expand Down Expand Up @@ -159,6 +170,33 @@ class node {
}

private:
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
template <typename Key>
inline node* get_value_from_merge_key(const Key& key, node* currentValue,
shared_memory_holder pMemory) const {
node* mergeValue =
static_cast<const node_ref&>(*m_pRef).get(std::string("<<"), pMemory);
if (!mergeValue) {
return currentValue;
}
if (mergeValue->type() == NodeType::Map) {
return &mergeValue->get(key, pMemory);
}
if (mergeValue->type() == NodeType::Sequence) {
for (const_node_iterator it = mergeValue->begin();
it != mergeValue->end(); ++it) {
if (it->pNode && it->pNode->type() == NodeType::Map) {
node* value = it->pNode->get(key, pMemory);
if (value && value->type() != NodeType::Undefined) {
return value;
}
}
}
}
return currentValue;
}
#endif

shared_node_ref m_pRef;
typedef std::set<node*> nodes;
nodes m_dependencies;
Expand Down
51 changes: 51 additions & 0 deletions test/integration/load_node_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,57 @@ TEST(LoadNodeTest, DereferenceIteratorError) {
EXPECT_THROW(node.begin()->begin()->Type(), InvalidNode);
}

#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
TEST(NodeTest, MergeKeyScalarSupport) {
Node node = Load("{<<: {a: 1}}");
ASSERT_FALSE(!node["a"]);
EXPECT_EQ(1, node["a"].as<int>());
}

TEST(NodeTest, MergeKeyExistingKey) {
Node node = Load("{a: 1, <<: {a: 2}}");
ASSERT_FALSE(!node["a"]);
EXPECT_EQ(1, node["a"].as<int>());
}

TEST(NodeTest, MergeKeySequenceSupport) {
Node node = Load("<<: [{a: 1}, {a: 2, b: 3}]");
ASSERT_FALSE(!node["a"]);
ASSERT_FALSE(!node["b"]);
EXPECT_EQ(1, node["a"].as<int>());
EXPECT_EQ(3, node["b"].as<int>());
}

TEST(NodeTest, NestedMergeKeys) {
Node node = Load("{<<: {<<: {a: 1}}}");
ASSERT_FALSE(!node["a"]);
EXPECT_EQ(1, node["a"].as<int>());
}

TEST(NodeTest, AnchorAndMergeKey) {
Node node = YAML::Load(R"(
a_root: &root_anchor
key1: value1
key2: value2
b_child:
<<: *root_anchor
key2: value2_override
)");

ASSERT_FALSE(!node["a_root"]);
ASSERT_FALSE(!node["b_child"]);
EXPECT_EQ("value1", node["a_root"]["key1"].as<std::string>());
EXPECT_EQ("value2", node["a_root"]["key2"].as<std::string>());
EXPECT_EQ("value1", node["b_child"]["key1"].as<std::string>());
EXPECT_EQ("value2_override", node["b_child"]["key2"].as<std::string>());
}
#else
TEST(NodeTest, MergeKeySupport) {
Node node = Load("{<<: {a: 1}}");
ASSERT_FALSE(node["a"]);
}
#endif

TEST(NodeTest, EmitEmptyNode) {
Node node;
Emitter emitter;
Expand Down