-
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
[Go] Object API support #5339
[Go] Object API support #5339
Conversation
Very cool! Code generally looks good to me. @rw can you review? |
Looks like you should run |
@iceb0y Thanks for submitting this PR! The ergonomic improvement this could bring would be large. Before I can merge this, I'd like to see comprehensive tests added to the test suite. Would you be able to do this? Perhaps we can take some ideas from the test suites of other language ports? |
@rw The generate_code.sh already has good coverage about the changes here. Do you mean to actually test the generated code? In that case, the C++ object API is also not tested (search for |
@iceb0y maybe you don't find Lines 445 to 529 in b56d60f
And actually I find further references to MonsterT in that file.
Yes, generated code should generally be tested. Writing something to the level of those C++ tests should not be that much work? |
Also keeping things "experimental" is hard, because there is no easy way for people to tell what code in this repo they can rely on, and what code is "use at your own risk" kind of deal. If something "experimental" gets merged, and the original author stops maintaining it, users may show up with issues, and now @rw has to go in and fix them. I'd rather features go in with a reasonable level of confidence that they're solid, which means tests. It doesn't have to be full featured, but it should be tested. I'd rather have partial functionality that is fully tested than full functionality that is partially tested :) |
Looks like flatbuffers support unions with multiple aliases with the same type. flatbuffers/tests/monster_test.fbs Line 22 in 8d86b53
In this case, we cannot simply use |
Some alternatives: type AnyAmbiguousAliasesT struct {
M1 *MonsterT
M2 *MonsterT
M3 *MonsterT
} type AnyAmbiguousAliasesT struct {
Type AnyAmbiguousAliases
Value interface{}
}
|
Ah yes, that may be a problem. Also, union members can also be strings, can 2 is what C++ does, not sure if it comes natural to Go, but it appears that it would work. It has the advantage that if there are no aliases, then the Go programmer can choose to ignore the type field? Inlining sounds fine to me, given that go lacks "inline" structs, so this would be more efficient. The problem is of course with having 2 of the same union in one table, though that could be fixed by always pre-fixing with the field name. @rw may know better. |
@iceb0y Personally, I like (1) the most. But, I think most of our users would want something closer to (2).
type AnyAmbiguousAliasesTValue interface {
AnyAmbiguousAliasesTValue()
}
func (_ *MonsterT) AnyAmbiguousAliasesTValue() {}
type AnyAmbiguousAliasesT struct {
Type AnyAmbiguousAliases
Value AnyAmbiguousAliasesTValue
} That gives us more type safety. |
I guess when it comes to the object API, design should be guided by what would give the most ergonomic typical Go code that is closest to hand-written. |
# Conflicts: # src/idl_gen_go.cpp # tests/MyGame/Example/Any.go # tests/MyGame/Example/AnyAmbiguousAliases.go # tests/MyGame/Example/AnyUniqueAliases.go
@aardappel can you check this PR now. @googlebot I consent. |
import ( | ||
"strconv" | ||
|
||
flatbuffers "github.com/google/flatbuffers/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.
How did we gain this import? why is it necessary when previously it wasn't
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 refactor for the Union Pack/UnPack methods requires access to flatbuffers.(Builder/Table) and so we need to import it
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 existing code already required access to these types?
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.
Yes
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.
So why didn't the existing code need this import?
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.
This code was in a different file. Since I have refactored the code into a different file it now requires the import in this file.
Code looks good to me. See 2 small comments above, and also, some minor docs, please? |
I'm helping a project experiment with swapping protobuf for flatbuffers and I'm happy to see this PR in the works already. The project's code uses the autogenerated protobuf structs as internal data containers, so having this object API will allow me to do a first pass conversion without reworking too much code. Something to consider: having an API similar to the protobuf one would reduce friction for people trying to experiment with flatbuffers as a protobuf replacement. More concretely, could we add Marshal/Unmarshal functions that sit a step above than Pack/Unpack? I'd also suggest adding a UnpackTo func so that the user can control where the object lives. And I prefer Unpack over UnPack since it's actually one word. Here's an off-the-cuff sketch of what the api could look like: func (m *MonsterT) Marshal() ([]byte, error) {
b := flatbuffers.NewBuilder(0)
b.Finish(m.Pack(b))
return b.FinishedBytes(), nil
}
func (m *MonsterT) Unmarshal(data []byte) error {
GetRootAsMonster(data, 0).UnpackTo(m)
return nil
}
func (m *MonsterT) Pack(b *flatbuffers.Builder) flatbuffers.UOffsetT {
...
}
func (m *Monster) UnpackTo(dst *MonsterT) {
...
}
func (m *Monster) Unpack() (*MonsterT) {
dst := &MonsterT{}
m.UnpackTo(dst)
return dst
} I can also hack on this but don't want to step on anyone's toes. The Marshal/Unmarshal stuff can maybe be a separate PR later to keep this PR moving, but we should consider the Unpack vs UnPack thing before merging. Edit: oops, pack function above should be a regular func rather than a method, but i'm not sure why that's preferred. No strong preference from me on that, though the method is less verbose. func MonsterPack(b *flatbuffers.Builder, t *MonsterT) flatbuffers.UOffsetT {
...
} |
I finally had some time to look at this. Some amendments to my comment above:
|
@llchan we may want to try and get this PR merged if you think its otherwise reasonable, and do some changes in a followup, since its been hanging for a while now.. @iceb0y there are Go CI errors that can easily be fixed by running |
Yep, LGTM. The UnPackTo will leave the UnPack interface unchanged so it can sit in a separate PR. I have those changes ready as well. |
@iceb0y can you fix the CI like I said above, rebase, and then we can merge? |
@@ -644,7 +652,7 @@ class GoGenerator : public BaseGenerator { | |||
} | |||
} | |||
|
|||
// Mutate the value of a struct's scalar. | |||
// Mutate the value of a struct's scalar. |
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.
Nit: spurious whitespace change.
To be more precise, need to rebase before regenerating. I think the CI tests are merging with master before testing, which is introducing new fields to the test fbs. |
Btw @iceb0y if you want me to take over the PR I'm happy to do the rebase/squash and open a new one. Will of course credit you (and @tigrato) with the majority of the legwork. |
Done. |
@tigrato I think you may need to consent in a standalone message |
@googlebot I consent. |
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
done |
@aardappel @rw just a friendly ping. Whenever this gets merged I'll push up the UnPackTo change + rebase my other PR. |
Awesome, thanks for the work everyone! |
thanks a lot!!! thanks all again!! |
* start * works for current usages! * unpack: vector of struct * optimize byte slice * support nested struct * support null table * support struct * support union * update generated code * grumble * fix compiler warning * update generated code * wrap type in namespace * bug * wrap in namespace * enum byte arrays * generate struct for unions * basic testing * remove branching * fix assert * pack vector of fixed structs correctly * omit null vectors * Refactor Union Pack and UnPack methods Remove append usage to increase code efficiency when dealing with large vectors * generate goldens
This PR adds object API support to golang. Related to #5072.