Meaning of .val()==nullptr and .has_val()
gczuczy opened this issue · 2 comments
Hello,
I've got the following input:
{'fermenter': null, 'id': 1}
and the following code for it:
std::cout << _node << std::endl;
ryml::ConstNodeRef f = _node["fermenter"];
std::cout << f << std::endl;
printf("fermenter isnull: %c\n", (f.val() ==nullptr)?'t':'f');
printf("fermenter hasval: %c\n", (f.has_val())?'t':'f');
Which prints:
data:
'fermenter': null
'id': 1
'fermenter': null
fermenter isnull: f
fermenter hasval: t
It's on tag v0.5.0
, and I'm trying to check for null values based on
rapidyaml/samples/quickstart.cpp
Line 2198 in 66c936e
Could you please adjust the examples? Btw val_is_null
is working as expected.
When the input has value, the output is:
data:
'fermenter':
'id': '1'
'id': 1
fermenter:
'id': '1'
Abort trap (core dumped)
Segfaults are the first printf. val_is_null()
also segfaults here.
Thanks
This is a continuation of #409.
All the behavior you describe is intended.
You seem to be laboring under a misunderstanding about what these functions do.
.has_val()
is purely a predicate to check if a node has a (possibly empty) scalar as its value..has_val()
is a structural query, and the logical opposite of.is_container()
in YAML structure; a container cannot be a val(scalar), and a val(scalar) cannot be a container. It tells you nothing about the contents of the scalar itself; only that it has a scalar and is therefore not a container. For example, in the YAML{a: }
for noderoot["a"]
,.has_val()
is true, even though its val is set to{nullptr, 0}
. But it is set, and this is the intent of the query.- when a node does not have a val, using
.val()
to get the val is UB. It causes an assertion in debug builds and gives you garbage in optimized builds. - likewise, if you ask whether
.val_is_null()
you will get the same assertion because- this check involves a string comparison against
"null"
,"~"
,"Null"
, etc - to get the val string, it needs to exist
- the node
.is_container()
, and does not have a val, so you can't read the val - hence the assertion you get when calling
.val_is_null()
when the node is a container
- this check involves a string comparison against
As for nullity, in the first example:
f
has the string"null"
as its scalar value- so it
.has_val()
- the val is a non-empty string, and has address. So comparing with
nullptr
yields false, because it is pointing at the string "null" which is non-null .val_is_null()
is indeed what you seem to be looking for. This function gets the scalar, and checks its contents against the preset list of null-meaning strings in YAML. But again, the subject node needs to verify.has_val()
, ie the subject node cannot be a container.
Consider this:
const Tree tree = parse_in_arena(R"(
a:
b: null
c: ~
)");
ConstNodeRef root = tree.rootref();
ConstNodeRef a = root["a"];
ConstNodeRef b = root["b"];
ConstNodeRef c = root["c"];
// the following holds:
assert(root.is_map());
assert(root.is_container());
assert(!root.has_val());
assert(a.has_val());
assert(b.has_val());
assert(c.has_val());
assert(a.val() == "");
assert(b.val() == "null");
assert(c.val() == "~");
assert(a.val() == nullptr);
assert(b.val() != nullptr);
assert(c.val() != nullptr);
assert(a.val_is_null());
assert(b.val_is_null());
assert(c.val_is_null());
assert(!root.val_is_null()); // ERROR! UB / crash. root is a container, cannot read the val.
So what you want to do is something like this:
// ...
ConstNodeRef f = _node["fermenter"]; // but ensure that "fermenter" exists!
if(f.is_container()) {
csubstr id = f["id"].val(); // but ensure that "id" exists!
}
else if( ! f.val_is_null()) {
csubstr id = f.val();
}
else {
// how to get the id from null?
}
Got it, thank you. Sorry about not understanding the logic, the samples are not explaining in, and without a documentation that's explaining these things like you do it here, it's mostly trial and error.