-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[c++] Add support for boolean types in flexbuffers #4386
Conversation
e828a24
to
b6ef1b5
Compare
Thanks! Not sure how I feel about this, as so far we intended for booleans to be the same as ints, to keep things simple. I.e. FlexBuffers stores things as they are represented, not what they mean. Then again, we already have an explicit null too, and maybe it would help interop with JSON to not lose "true" and "false". Anyone have an opinion? |
Thanks for considering my pull request. The reason for the pull request is to have better compatibility with JSON and not lose "true" and "false". |
I agree that would be nice. Let me have a look.. |
b6ef1b5
to
02c6857
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.. looks pretty good!
include/flatbuffers/flexbuffers.h
Outdated
@@ -377,12 +379,44 @@ class Reference { | |||
case TYPE_NULL: return 0; | |||
case TYPE_STRING: return flatbuffers::StringToInt(AsString().c_str()); | |||
case TYPE_VECTOR: return static_cast<int64_t>(AsVector().size()); | |||
case TYPE_BOOL: return static_cast<int64_t>(AsBool()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just call ReadInt64
here?
include/flatbuffers/flexbuffers.h
Outdated
default: | ||
// Convert other things to int. | ||
return 0; | ||
} | ||
} | ||
|
||
bool AsBool() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put this behind all the AsInt functions?
include/flatbuffers/flexbuffers.h
Outdated
default: | ||
// Convert other things to int. | ||
return 0; | ||
} | ||
} | ||
|
||
bool AsBool() const { | ||
if (type_ == TYPE_BOOL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The body could be if (type_ == TYPE_STRING) { .. } else return AsInt64() != 0
Actually I'm not sure how I feel about this string special-case.. if the JSON contains "true" as a string, it be better to serialize it as a TYPE_BOOL, and then we wouldn't need this code here at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only issue I see with that you can lose semantic meaning.
A very contrived example.
{
"question" : "Is flex awesome",
"answer" : "true",
"choices" : ["true", "false", "unsure"]
}
Someone would expect the type of answer to always be a string by converting a string during parsing to a boolean you that meaning.
So which direction would you want me to go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(sorry for the late reply, somehow missed this)
Yeah, this is indeed tricky. I guess one could argue that if someone writes "true"
instead of true
then maybe that is what they want, or if they didn't, then the fact that they get costlier string storage is their own fault.
If we go that way though, you could also argue that a string should always be false
, regardless of wether it contains "true"
, since a string is not a boolean. Or maybe, ""
should be false and all other strings true, analogous to how it converts strings/vectors to int. That is already implied by return AsInt64() != 0
. I guess I don't like this string special case.
If were to go with having the parser convert "true"
to a boolean value, then ToString
would get the correct string back, but AsString
wouldn't. So yeah, maybe lets not go with that.
I'm kind of in favour of the simplest solution here, meaning no special case at all.
include/flatbuffers/flexbuffers.h
Outdated
@@ -404,6 +438,7 @@ class Reference { | |||
case TYPE_NULL: return 0; | |||
case TYPE_STRING: return flatbuffers::StringToUInt(AsString().c_str()); | |||
case TYPE_VECTOR: return static_cast<uint64_t>(AsVector().size()); | |||
case TYPE_BOOL: return static_cast<uint64_t>(AsBool()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again just call ReadInt64(data_, parent_width_)
directly.
include/flatbuffers/flexbuffers.h
Outdated
@@ -431,6 +466,7 @@ class Reference { | |||
case TYPE_NULL: return 0.0; | |||
case TYPE_STRING: return strtod(AsString().c_str(), nullptr); | |||
case TYPE_VECTOR: return static_cast<double>(AsVector().size()); | |||
case TYPE_BOOL: return static_cast<double>(AsBool()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReadInt64(data_, parent_width_)
include/flatbuffers/flexbuffers.h
Outdated
@@ -1194,6 +1239,8 @@ class Builder FLATBUFFERS_FINAL_CLASS { | |||
|
|||
Value() : i_(0), type_(TYPE_NULL), min_bit_width_(BIT_WIDTH_8) {} | |||
|
|||
Value(bool b) : u_(b ? 1 : 0), type_(TYPE_BOOL), min_bit_width_(BIT_WIDTH_8) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just static_cast<int64_t>(b)
include/flatbuffers/idl.h
Outdated
@@ -420,12 +420,13 @@ struct IDLOptions { | |||
|
|||
// This encapsulates where the parser is in the current source file. | |||
struct ParserState { | |||
ParserState() : cursor_(nullptr), line_(1), token_(-1) {} | |||
ParserState() : cursor_(nullptr), line_(1), token_(-1), is_bool_(false) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a bit of a hack :)
The better way is to introduce a kTokenBooleanConstant
, and to check the uses of kTokenIntegerConstant
to also deal with this new token (in some cases, not all)
@@ -391,6 +392,7 @@ CheckedError Parser::Next() { | |||
if (attribute_ == "true" || attribute_ == "false") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in ParseFlexBufferValue
, you should check for strings true/false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed above, lets not do this.
tests/test.cpp
Outdated
TEST_EQ(vec[0].AsInt64(), -100); | ||
TEST_EQ_STR(vec[1].AsString().c_str(), "Fred"); | ||
TEST_EQ(vec[1].AsInt64(), 0); // Number parsing failed. | ||
TEST_EQ(vec[2].AsDouble(), 4.0); | ||
TEST_EQ(vec[2].AsString().IsTheEmptyString(), true); // Wrong Type. | ||
TEST_EQ_STR(vec[2].AsString().c_str(), ""); // This still works though. | ||
TEST_EQ_STR(vec[2].ToString().c_str(), "4.0"); // Or have it converted. | ||
TEST_EQ(vec[3].IsBool(), true); // This is a boolean |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These comment don't explain anything extra.. same below.
8557909
to
2a7f493
Compare
tests/test.cpp
Outdated
slb.Double("foo", 100); | ||
slb.Map("mymap", [&]() { | ||
slb.String("foo", "Fred"); // Testing key and string reuse. | ||
slb.String("sbool1", "true"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these tests can be removed if we're not supporting booleans in strings
070183f
to
5754ff0
Compare
Updated everything let me know if there is anything else needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good! just a little code simplification..
include/flatbuffers/flexbuffers.h
Outdated
@@ -363,6 +365,13 @@ class Reference { | |||
bool IsMap() const { return type_ == TYPE_MAP; } | |||
bool IsBlob() const { return type_ == TYPE_BLOB; } | |||
|
|||
bool AsBool() const { | |||
if (type_ == TYPE_BOOL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return (type_ == TYPE_BOOL ? .. : ..) != 0
include/flatbuffers/flexbuffers.h
Outdated
@@ -588,6 +603,13 @@ class Reference { | |||
} | |||
} | |||
|
|||
bool MutateBool(bool b) { | |||
if (type_ == TYPE_BOOL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return type_ == TYPE_BOOL && ..
Done! |
Thanks, nice improvement! |
That would also require to add a new type TYPE_VECTOR_BOOL. |
That is not strictly necessary (you can just use |
Since there is no TYPE_VECTOR_BOOL there is no need to change IsTypedVectorElementType. Unless I misunderstand the purpose of the function. Which I assume is to determine if a type can be a turned into a typed vector. |
That is correct, it would only need to change if |
Well this in not what I meant :) |
Ok, I'm a bit confused about what you're suggesting we do. You're saying you want both a typed vector of bool (that's store as a uint8) and a bitfield? Currently there's no typed vector of bool, so I am not sure what the problem is with As for bitfields, I would not call them Also, your |
Yeah sorry my answer was not really structured. I would suggest following: So I change Now here is the part which confuses me my self when I am looking at it again :) |
Looking at
See if it decodes from JSON and than encodes to proper JSON again. |
fbb.Vector with a std::vector would currently fail, because the code assumes all scalars are available as a typed vector, which bools currently are not. So this would be a good reason to add I don't think vectors of bools should become typed vectors of uint if we have an actual bool type. We should definitely have a typed version, since paying 2 bytes per bool would be a bit wasteful. So my recommendation, for now, is to fix things by adding a |
Cool. Will introduce |
No description provided.