-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
AK: Make Variant constexpr #25025
base: master
Are you sure you want to change the base?
AK: Make Variant constexpr #25025
Conversation
construct_at has to be defined in the std namespace because outside of it, compilers wouldn't allow it to be constexpr because of its use of placement new.
5b09e4e
to
1f51879
Compare
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.
Just a very cursory look for now, will take a look at the template-y stuff in a bit.
AK/Variant.h
Outdated
static constexpr auto current_index = VariantIndexOf<F, IndexType, InitialIndex, F, Ts...> {}(); | ||
ALWAYS_INLINE static void delete_(IndexType id, void* data) | ||
|
||
constexpr static void delete_(IndexType state, VariantStorage<F, Ts...>& storage) |
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.
with all this de-inlining, please present perf change reports.
Any benchmark should do, variant is sufficiently integrated I think.
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.
Sure, will put ALWAYS_INLINE back, although I think that that is implied by constexpr
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.
inline
is implied (on functions), the attribute is not.
This will allow us to make it `constexpr` in the next commits Co-Authored-By: Ali Mohammad Pur <[email protected]>
Co-Authored-By: kleines Filmröllchen <[email protected]>
1f51879
to
7faff15
Compare
template<typename T, typename... Args> | ||
constexpr T* construct_at(T* p, Args&&... args) | ||
{ | ||
return new (const_cast<void*>(static_cast<void const*>(p))) T(forward<Args>(args)...); |
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.
But if AK_REPLACED_STD_NAMESPACE
is AK::replaced_std
, this won't be in std namespace.
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.
makes sense,
I think I can get around that by always using
the std::
version in AK?
(I hate this function, and cant wait for the c++26 being officially implemented everywhere)
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.
Also CC @nrdmn, as this affects your PR/commit
AK/Variant.h
Outdated
// Note: Actual destruction is handled by the Variant itself | ||
~VariantStorage() = default; | ||
~VariantStorage() | ||
requires(!IsTriviallyDestructible<T> || !(IsTriviallyDestructible<Rest> && ...)) {}; |
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.
Nit: maybe do (!IsTriviallyDestructible<Rest> || ...)
(as it is done above) to not needlessly confuse potential readers. (This definitely confused me for a few seconds)
AK/Variant.h
Outdated
VariantStorage& operator=(VariantStorage const&) = default; | ||
|
||
// Note: Actual destruction is handled by the Variant itself | ||
~VariantStorage() = default; |
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 don't immediately see a reason to have a default overload that will be empty for trivially destructible types and an overload for the case of non-trivially destructible types that is explicitly empty but I suppose there's one, so a comment will be nice.
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.
IIRC think =default
means something else than {}
,
But sure I can add a comment explaining how and when a member is supposed to be constructed/destructed
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.
From https://stackoverflow.com/questions/56799129/when-to-make-a-destructor-defaulted-using-default:
Versus
Implicit,
an explicitly defaulted destructor suppresses move operations, can be declaredprivate
,protected
, ornoexcept(false)
, and in C++20 can be constrained (but notconsteval
). Very marginally, it can be declaredconstexpr
to verify that it would be anyway. Declaring itinline
doesn’t do anything. (It can also be out of line or virtual, but as stated above that can’t be a reason to use it.)
So there should be no reason for us to declare a defaulted destructor here? (Unless, of course, it doesn't compile without it somehow :))
AK/Variant.h
Outdated
|
||
template<typename T, typename... Rest> | ||
union VariantStorage<T, Rest...> { | ||
VariantStorage() = default; |
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.
See the comment about destructor.
AK/Variant.h
Outdated
VariantStorage(VariantStorage&&) = default; | ||
VariantStorage(VariantStorage const&) = default; | ||
|
||
VariantStorage& operator=(VariantStorage&&) = default; | ||
VariantStorage& operator=(VariantStorage const&) = default; |
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.
Won't those be invalid for sufficiently non-trivial types?
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.
Those should only get initialized -and there for checked- iff the default comparison operator on Variant proper is used, the non trivial case uses a visit
and get_pointer()
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.
Ah, sorry I can't read, thought this was operator==
, will check again
Seems like I only ever built lagom with this, so it seems like I missed some cases not present there |
// Note: Make sure not to default-initialize! | ||
// VariantConstructors::VariantConstructors(T) will set this to the correct value | ||
// So default-constructing to anything will leave the first initialization with that value instead of the correct one. |
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 think I can remove this comment now...
{ | ||
// This constructor is delayed, so that m_storage and m_index are "live" once | ||
// this accesses them. | ||
Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...>(*this, forward<T>(value)); |
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.
template<typename T, typename... Args> | ||
constexpr T* construct_at(T* p, Args&&... args) | ||
{ | ||
return new (const_cast<void*>(static_cast<void const*>(p))) T(forward<Args>(args)...); |
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.
makes sense,
I think I can get around that by always using
the std::
version in AK?
(I hate this function, and cant wait for the c++26 being officially implemented everywhere)
template<typename T, typename... Args> | ||
constexpr T* construct_at(T* p, Args&&... args) | ||
{ | ||
return new (const_cast<void*>(static_cast<void const*>(p))) T(forward<Args>(args)...); |
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.
Also CC @nrdmn, as this affects your PR/commit
AK/Variant.h
Outdated
VariantStorage& operator=(VariantStorage const&) = default; | ||
|
||
// Note: Actual destruction is handled by the Variant itself | ||
~VariantStorage() = default; |
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.
IIRC think =default
means something else than {}
,
But sure I can add a comment explaining how and when a member is supposed to be constructed/destructed
AK/Variant.h
Outdated
VariantStorage(VariantStorage&&) = default; | ||
VariantStorage(VariantStorage const&) = default; | ||
|
||
VariantStorage& operator=(VariantStorage&&) = default; | ||
VariantStorage& operator=(VariantStorage const&) = default; |
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.
Those should only get initialized -and there for checked- iff the default comparison operator on Variant proper is used, the non trivial case uses a visit
and get_pointer()
AK/Variant.h
Outdated
VariantStorage(VariantStorage&&) = default; | ||
VariantStorage(VariantStorage const&) = default; | ||
|
||
VariantStorage& operator=(VariantStorage&&) = default; | ||
VariantStorage& operator=(VariantStorage const&) = default; |
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.
Ah, sorry I can't read, thought this was operator==
, will check again
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! |
CC: @alimpfard
I am sure the template magic needs cleanup, so roast the code for me
CC: @kleinesfilmroellchen
your small little test works on my end