Conversation
include/yaml-cpp/node/impl.h
Outdated
|
In addition to the nits on the CL, I have a couple high-level questions:
|
TBH I haven't looked at
I think there are probably situations where being able to access the merge node itself would be beneficial, eg. during document conversion, where you might want to change what gets merged into a node before emitting the YAML.
Wrap the code in a preprocessor definition, eg. |
|
I suspect it'd be better to put the implementation in YAML::detail::node - the purpose of YAML::Node is just for wrapping the actual work in an API. The problem with a preprocessor definition is that it's going in the headers, so we have to make sure people set it when they install the library. Maybe a CMake flag that generates the definition? That seems super hacky... |
Okay, I'll do it then.
Edited, disregard what I previously wrote. I'm not sure there is a cleaner way of doing things. |
|
Can not build since to_value wanted in GetValueFromMergeKey is defined in node/impl.h. |
Works for me, but I suppose it's bad design anyway. I've replaced |
d4d783b to
c7e490f
Compare
c7e490f to
985b154
Compare
|
@jbeder: I've just rebased this PR on top of the latest |
|
Did we ever work out a good API to make sure that this doesn't confuse people (and can be turned on/off)? |
|
No, but I'm still partial to using a E.g. in the yaml-cpp code it would be Somewhere in yaml-cpp's If I wanted to include merge key support I'd build it with That way users have to explicitly opt-in to the behaviour, so there shouldn't be any nasty surprises. |
|
I've committed my proposal for turning on/off merge key support. |
|
I disagree that this feature would be unexpected. Every major implementation does this with no easy option to disable: pyyaml, ruby stdlib, java snakeyaml, etc. Also it is mentioned in common introductory material, including the YAML wikipedia page. Also, it is (almost) part of the 1.2 spec*:
which links to a list of recommended interoperable schemas to support, which includes this merge feature. *The interoperable schemas are intentionally left out of the official spec to allow them to evolve independently. |
|
@jbeder How's my implementation of the support switch? Does @joseph-ireland's spec find (which I'd missed) change your stance on whether the support should be default on, default off or always on? What's (if anything) blocking this getting merged? |
|
Strong support to merge this. Currently, we use the old version of "the merge support" and looking forward to the merge into master. |
|
There is an issue with editing documents in this PR. If you try to assign I think a better approach would be to implement it at the NodeBuilder level, and do a single-level copy (this is what pyyaml does), i.e. OnMapEnd, if (node["<<"].IsMap()), loop over its keys and insert them into the node if not overriden, then remove the "<<". This will eliminate the *DEFAULTS ref in the above case, but not any nested references, acting consistently with other implementations |
I'm not sure I understand, what would that look like (eg. as a test)? I don't think I'm ever in a position where I edit a Node that may contain merged keys, so I guess I missed something... |
|
@WrinklyNinja I'd expect something like this to fail: |
|
Ah, I understand now. I'll see about changing my implementation so it passes that test. |
|
So I tried a naive reimplementation in NodeBuilder: void NodeBuilder::OnMapEnd() {
detail::node& node = *m_stack.back();
const std::string mergeKey = "<<";
detail::node& mergeValue = node.get(mergeKey, m_pMemory);
if (mergeValue.type() == NodeType::Sequence) {
for (detail::node_iterator sit = mergeValue.begin();
sit != mergeValue.end(); ++sit) {
InsertAll(*sit->pNode, node);
}
} else {
InsertAll(mergeValue, node);
}
node.remove(mergeKey, m_pMemory);
assert(m_mapDepth > 0);
m_mapDepth--;
Pop();
}
void NodeBuilder::InsertAll(detail::node& sourceNode, detail::node& destinationNode) {
if (sourceNode.type() == NodeType::Map) {
for (detail::node_iterator it = sourceNode.begin();
it != sourceNode.end(); ++it) {
destinationNode.insert(*it->first, *it->second, m_pMemory);
}
}
}However, this fails because it's inserting references to existing nodes, as documented for the higher-level API. I had a look through the code but couldn't find any way to create a copy of a node (which would have to be recursive for protection against overwriting merged map values). I may be missing something, but it seems like editing nodes is broken by design in general, and not just for merged nodes. Merging nodes just makes it easier to hit that brokenness, which I think is way beyond the scope of this PR to fix. |
724d481 to
a66cf12
Compare
Merge keys are specified here[1] for YAML 1.1. While not part of the YAML 1.2 specification, they're very useful and are supported in other implementations[2][3][4] that target 1.2. Support for merge keys is optional and disabled by default. It can be enabled by defining YAML_CPP_SUPPORT_MERGE_KEYS, either directly or by setting the CMake option YAML_CPP_SUPPORT_MERGE_KEYS=ON. [1]: http://yaml.org/type/merge.html [2]: https://github.com/go-yaml/yaml [3]: https://github.com/ruby/psych [4]: https://bitbucket.org/ruamel/yaml
a66cf12 to
5d2dfe8
Compare
|
@jbeder I've rebased this PR off a recent |
|
Could you also add this as a Unit Test. This is related to issue #617 and I'm not sure if this will also work. TEST(NodeTest, AnchorAndMergeKey) {
Node node = 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>());
} |
|
@WrinklyNinja Could you rebase and squash again that we can merge this |
|
@hofbi Who is 'we'? You don't appear to have any authority to merge this (you're not the owner and aren't shown as a collaborator). I added the previous test thinking that you were asking as a maintainer, I'm unwilling to act in the interests of an unknown party without knowing how it'll affect the chances of this PR finally getting merged. For all I know, @jbeder wouldn't want the test I added for you, or want this squashed. |
|
@WrinklyNinja
|
|
@jbeder Could you review the changes again please? |
|
I'm still not sure if we want this. It's not in the official spec, and I
don't like the idea of a preprocessor config.
…On Mon, Oct 8, 2018 at 9:58 AM Markus Hofbauer ***@***.***> wrote:
@jbeder <https://github.com/jbeder> Could you review the changes again
please?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#300 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABAqBqZvuNrZ6CJdZAeuOPKwcnQOsC9Jks5ui2gugaJpZM4D6OV8>
.
|
As joseph-ireland noted, I was wrong in my initial PR message, as the spec does strongly recommend using merge keys, along with other types not included in the spec itself. Quoting the relevant section:
Not supporting merge keys because they're not in the spec document itself seems to directly contradict the text. |
|
Also check node anchors of the spec http://yaml.org/spec/1.2/spec.html#id2785586 |
|
We would like to use this to avoid repetitions of defaults in configurations, which can be rather annoying. I would say the cat is already out of the bag regarding merge keys, they are used heavily, e.g., for CI tool configuration (cf. CircleCI blog entry), and it's mentioned in tutorials. We also have an example in our Buildkite pipeline configuration, where we would need to repeat this snippet several times. Now, we would like to have the same capabilities in our YAML-based configuration implemented using yaml-cpp. I think it would be good for yaml-cpp to merge this for compatibility with the rest what's out there in YAML-land. |
Can't this be supported with a toggle? If someone wants "pure" spec-compliant version they can compile it with such flag, and those who find merge functionality to be one of the best things about YAML file composability and removing duplication could use the feature with yaml-cpp, rather than try to find some other way to do this. As far as I know this is widely supported by libs from other languages too? |
|
I was surprised today to find that yaml-cpp does not support merge keys. Super useful to avoid repetitions. Very much in favor of seeing this implemented! |
Since yaml-cpp does not support, nor intend to support YAML merge anchors, this manually adds this ability to inherit from another robot's plans configuration to avoid duplication See jbeder/yaml-cpp#300
|
It's 2021 now. Any plans on adding this feature? |
|
Would very much appreciate merge keys being supported in yaml-cpp. Took me a while to realize I was doing nothing wrong when I had a node key of "<<" |
|
I think the place this PR ended up is that I don't think it should be in the mainline of yaml-cpp because it's not part of the standard YAML 1.2 spec. I'm going to close this PR so that's clear. |
|
Well you not thinking it should be in the mainline was the start of this years long "conversation" where people tried to convince you that having this as a switchable feature would be beneficial for the library. At least that gives us official stance on this feature and maybe some incentive to start a fork. |
|
Yeah, that's totally fine. This probably would be a good feature on top of the library (like a call to Merge or something). I'm also looking for someone to take over yaml-cpp, so if that future person wants to merge this feature, that's fine too. |
This would close issue #41. While the merge key type isn't mentioned in the YAML 1.2 spec, there's a spec for YAML 1.1, and I know I'm not alone in finding it a very useful feature.