Skip to content
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

Refactor(parented_ptr): make trivially destructible in release mode, delete move operations #11981

Merged
merged 3 commits into from Sep 17, 2023

Conversation

Swiftb0y
Copy link
Member

not ready yet, just did this for fun and an experiment. wdyt about this? It's obviously not super necessary but it may take of even more burden of a reviewer when they see a make_parented, knowing that it will only work if the object is actually capable of participating in the Object Tree memory management and not just taking a QObject by chance.
Thoughts and reviews welcome even though this is just a draft.

@daschuer
Copy link
Member

I did not understand the std::is_invocable_r_v constrains.

@Swiftb0y
Copy link
Member Author

They currently don't work either, but the idea was to confirm that there exists a constructor for the given args. Ideally those args could also be checked to contain at least one QObject (the parent argument), but I didn't figure out how to check that in a reasonable and performant fashion. I wanted to avoid the case where people would use make_parented on a class that supports it, but then don't actually pass a parent object, which would create a leak.

@daschuer
Copy link
Member

The parented pointer already checks if the object has a parent. Constructing the object without a parent already fails.

@Swiftb0y
Copy link
Member Author

yes, it does in a debug assert, but for one, thats based on a ducktyping which might result in false negatives (can be solved via static_assert with std::derived_from) and one the other hand it's at runtime even though a large subset of cases could be detected at compile time. Also, the runtime assertion in the destructor also doesn't protect against the problem when qobjects are destructed out-of-order.

@daschuer
Copy link
Member

Do you want to replace the runtime assertion?

@Swiftb0y
Copy link
Member Author

No the runtime assertion is fine (mostly, commit is in the works). But the runtime assertion only happens in debug builds on shutdown and is probably only noticed with —DDEBUG_ASSERTIONS_FATAL. I want to turn it into a compile time assertion so its easier to spot.

@Swiftb0y Swiftb0y marked this pull request as ready for review September 14, 2023 16:04
@Swiftb0y Swiftb0y changed the title Refactor/Experiment: constrain make_parented to types capable of using the Qt Object Tree Refactor/Experiment: constrain make_parented to types derived from QObjects; misc improvements. Sep 14, 2023
@daschuer
Copy link
Member

How to detect that the parent pointer is not null at compile time? A not_null pointer wrapped does not help, because it is also a runtime check. Maybe with link time assets #4139 or https://github.com/daschuer/mixxx/tree/not_null_link_assert

@Swiftb0y
Copy link
Member Author

How to detect that the parent pointer is not null at compile time?

you can't reliably. Again, I don't want to remove the runtime time check, I just want to catch a subset of cases earlier.

I have reservations against link-time asserts so I don't want to go down that route now.

@Swiftb0y Swiftb0y force-pushed the refactor/parented_ptr branch 3 times, most recently from 2f016ce to 4d2974a Compare September 14, 2023 17:49
@Swiftb0y
Copy link
Member Author

got sick of all the compile errors on incomplete compilers. I'll try again in a year or two when we hopefully have clang 15 on macos...

So this PR just degenerated to the "misc improvements" part of the title...

@Swiftb0y Swiftb0y changed the title Refactor/Experiment: constrain make_parented to types derived from QObjects; misc improvements. Refactor(parented_ptr): make trivially destructible in release mode, delete move operations Sep 14, 2023
@github-actions github-actions bot added the ui label Sep 14, 2023
@Swiftb0y
Copy link
Member Author

friendly ping, requesting review.

#else
// explicitly generate trivial destructor (since decltype(m_ptr) is not a class type)
~parented_ptr() noexcept = default;
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for my understanding '~parented_ptr() = default' and ~parented_ptr() {} is the same. So I can imagine that this is not needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No its not equivalent. One if an empty definition which means that its still user provided even if it does nothing, the other one is the default declared one which does not count as user-provided.

See
https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor
https://compiler-explorer.com/z/cbG99jbzr

Comment on lines 40 to +43
parented_ptr(const parented_ptr<T>&) = delete;
parented_ptr& operator=(const parented_ptr<T>&) = delete;
parented_ptr(const parented_ptr<T>&& other) = delete;
parented_ptr& operator=(const parented_ptr<T>&& other) = delete;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never understood why it should be not lowed to copy or move a parented_ptr()
A copy of the parented pointer can be safely moved around since it is not owning the pointer. It is just telling the programmer: "This is a borrowed pointer owned by the parent object".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, It makes sense to have only one instance checking the ownership. The deleted copy constructor enforces that.
Unfortunately it prevents also to copy the object that contains it.
A move will move the responsibility for checking the parent along with the pointer. There is nothing wrong with it. Is it?
What is your opinion here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really check to be honest. I just saw that we didn't follow the rule of five and thus I chose the safe default. We can lift the restrictions if we can be sure that its safe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reservations about moving. If we move an object that is owned by its parent, its only safe if the parent knows that it has been moved, do QObjects take care of telling their parent?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is probably OK, we can change it whenever we find a valid use case for it.

@daschuer
Copy link
Member

can be solved via static_assert with std::derived_from

this was a good idea IMHO, why did you discard it?

@Swiftb0y
Copy link
Member Author

Because its not supported on the outdated apple-clang we're stuck with and working around that resulted in too much of a hassle and IIRC also resulted in false positives (?)

Copy link
Member

@daschuer daschuer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thank you.

@daschuer daschuer merged commit 658cb7c into mixxxdj:main Sep 17, 2023
13 checks passed
@Swiftb0y
Copy link
Member Author

Thank you

@Swiftb0y Swiftb0y deleted the refactor/parented_ptr branch September 17, 2023 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants