-
Notifications
You must be signed in to change notification settings - Fork 37
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
A radical suggestion for QtProtobuf (please sit down before reading) #180
Comments
Hi @gmabey , I used QSharedPointer because there are couple issues related to serializatoin/deserialization. Some issues related to performance, since copy-on-write is not applicable in case of serialization/deserialzation - containers change each time new list portion found in proto stream. There might be raw pointers as well, but it's 2020 already, it's better to relay on modern memory management. I understand concerns about usage, and probably I could try to implement extra helper functions in generated code, that will produce required containers. I need to think how to improve your approach in any case. |
Hmm -- so, if you're deserializing (writing into) a new instance, and there haven't been any copies made of that instance yet, then it wouldn't get copied when non-const members are invoked, right? (because the reference count is still just 1) Maybe I'm missing your point, but it seems to me that there wouldn't be issues deserializing into COW classes. And, if only Where I really have never worked is in the QML interface issues, nor in the |
I made short code revision at code today and remember one more issue that related to objects storing by value in protobuf classes. In protobuf it's possible to make circular dependency between messages. So in case if we would define interfaces that return non-pointer types, that would not be possible. So this idea couldn't be accepted, sorry. |
Hmm -- how about QExplicitlySharedDataPointer?
Glen
…On Sun, Dec 13, 2020 at 4:51 AM Alexey Edelev ***@***.***> wrote:
I made short code revision at code today and remember one more issue that
related to objects storing by value in protobuf classes. In protobuf it's
possible to make circular dependency between messages. So in case if we
would define interfaces that return non-pointer types, that would not be
possible. So this idea couldn't be accepted, sorry.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#180 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABVTOUA5PFIT7W72NQT62M3SUSTEPANCNFSM4UBV5GCA>
.
|
I don't get idea what it should solve? If got the idea of changes you suggest, you want to have:
But for protobuf it's acceptable to have following message structure:
I that case it's not feasible to solve value-based getters using forward declaration, like it's done currently using pointers. |
Ahhh, ok I didn't understand the suggestion. Then I need to think a little bit more. |
Ok, it's more about pimpl, but not about interfaces change. Yep, that what I supposed to add at some point, and shared data could be used, the only thing I would need to understand is how to manage pointers to other messages in any case. |
I'm very glad to see that this feature has been added to a specific milestone! However, it's continuing to haunt me in my usage of qtprotobuf ... is there anything I could do to help move this feature along? I think that the approach I provided in the new_addressbook.zip example should work ... I realize that there are competing priorities here. |
@gmabey I'm open to accepting contributions, so feel free to start the implementation. |
Ok, good -- so do you (at least initially) agree with these points?
|
Almost:
You could start with the adaptation of the generator to support the second point in the list. Perhaps for the simple data only as a starting point. |
So, none of the basic "data" types are derived from Now, I do agree that Here is an example of how this worked out for the QtPositioning classes: https://doc.qt.io/qt-5/positioning-cpp-qml.html I'm not claiming that I fully understand all of the implications here, but think that many things would be greatly simplified if a direct copy constructor were provided by the generated classes, and I don't think that's really going to happen as long as |
You suggest taking the complexity of the QObject implementation to the QtProtobuf generated code. |
Hmm -- actually, I was trying to ask you to reconsider whether the signals are necessary or appropriate for a data class. The reason why I sent the link was to help establish that none of the basic qml types (bool, int, string, url, color, font) nor their C++ counterparts (bool, int, QString, QUrl, QColor, QFont) inherit from QObject (and hence, do not have any "changed" signals). The second link was an attempt to demonstrate that even in the Qt codebase, there are complex classes (with no direct qml counterpart) that integrate well with qml and do it through the use of A separate issue is whether you feel that the qtprotobuf-generated classes need signals. I can tell you that I have not used them, and I'm very curious to know how you have used them. I do not intent to suggest that complexity should be added to the qtprotobuf-generated classes -- to the contrary I think that by removing the QObject inheritance, the entire process will be much simpler and easier to maintain. But it all hinges on whether you need the "changed" signals ... |
Oh -- wait -- do you anticipate using the qtprotobuf-derived classes as a QML Component?? I only ever envisioned using them as a property within a Component ... |
Yeah, then you may change the data model field only and send an update without recreating/copying the whole data model.
And I'm probably lost in terms already, what do you mean under the "data" class. I could suggest starting with establishing the updated structure of the qtprotobuf-generated classes. How I see it:
Because there is a lot of code that provides their support to QML
Yep, but this requires either QtProtobuf user to write a wrapper or generate an additional code for QML
I use it since I do not always expect that all message fields will be updated and connect to only the field that is required for the particular handler.
Not sure that it's easier to maintain in this case. It's extra code that is on QtProtobuf side. Blaming Qt looks easier for me :) |
Preliminary side note: I think it's a lot easier to read these "reply" comments directly in the GitHub issue, instead of in the notification email ...
Incidentally, it was "updating" a qtprotobuf-generated class instance that led me to revisit this today ... Hmm -- do you envision a scenario in which a qtprotobuf-generated class instance would be modified in QML??
Just like you hinted above, I'm thinking of the use case (and, I've only ever used qtprotobuf-generated classes in this scenario) in which an entire message (a qtprotobuf-generated class) is updated (overwritten) when (for example) a new message is received over the network and the new values need to be displayed on the screen. I'm very surprised to think that it could be significantly faster to bind properties directly to members of a message, instead (up until now) I was only thinking of the use case that the "whole message" gets updated (which works for me) and then properties of other QML components get updated via assignment (not binding). At the time of assignment, that's when QML would determine whether the actual value has changed, and a drawing action scheduled. I don't think that the "assignment" option involves much more code than the "on whole item changed, update other properties via assignment" approach, though -- it's either one line for binding or one line for assignment, no? So, when I say "data" class in the context of copy-on-write, I mean an indivisible set of data members (that would change all at once). An imperfect analogy is that a qml string is certainly composed of an array of characters, but there are no NOTIFY signals on individual characters -- just on the whole string. My QML code looks like this: Item {
property Event currentEvent
...
onCurrentEventChanged: {
eventIdText.text = currentEvent.eventId
...
}
} I suspect that for
Yeah I guess it comes down to that -- I don't think that more QML code is needed in order to handle a
Same discussion as above ...
Ok, now at least I understand the motivation here, and I now agree it is a reasonable use model.
I still think there's a disconnect on the copy-on-write behavior and the use of
then I think that copy-on-write can't be implemented, mostly because there is no copy constructor for That is, the primary obstacle to this working is the question "what happens to existing signal and slot connections when copy-on-write occurs?" I think if you review the Qt provided classes I think you'll agree that copy-on-write and QObject are mutually exclusive -- they never overlap, and for good reasons. However, I agree that in order for qtprotobuf to use I hope these ramblings are helpful in the end! |
I'm extremely curious to know what your thoughts and plans are for this suggestion. Like, if you don't agree, that's fine, but it affects some decisions that I need to make soon. I'm also curious to hear perspectives from others who might be following this project (independent of my opinion) while still recognizing all of the leadership and hard work that you (Alexey) have put into this project. |
I've taken a whack at this approach, but I got stuck trying to understand what was going on with the nested messages :-/ https://github.com/gmabey/qtprotobuf/tree/shareddata This is definitely a WIP, but please take a look at what I've got so far. The generated source for all of the WellKnownTypes compiles (they are all pretty simple messages) -- it's the tricky tests that I get tangled up in. And just to review -- these changes propose the following behavior changes:
Also, please be aware that I have not yet tested these changes within my own project; for now I'm just asking for help in understanding the nested messages. In addition, the |
I've now resolved the functional issues I was working against the last time I pushed an update to my fork (nested messages). The following tests now work: A word about my application ... all of the protobuf-deserializing code is threaded apart from the main thread, and there are very deep I know what I've done is not perfect (in particular, I need to create an example of how it would interface with QML) but I'm really curious to know where we go from here. I do not want to fork your project permanently, and I admire all of the very careful work you've done. I hope that you will consider this contribution seriously or advise me that you're not interested; I'm at a point where I really need to know. Best Regards, |
Hi :) Sorry, it's quite hard to review such a lot of code in one piece, but overall I like the idea to use Q_GADGET instead Q_OBJECT. Also I didn't look closely yet at the QProperty<> that was invented several months ago. So I suspect that combination of QProperty<> and Q_GADGET might give even better experience for users, but still I need to understand how it could be combined with QML and other things that might need 'signals' involved. I'll back in couple of days with some more comments, since as I already mentioned I need to read the code and understand what is impact on existing concepts. Regards, P.S. have not too much powers for a while, now I'm trying to recover and get back to the project. |
@semlanik I'm sorry to hear you haven't been doing well and I hope that you recover soon. I just now pushed some more changes to my |
P.S. One of the other ways (I think) to use the message contents in QML is to have the message as a property of another class that does inherit from |
P.P.S. I'd love it if there was a simpler way to handle the initialization of nested messages (it really hit hard in the cyclic loop scenario) than using |
@semlanik any further thoughts on this suggestion? I need to go back and merge your recent commits into my branch, but haven't yet. I am still actively using and appreciating this code! |
@semlanik in case it wasn't clear, one of the main snags I hit as I was trying to get the gRPC stuff to work was the use of |
@semlanik I'm working today on trying to get my fork to compile for Windows, but it's getting pretty ugly. I sure wish I was better at the |
@gmabey sorry I abandoned this repo for now. I'm not sure if I will have time to look in near future, but over all idea to use Q_GADGET as the optional generating output is still relevant I guess. |
@semlanik Do you mean the whole qtprotobuf project? Or, just abandoned the idea of switching your repo to use Q_GADGET? I hope that you will reconsider someday. Particularly for the sake of usage -- it makes for much simpler code for the user, in my humble opinion, and is very Qt-esque. And -- I'm able to access message members in my QML with no problems, just like other Qt data classes. And ... the longer you wait to make the switch, the more painful the change will be for your growing user base ... just sayin'. |
I'm using
qtprotobuf
not only over the wire, but also as internally-used data structures in my application.I often find the appearance of
QSharedPointer
to be awkward, such as when one part of my application generates a list of structures that I am going to assign to a repeated field in a protobuf message to then send over the wire.For example, suppose I have a
.proto
file like this:Now suppose that there's a slot in one of my processing classes that looks like this:
I just find myself wishing that I could use the `` directly in assigning the
attrs
field, instead of having to created a whole bunch of new QSharedPointer's. Like, the use of QSharedPointer is a detail that it seems like the user shouldn't have to be aware of.Now, I don't think I know all of the reasons for the appearance of
QSharedPointer
in the generated code, but I would like to suggest an alternative that I think will make usingqtprotobuf
much more pleasurable (in the long term) even though it would break current interfaces (you haven't reached release 1.0 yet, right??).The alternative that I would like to propose makes the generated classes look and feel a lot more like standard Qt containers, preserving the "copy-on-write" behavior they exhibit, and reducing the verbosity of
qtprotobuf
usage.Basically, the idea is to use
QSharedDataPointer
https://doc.qt.io/qt-5/qshareddatapointer.html as the only data member of a "methods class", and keep all of the fields in a "data class".I have used this technique many times in the past to fashion efficient data container classes.
I suspect that (as a Qt user) you are already familiar with this programming pattern and I also suspect that the classes generated from proto messages inherit from
QObject
only in order to supportNOTIFY
in the properties.If you're unwilling to consider removing the
NOTIFY
then you can probably stop reading now. However, I would like to point out that, as data containers, these messages could be considered to be more like basic Qt (and QML) types thanQObject
s orQQuickItem
s.That is, QString doesn't have signals, like
valueChanged()
, nor does a qml string.In order to illustrate how this might be done, I've hacked the generated files produced by the
addressbook.proto
example and gotten it to compile. I suggest you diff them against those generated by the code in the master branch today.new_addressbook.zip
There are surely a lot of details involved in the inner workings of serialization and deserialization that I don't understand or appreciate, so I'm not going to stop using QtProtobuf if you reject this idea. However, it seems to me that transitioning to this syntax would result in a more seamless experience as a user.
The text was updated successfully, but these errors were encountered: