-
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
Java: public default constructor is error-prone #3945
Comments
Classes like C++ has a |
So a decision must be taken. Either add the line of code I suggested so creating an empty flatbuffers is easy as ABC, or add a private constructor to each generated class so nobody can try to create a broken flatbuffers. I would personally prefer the first option to don't add complexity where not needed. How to create an empty Flatbuffers to get default values:First solution:mTheme = new Theme(); // Requires only one extra line in Table#__offset() to return 0 if bb is null. Second solution:FlatBufferBuilder themeBuilder = new FlatBufferBuilder(0);
Theme.startTheme(themeBuilder);
final int o = Theme.endTheme(themeBuilder);
Theme.finishThemeBuffer(themeBuilder, o);
mTheme = Theme.getRootAsTheme(themeBuilder.dataBuffer()); I guess it's pretty obvious which one requires less boilerplate from developers… |
As @GWO said the generated code is there to handle already populated flatbuffers (in java, bytebuffer rightly written by the flatbuffers generated code....ByteBuffer.allocate(4) might not be a rightly built bytebuffer; it might not work for complex flatbuffers that will access bytes fartyher than the first 4 bytes) You should probably handle your problem differently : handling a null bytebuffer (and defaults) could then be done at the ThemeWrapper stage |
Also, if you code for android and if you use kotlin, you might want to use the kotlin code generator implemented there #1190 |
I don't use kotlin yet, but I don't see the benefit of flatbuffers default values if I have to write a boilerplate friendly class myself, which anyway, can't read flatbuffers default values as it will always get an NPE. I mean, if I create other tables, I have to rewrite each default values for each table in a wrapper class? I don't get how you find it more practical that the ability to instantiate a working empty flatbuffers table with the Please, tell me, if I want to use it in java, I have to write another class manually and copy paste all the default values and created delegates for the class under the wrapper, or stop using java? Don't you think it deviates from flatbuffers philosophy which is closer to Don't Repeat Yourself and which aims to keep things simple? Anyway, I'm lucky that it's so easy to edit flatbuffers behavior as there's no read-only dependency, so I added the line I was talking about in the |
First, please wait for what @GWO has to say. I happened to write the Le 15/07/2016 15:46, Louis Cognault a écrit :
I see your point and I think that I understand your need.
|
@Lakedaemon Don't structs default to null when not set? If yes, there should be a way to return |
No, they don't. A struct is never null. They are inlined. If the root of your flatbuffer is a struct that holds an int, you get a bytebuffer with at least 4 bytes (might even be more because of other flatbuffer features (file identifyer)) . |
I'll try adding a struct to my flatbuffers to see how this could be addressed |
@Lakedaemon Where did you see that structs are never null? From what I see by reading generated code, they can be, and the line I added is struct safe in this regard. |
My bad, you are right (I tried to be of help but failed miserably, sorry).
Le 16/07/2016 16:47, Louis Cognault a écrit :
|
@Lakedaemon It made me check that it would work properly with structs, so thanks! Last thing to check is for unions and their type. Do you have an idea? |
Le 16/07/2016 18:02, Louis Cognault a écrit :
Apparently, with java generated code, with offset = 0 So, I guess that it would work. It's trading off convenience against performance : Also, another reason not to do it this way (though it works) is that you The java code generator doesn't have this problem as java doesn't care
|
@Lakedaemon I don't think checking if a reference points to And about kotlin, this is only about java, so maybe if this problem is present in kotlin too, there'd be another way to address it, by hiding the BTW, glad to see it'd work on unions too and that it's absolutely safe for the whole flatbuffers grammar |
Sorry, you can't construct FlatBuffers from empty using the accessor API. You'll have to use building API for that instead. |
@gwvo So can it be simplified for cases when you want to leave all default values untouched? What about something like the following that would generate all the boilerplate code needed to build a flatbuffer with just default values? Code that the programmer needs to write mTheme = Theme.createDefaultTheme();
mMonster = Monster.createDefaultMonster(); Generated boilerplate code public class Theme {
private Theme() {}
public static Theme createDefaultTheme() {
FlatBufferBuilder themeBuilder = new FlatBufferBuilder(0);
Theme.startTheme(themeBuilder);
final int o = Theme.endTheme(themeBuilder);
Theme.finishThemeBuffer(themeBuilder, o);
return Theme.getRootAsTheme(themeBuilder.dataBuffer());
}
...
}
public class Monster {
private Monster() {}
public static Monster createDefaultMonster() {
FlatBufferBuilder monsterBuilder = new FlatBufferBuilder(0);
Monster.startMonster(monsterBuilder);
final int o = Monster.endMonster(monsterBuilder);
Monster.finishMonsterBuffer(monsterBuilder, o);
return Monster.getRootAsMonster(monsterBuilder.dataBuffer());
}
...
} |
I don't see what the point is to go all the way thru the trouble of serializing an empty FlatBuffer just to access default values? |
@gwvo I'm not serializing any flatbuffers with the line I added in the Maybe you have a better solution to access default values as I do when the user didn't configured any theme yet? |
I think the solution is to write the "boilerplate" code you showed.. I don't think this functionality is common enough to have the code generator take care of it. |
Mine is to edit the And about use cases: |
It seems that you were a little uncomfortable with the possible performance hit of adding a null check in the Table class (
I'm doing the 1-line-of-code pull request, hope it'll be accepted |
This allows to call the default constructor and not have a broken object anymore. This also drastically reduces boilerplate when creating a flatbuffer with default values. Solves issue google#3945
Efficiency is the #1 priority in FlatBuffers, even in languages like Java where that isn't easy. Adding an if-check in a very frequently executed part of the code is therefore not to be taken lightly. Now if that if-check would be a major improvement to the API, it could be considered, but in this case it appears the use of it is to save a single person, you, to have to write some extra code. That is not enough reason to make everyone else's code a tiny bit slower for no clear benefit. |
Also, how can 2,000,000,000 null checks only take 3ns when a cpu only has about 3,000,000,000 cycles in a second ? Those null checks in the test might have been ignored by the compiler and I wouldn't trust that benchmark... |
It's an average per loop, done with 2Billion loops. So multiply 3ns by On Mon, Aug 22, 2016, 11:24 PM Olivier Binda notifications@github.com
|
Hi!
I am currently using flatbuffers to both hold default values and allow to sync user themes (for an Android Wear Watch Face) between the handset and the wearables. I always need a
Theme
object (which is a flatbuffersTable
) available to get the default values if the user hasn't set any theme. How to create an empty object is not clear.From the code (and I tried to confirm), creating a new object initially (
new Theme()
) is possible, and creates one with anull``ByteBuffer
, leading to aNullPointerException
as soon as you call an accessor.I tried calling this:
But got an
IndexOutOfBoundsException
when accessing a property.Finally, this code worked:
IMHO, the first attempt (
new YourFlatbuffersTable()
) should work without pending NPEs, andgetRootAsYourFlatbuffersTable()
should be only needed when you have a real flatbuffers binary.I suggest to address this issue by making the
__offset(…)
method inTable
class check forbb
nullability, and return 0 if it is, so the calling accessor/mutator skips to default value/false without trying to call any method onbb
.This would just need one line at the beginning of the method:
Also, it should be clearly documented (especially for network apps) that accesses can throw
IndexOutOfBoundsException
if wrong data it in theByteBuffer
for current implementation, and IMHO, future implementations should have apublic static boolean checkIntegrity(ByteBuffer _bb)
method generated in each table class, designed to be called beforegetRootAsYourFlatbuffersTable(ByteBuffer _bb, YourFlatbuffersTable obj)
(hence thestatic
keyword) which would prevent someone causing a crash to your app by corrupting network (or shared disk) data. Maybe this is worth a new issue?The text was updated successfully, but these errors were encountered: