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

Implement Smart Pointer Creation With Default Initialization #1315

Merged
merged 25 commits into from
Oct 17, 2020

Conversation

AdamBucior
Copy link
Contributor

Resolves #48. Based on #778.

stl/inc/memory Outdated Show resolved Hide resolved
stl/inc/memory Outdated Show resolved Hide resolved
@CaseyCarter CaseyCarter added the cxx20 C++20 feature label Sep 24, 2020
@StephanTLavavej
Copy link
Member

I pushed a commit (deleting an extra newline) with the --author= option, so when we merge this PR, it will be displayed as co-authored by @Weheineman. Thanks for picking up this work!

@StephanTLavavej StephanTLavavej self-assigned this Sep 30, 2020
@StephanTLavavej
Copy link
Member

Status update: Planning to review this on Thursday, Oct 8. Apologies for the delay!

Copy link
Member

@StephanTLavavej StephanTLavavej left a comment

Choose a reason for hiding this comment

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

Thanks! This looks good, my main concern is with a potential corner case in overload resolution that should be avoidable with different metaprogramming. Please let me know if you'd like me to explain further, or I can revise the PR if you want - your preference.

stl/inc/memory Outdated Show resolved Hide resolved
stl/inc/memory Outdated Show resolved Hide resolved
stl/inc/memory Outdated Show resolved Hide resolved
stl/inc/memory Outdated Show resolved Hide resolved
stl/inc/memory Outdated Show resolved Hide resolved
@StephanTLavavej StephanTLavavej removed their assignment Oct 9, 2020
@StephanTLavavej
Copy link
Member

This looks great - thanks for the thorough and highly detailed work. (I noticed and appreciated the improvements like using remove_cvref_t on perfectly forwarded _Types to handle const lvalues, and unifying additional repetitive code. Combining 3 overloads into 1, for both the make and allocate families, is awesome.)

I pushed a merge with master and a small set of superficial cleanups, after verifying that the test is still passing:

  • When granting friendship to _Make_shared_unbounded_array and _Allocate_shared_unbounded_array, drop const on the value parameter size_t _Count. (We conventionally avoid top-level const on value parameters in function declarations, since it has no meaning to callers and is immediately ignored by the language. We are so picky about const! 😹)
  • Your _Make_shared_unbounded_array/_Allocate_shared_unbounded_array refactoring has a bonus: we no longer need to grant friendship to several make_shared etc. overloads.
  • When functions take const _Arg&, we don't need to apply remove_cvref_t to _Arg (unless we were worried about _Arg being passed as an explicit template argument, instead of being deduced, which is not a concern here).
  • Remove a leftover comment after the allocate_shared refactoring.
  • Use is_bounded_array_v in make_unique_for_overwrite for consistency.

I'll find a second maintainer to review and then we can merge this! 🚀

@StephanTLavavej StephanTLavavej removed their assignment Oct 16, 2020
Comment on lines +2382 to +2386
_Uninitialized_rev_destroying_backout _Backout{_Out};
for (size_t _Idx = 0; _Idx < _Size; ++_Idx) {
_Backout._Emplace_back_for_overwrite();
}
_Backout._Release();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we simplify this to

Suggested change
_Uninitialized_rev_destroying_backout _Backout{_Out};
for (size_t _Idx = 0; _Idx < _Size; ++_Idx) {
_Backout._Emplace_back_for_overwrite();
}
_Backout._Release();
_Uninitialized_default_construct_n(_Out, _Size);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no such function as _Uninitialized_default_construct_n and we can't use uninitialized_default_construct_n because it doesn't destroy elements in the reverse order.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm there is the ranges version (where I found a unneeded specialization that I forgot to remove so thanks ;))

Should we also destroy in backwards order there?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ranges::uninitialized_default_construct_n needs to support forward iterators and destroying in reverse order with them would be hard.

stl/inc/xutility Outdated Show resolved Hide resolved
stl/inc/xutility Outdated Show resolved Hide resolved
@@ -2351,6 +2369,25 @@ void _Uninitialized_value_construct_multidimensional_n(_Ty* const _Out, const si
}
}

template <class _Ty>
void _Uninitialized_default_construct_multidimensional_n(_Ty* const _Out, const size_t _Size) {
if constexpr (!is_trivially_default_constructible_v<_Ty>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

To be absolutely sure, even for the meow_overwrite we do not want to default initialize here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's the whole point of *_for_overwrite

Copy link
Contributor

Choose a reason for hiding this comment

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

For better or worse, there are many places in the STL where we assume that we can omit default-initialization of objects that are trivially default constructible. I'm not sure the resulting behavior is portably defined, but it has been working as intended on our implementation. We may need to make changes if and when compilers complain about this trick in constexpr, but that hasn't happened yet and in any case would need for more widespread changes than just this PR.

stl/inc/yvals_core.h Outdated Show resolved Hide resolved
@@ -2351,6 +2369,25 @@ void _Uninitialized_value_construct_multidimensional_n(_Ty* const _Out, const si
}
}

template <class _Ty>
void _Uninitialized_default_construct_multidimensional_n(_Ty* const _Out, const size_t _Size) {
if constexpr (!is_trivially_default_constructible_v<_Ty>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For better or worse, there are many places in the STL where we assume that we can omit default-initialization of objects that are trivially default constructible. I'm not sure the resulting behavior is portably defined, but it has been working as intended on our implementation. We may need to make changes if and when compilers complain about this trick in constexpr, but that hasn't happened yet and in any case would need for more widespread changes than just this PR.

@StephanTLavavej
Copy link
Member

Thanks - the latest changes look good. I was concerned about debug codegen impact from increased use of _Voidify_iter (as we would addressof twice in several cases), but it does centralize the obnoxious casts. So I believe a reasonable compromise is to add a simple throughput optimization, avoiding deref-and-addressof when the input is already a raw pointer, and I went ahead and pushed that. For precedent, we do the same thing in _Unfancy although without if constexpr:

STL/stl/inc/xstddef

Lines 281 to 290 in e7d5113

// FUNCTION TEMPLATE _Unfancy
template <class _Ptrty>
_NODISCARD constexpr auto _Unfancy(_Ptrty _Ptr) noexcept { // converts from a fancy pointer to a plain pointer
return _STD addressof(*_Ptr);
}
template <class _Ty>
_NODISCARD constexpr _Ty* _Unfancy(_Ty* _Ptr) noexcept { // do nothing for plain pointers
return _Ptr;
}

@StephanTLavavej StephanTLavavej self-assigned this Oct 17, 2020
@StephanTLavavej StephanTLavavej merged commit 1f83628 into microsoft:master Oct 17, 2020
@StephanTLavavej
Copy link
Member

Thanks again for implementing this feature, @AdamBucior and @Weheineman! This will help users achieve maximum performance with Standard smart pointers. 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cxx20 C++20 feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

P1020R1 Smart Pointer Creation With Default Initialization
6 participants