-
Notifications
You must be signed in to change notification settings - Fork 29.1k
Define a concept for UniqueObjectTraits. #174905
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
base: master
Are you sure you want to change the base?
Conversation
It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging. If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group. |
There are other instances where concepts would have made compiler errors easier to reason about. But this one was by far my biggest bugbear and one I had run into numerous times. |
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.
Code Review
This pull request introduces a C++20 concept, UniqueObjectTraits
, to enforce the requirements for traits classes used with fml::UniqueObject
. This is a great improvement as it will provide clearer compiler errors when the traits are not implemented correctly, as described in the pull request. The change in embedder_config_builder.h
is a good fix that aligns with the new concept.
I have one suggestion to make the new concept even more robust by removing an implicit DefaultConstructible
constraint on the object type T
.
concept UniqueObjectTraits = requires { | ||
// |InvalidValue| should be fast and inline. | ||
{ Traits::InvalidValue() } -> std::same_as<T>; | ||
|
||
// |IsValid| function should be fast and inline. | ||
{ Traits::IsValid(T{}) } -> std::same_as<bool>; | ||
|
||
// |Free| function will not be called if value == InvalidValue()! | ||
{ Traits::Free(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 use of T{}
in the requires
expression for IsValid
and Free
introduces a requirement that T
must be default-constructible. This might be too restrictive for a generic utility like UniqueObject
, as some handle types T
might not have a default constructor.
To make the concept more general and avoid this constraint, you can use std::declval<T>()
. This allows checking the validity of the expressions without needing to construct an object of type T
.
This change maintains the other constraints of the concept (like preventing Free
from taking a non-const lvalue reference) while making it applicable to a wider range of types for T
.
concept UniqueObjectTraits = requires { | |
// |InvalidValue| should be fast and inline. | |
{ Traits::InvalidValue() } -> std::same_as<T>; | |
// |IsValid| function should be fast and inline. | |
{ Traits::IsValid(T{}) } -> std::same_as<bool>; | |
// |Free| function will not be called if value == InvalidValue()! | |
{ Traits::Free(T{}) }; | |
}; | |
concept UniqueObjectTraits = requires { | |
// |InvalidValue| should be fast and inline. | |
{ Traits::InvalidValue() } -> std::same_as<T>; | |
// |IsValid| function should be fast and inline. | |
{ Traits::IsValid(std::declval<T>()) } -> std::same_as<bool>; | |
// |Free| function will not be called if value == InvalidValue()! | |
{ Traits::Free(std::declval<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.
good bot gem gem
@@ -18,7 +18,7 @@ struct UniqueEngineTraits { | |||
|
|||
static bool IsValid(const FlutterEngine& value) { return value != nullptr; } | |||
|
|||
static void Free(FlutterEngine& engine) { |
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.
FlutterEngine is a pointer. This one was creating a reference to a pointer. The concept makes this check stricter. Though I am sure I could use std::convertible_to instead of std::same_as. But this one is an error so decided to keep the stricter version and fixup this callsite.
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 same would be true of the IsValid method above this.
Why is that type defined as a pointer? Shouldn't we define the base type and make the pointers explicit at the point of use?
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.
Hmm, you're right. IsValid
could be static bool IsValid(FlutterEngine value)
just fine. So why is Free
an issue.
From my investigation, FlutterEngine&
would make try to bind a non-const lvalue reference to a temporary of type pointer. According to the internet, this should be problematic. I am not sure why the current code works. It shouldn't right?
Instead of figuring out why the bad code is working, I'm going to vote for just fixing 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.
lgtm modulo gemini's suggestion to support non default constructible types.
Expect presub failures till #174891 lands. Will rebase on top. |
Earlier, it was just a comment. If you didn't do what the comment said, you'd get a mysterious compiler error when the template was instantiated at the point where the trait method was invoked. This would be extremely far away from where the template was instantiated and typically in `fml/unique_object.h`. Now, the exact reason and where a fix would go is printed in the compiler error. For instance, if I delete the Free method in `UniqueDirTraits`, I get (among other output): ``` no member named 'Free' in 'fml::internal::os_unix::UniqueDirTraits' ```
63e1f7e
to
ab3cce1
Compare
@@ -18,7 +18,7 @@ struct UniqueEngineTraits { | |||
|
|||
static bool IsValid(const FlutterEngine& value) { return value != nullptr; } | |||
|
|||
static void Free(FlutterEngine& engine) { |
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 same would be true of the IsValid method above this.
Why is that type defined as a pointer? Shouldn't we define the base type and make the pointers explicit at the point of use?
autosubmit label was removed for flutter/flutter/174905, because This PR has not met approval requirements for merging. Changes were requested by {flar}, please make the needed changes and resubmit this PR.
|
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.
I've identified my concern. I'll approve and leave it to you to figure out what/whether to do about it.
Earlier, it was just a comment. If you didn't do what the comment said, you'd get a mysterious compiler error when the template was instantiated at the point where the trait method was invoked. This would be extremely far away from where the template was instantiated and typically in
fml/unique_object.h
.Now, the exact reason and where a fix would go is printed in the compiler error. For instance, if I delete the Free method in
UniqueDirTraits
, I get (among other output):