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

AK: Make Variant constexpr #25025

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

Hendiadyoin1
Copy link
Contributor

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

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.
@github-actions github-actions bot added the 👀 pr-needs-review PR needs review from a maintainer or community member label Sep 16, 2024
Copy link
Member

@alimpfard alimpfard left a 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 Show resolved Hide resolved
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)
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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.

AK/Variant.h Outdated Show resolved Hide resolved
Hendiadyoin1 and others added 2 commits September 16, 2024 14:48
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]>
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)...);
Copy link
Contributor

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.

Copy link
Contributor Author

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)

Copy link
Contributor Author

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> && ...)) {};
Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

@DanShaders DanShaders Sep 27, 2024

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 declared private, protected, or noexcept(false), and in C++20 can be constrained (but not consteval). Very marginally, it can be declared constexpr to verify that it would be anyway. Declaring it inline 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;
Copy link
Contributor

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
Comment on lines 51 to 55
VariantStorage(VariantStorage&&) = default;
VariantStorage(VariantStorage const&) = default;

VariantStorage& operator=(VariantStorage&&) = default;
VariantStorage& operator=(VariantStorage const&) = default;
Copy link
Contributor

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?

Copy link
Contributor Author

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()

Copy link
Contributor Author

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

@Hendiadyoin1
Copy link
Contributor Author

Seems like I only ever built lagom with this, so it seems like I missed some cases not present there

Comment on lines 604 to 606
// 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.
Copy link
Contributor Author

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));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

should I replace this with an "explicit Object parameter"1 member function on the helper?

Footnotes

  1. aka deducing this

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)...);
Copy link
Contributor Author

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)...);
Copy link
Contributor Author

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;
Copy link
Contributor Author

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
Comment on lines 51 to 55
VariantStorage(VariantStorage&&) = default;
VariantStorage(VariantStorage const&) = default;

VariantStorage& operator=(VariantStorage&&) = default;
VariantStorage& operator=(VariantStorage const&) = default;
Copy link
Contributor Author

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
Comment on lines 51 to 55
VariantStorage(VariantStorage&&) = default;
VariantStorage(VariantStorage const&) = default;

VariantStorage& operator=(VariantStorage&&) = default;
VariantStorage& operator=(VariantStorage const&) = default;
Copy link
Contributor Author

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

@Hendiadyoin1 Hendiadyoin1 marked this pull request as draft September 22, 2024 21:11
@github-actions github-actions bot removed the 👀 pr-needs-review PR needs review from a maintainer or community member label Sep 22, 2024
Copy link

stale bot commented Nov 5, 2024

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!

@stale stale bot added the stale label Nov 5, 2024
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.

4 participants