-
Notifications
You must be signed in to change notification settings - Fork 2
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
Use custom derives for builders instead of Macros #2
Comments
@josephDunne I had a chance to look a bit into this yesterday, and I want to make sure I understand it before we make the change. This is what we get out of the generator right now for a builder: The builder trait.
The implementation of the builder trait.
The table object itself.
Currently, builders are generated entirely in flatc, but it sounds like we want to move some of that code generation out of flatc and into a custom derived trait. To be clear:
|
@hollinwilkins I think we should drop the macros and switch to custom derive.
The macro code is difficult to maintain but i did it because:
The macro code is made more difficult by the fact that I can't do macro expansion in the so we have two options:
Custom derive is more ergonomic and more familiar to the rust community (because Serde uses them). thought? |
@josephDunne This is definitely much cleaner. I haven't used Rust custom derives or macro system extensively, so I'll ask you a few implementation details here before I get started:
I know this is not including a lot of items, but I want to make sure I understand the capabilities/limits of custom derive and also the core set of impls/structs we are meant to be generating using macros/custom derive. |
yes
yes. In addition to the structs and impls you outlined we could do something like this:
naturally this goes against the whole point of flatbuffers but it could be useful. A more useful trait might be:
which uses a WeaponBuilder to construct a owned
yes. On a separate note we could replace the struct
All the functions in the This needs some thought so as you are looking into this let me know what you think. For now we can stick with what we have. Will be trivial to change later. |
👍 This makes a lot of sense, it will be nice. I'll try to get a start on this work tonight and keep you posted as I find issues or questions come up |
What do you think of making the base struct from which we derive this so we can reserve
|
@josephDunne I ended up creating a sample (compilable) test file to explore some ideas for the code these macros could produce. It ended up being pretty long because of all the comments I added, so I create a gist for it. Here it is, let me know what you think: https://gist.github.com/hollinwilkins/7c9b8f0c642e5c53e1727e74133b29e2 |
This would force the end user to remember to call the struct SomethingSpec. Also if you have an existing code base it would prevent you from just adding derive annotations to existing structs. So I'm against the idea. |
I think we should go with the standard builder pattern as outlined here: https://aturon.github.io/ownership/builders.html and I think because we are populating a buffer we will have to be a consuming builder i.e. the function above would become:
the all builder methods would take then you could have:
and
|
I agree we need to think about this to open the possibilities. Deref is just as limiting though. So perhaps we need a trait that we implement for common types |
a trait for |
there shouldnt be any name collisions between the builder trait and the impl so think this is ok. Also the name collision will only happen if the ned user of WeaponBuilder imported the trait into scope. Normal usage would be just to import |
Oh I think you where exploring the possibility of making each individual builder a trait itself. No I agree with you that thats not going to work well. Each builder instance will be a struct with normal member functions that utilize common builder functions under the hood (by implementing the common |
Builders are not
then
So...it seems Builders need to be generic over some underlying buffer. They need to be able to build into any type that implements a vector like api. that could be a Vec or a &mut Vec or another Builder. As part of implementing the common |
I need to just double check here. I agree that
|
I had a thought
then in the flatbuffers crate we have:
|
Ive created a new branch rust_support_triage please work there unless you have started on rust_support already (want to keep notifications off the main google repo until we are ready to merge) |
@josephDunne I signed the Google CLA. Here are some thoughts on your feedback:
Agree, let's stick with
Okay, let's stick with the standard. I think C++ provides the Create methods to help with this issue I was solving for here.
I like this, thanks for sending the above link on this subject.
I think this build method should return the offset, which can be used for building vectors and nested objects later. Right?
I think we should follow the C++ implementation here and have a reference to an underlying builder: Users can always access the builder through an accessor function or with the original builder that was created for the table.
Agreed, builders should not be Sync, we should make sure the
I think that there is a lot more to builders than just a vector (at least in the current implementation). There is a lot of information stored Briefly looking at the current implementation of builder, there are many fields:
WeaponBuilder should just reference a Builder, and any other derived builders (Monster, Vec3, etc.) can all reference the same underlying Builder struct. If we make a Builder !Send, then we don't have to worry about multiple threads accessing it at the same time. We could use an Rc in this case for the underlying data.
I think you were correct in using |
Ok agreed. My only concern is that this UOffset is actually a reference to an offset in a buffer that has some lifetime and the Rust type system could express that if we wanted. I will play around and see.
They can be
!Sync will stop threads from sharing one reference to the same builder. But one builder can still be sent across threads (you cant send references across thread boundaries unless you wrap it up in a struct and that struct will then take ownership of that one builder. !Sync will stop that struct from being cloned etc)
I forgot about this. So you are right to have a builder reference another builder struct. |
I tack this back. The builder owns the vec so it cant return just a
|
We should share Builder between WeaponBuilder, MonsterBuilder, etc. as some sort of pointer. My initial thought is an Thinking about threads, concurrency, etc. I don't think we should be concerned too much with it anymore. The implementor can choose the underlying Very roughly, we could structure it like this:
|
This is where we disagree. The introduction of a pointer is extra complication that I don't think fits with the flatbuffer idea. My reasoning: A builder builds a owned slice of
All data is owned and there are no references so this structure is If you do have a concurrent builder (as a secondary use case) then you need to introduce the concept of synchronise access and the only way that is possible is as you had above with a read write lock: So the append only nature of the builder suggests to me that ownership not shared references are the way forward. Here is how we could implement the above code snippet:
There are gaps in the above implementation. As you pointed out builders have a lot of state and the exact sequence of calls need to be worked out. IF references are necessary then the only one that makes sense is a |
@josephDunne I agree with you and see what you're going for here. I'm going to come up with some skeleton implementations and post them here. Agreed adding Rc/Arc is heavyweight, and relying on Rust borrow checker to ensure just one builder at a time would be a huge win. I'll write some code and get back to you with something here. |
|
Let me know what you think of the above. I tried to take into account all of the things we've talked about. I like the DSL a lot, lots of implementation details missing obviously, but I don't think any individual piece will be too difficult. |
I say give the above a go. I can't see anything wrong. I don't like passing in closures but if it works it works. |
@josephDunne Sounds good, I will go ahead with this design then. If we decide we want to get rid of the closure DSL in the future, should be a pretty easy change. |
Currently a system of macros is used to generate builders, which requires a large amount of generated code. Using custom derived traits for the builders instead would save how much generated code we need to produce and simplify creating builders.
The text was updated successfully, but these errors were encountered: