Tree::operator[]
with a const char*
: ryml::NodeRef node = tree["foo"];
, but that function doesn't seem to exist. There is an overload that takes a c4::csubstr
, but I'd rather not have to wrap all the literals. Am I doing something wrong here?
tree["foo"]
the argument type is actually an array, template<size_t N> csubstr(const char (&arr)[N])
, which is then converted to a temporary c4::csubstr
using the N size from the templated constructor. This is O(1) (when you have a const char*
, constructing a c4::csubstr
is O(N) by necessity, as the length is unknown). Unfortunately, csubstr has no overload for constructing from const char*
. Although this seems peculiar, and is undesirable indeed, there's an annoying reason for this : if you provide an overload both for const char*
and const char (&arr)[N]`, the compiler will always pick the first, and never the second -- even if you pass an in-scope array instead of the decayed pointer. That's why I've gone with the utility function to_csubstr() to make it easier.
Tree::operator[](size_t i)
with the string literal (and failing, obviously). It seems like there should be an implicit conversion to csubstr
, but that wasn't happening for me.
Tree::operator[](csubstr)
--- and if it doesn't that's something that should be looked at. With a plain c-string things change however; there you are required to
to_csubstr()
emit()
prototypes use FILE*
, but there doesn't seem to be an equivalent on the parse()
side, and the c4::fs::file_get_contents
used in the tests doesn't seem to be part of c4core. Is there a convenient way for reading from a file source? Right now I'm just throwing the whole file in a char*
myself but it feels like I'm missing something obvious.
@aitken.tim_gitlab - Thanks for asking. You are correct. Indeed, the reasoning here is different for parse()
and emit()
. The former always requires an existing YAML string which is indexed into in the tree, whereas emitting is a bit more abstract in that regard, because we're just dumping characters into a sink. So that's why the API reflects this distinction. Maybe it would make sense to create some wrappers like receiving FILE*
and doing something like file_get_contents()
to pass the buffer to parse()
, but then this would beg the question of also providing wrappers for iostreams, and that is something I'd rather not do - #including streams and std::string in parse is absolutely out of question here. Another reason is that the parse()
overloads would grow in number even further. Yet another reason is that I also do not want to force the c4fs library on users, and getting the file contents is actually a minor task.
So that's why things are the way they are. But I'm open to opposing view or enabling ideas, as I admit the situation is not ideal.
parse
/emit
in this case because seeing the one prototype sent me on a bit of a goose-chase looking for the other, and I suspect this is going to be common. Alternately, showing a file-based input example in the documentation could help guide people in the right direction.
map<string,string>
Hi, I created a documentation with api references at https://rapidyaml.docsforge.com/
Tell me if it's helpful, It's basically a modern version of doxygen.
Disclaimer, I'm the creator of docsforge.
BTW, you're using doxygen groups for preprocess_json
& preprocess_rxmap
. Is that intended?
Hello, I've a problem with the ryml library. Maybe I'm missing something but let me elaborate:
Lets have this simple yaml:
some_entry: 3
other_entry_list:
- first element
- second element
- third_element:
- something_else
What I need to do is to validate such yml. So I wrote code that goes by all children of root, validates its names, then go inside all that has children.. When I approach to "third_element" on the list "other_entry_list" ryml gets weird.. third_element is_map() is true, however is_key() return false
This should help clarify:
TEST(seq_of_map, general)
{
auto tree = parse(R"(
some_entry: 3
other_entry_list:
- first_element
- second_element
- third_element:
- something_else
)");
auto r = tree.rootref();
EXPECT_EQ(r["some_entry"].val(), "3");
auto other = r["other_entry_list"];
EXPECT_EQ(other.is_seq(), true);
EXPECT_EQ(other[0].val(), "first_element");
EXPECT_EQ(other[1].val(), "second_element");
EXPECT_EQ(other[2].is_map(), true);
EXPECT_EQ(other[2]["third_element"].has_key(), true);
EXPECT_EQ(other[2]["third_element"].has_children(), true);
EXPECT_EQ(other[2]["third_element"][0].val(), "something_else");
}
The element that is a map is a sequence member. The colon turns it into a map, which has a value that is a sequence.
Welcome to YAMLS's complex simplicity :-)