Skip to content

Conversation

@miscco
Copy link
Contributor

@miscco miscco commented May 13, 2020

This moves the helper structs _Equal_memcmp_is_safe and _Equal_memcmp_is_safe_helper to use variable templates rather than structs.

This is a precursor to #819

@miscco miscco requested a review from a team as a code owner May 13, 2020 19:07
@CaseyCarter CaseyCarter added the enhancement Something can be improved label May 13, 2020
@miscco miscco force-pushed the equal_memcmp branch 6 times, most recently from 6ff69a1 to 1ab5481 Compare May 14, 2020 10:34
@miscco
Copy link
Contributor Author

miscco commented May 14, 2020

Just one more round is great for civilization bot not good for PRs. This should now be ready for initial review.

stl/inc/xutility Outdated
return _Equal_unchecked(_UFirst1, _ULast1, _UFirst2, _Pass_fn(_Pred));
}
#endif // _HAS_IF_CONSTEXPR
#endif // !_HAS_IF_CONSTEXPR
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this what we typically do? If you look at the example just below (line 4793-4871 before changes), the #endif is commented with the original condition (not negated). Doing a quick search throughout the code base also shows that the original comment follows our convention.

Copy link
Contributor Author

@miscco miscco May 14, 2020

Choose a reason for hiding this comment

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

As far as I know there was a discussion early after open sourcing and I have the memory that the majority was in favor of

#if foo
#else // ^^^ foo / !foo vvv
#endif // !foo

The reason was that for longer clauses such as this just reading #endif // foo would lead you to believe that foo is active or require you to check the start of the block.

That said sleep was rather short this year so I doubt any memory I have

Copy link
Member

Choose a reason for hiding this comment

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

This is #351 - existing code has accumulated inconsistent patterns. I'm not sure how we ended up with the "hybrid arrow" examples that @mnatsuhara pointed out - I think they evolved from our classic pattern of

#if _HAS_CATS
#else // _HAS_CATS
#endif // _HAS_CATS

#ifdef __cpp_lib_kittens
#else // __cpp_lib_kittens
#endif // __cpp_lib_kittens

by replacing just the middle portion with arrows.

My personal opinion: Aside from the question of when comments can be omitted, I'd prefer for us to standardize on consistent arrow comments:

#if _HAS_CATS
#else // ^^^ _HAS_CATS ^^^ / vvv !_HAS_CATS vvv
#endif // ^^^ !_HAS_CATS ^^^

#ifdef __cpp_lib_kittens
#else // ^^^ defined(__cpp_lib_kittens) ^^^ / vvv !defined(__cpp_lib_kittens) vvv
#endif // ^^^ !defined(__cpp_lib_kittens) ^^^

(I'm not very concerned with whether ^^^ / vvv or just / appears in the middle; I like the former slightly more.) I prefer this because it's the clearest in dense regions of preprocessor logic, and it's also unambiguous about whether the final comment should say _HAS_CATS or !_HAS_CATS (because it points to a specific section of code).

I could see an argument that "of course an #endif closes a section of code, so we just need to consistently identify what we closed", which would argue for !_HAS_CATS here.

Somewhat related: I believe we've accumulated some inconsistency about how to refer to #ifdef (and #if defined(MEOW) etc.) conditions in comments - I have seen some comments switch from the defined(MEOW) / !defined(MEOW) domain to the MEOW / !MEOW domain which I find confusing. This is because of the danger of actually confusing the definedness versus valueness domains in the test (and the compiler warning about #if NOT_DEFINED being treated as #if 0).

There are "only" 444 occurrences of #else in stl/inc, but 2049 #endifs - tedious to audit, but still easier than bracing all control flow, which we finally did and it was awesome. I should probably just submit a PR to prevent this from burning up valuable time in the future. 🔥

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 have to agree that we should be consistent. As I had to rebase anyway I removed that change so that it can be tackled with all the other comments in a consistent fashion

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe this would be a "good second issue"

@miscco
Copy link
Contributor Author

miscco commented May 20, 2020

I am still kind of stuck here. My problem with the top level const is that I cannot really constrain the _Equal_memcmp_is_safe because if I use enable_if_t as an additional template argument, it complains about the invalid specialization. If I use enable_if_t on one of the arguments of the specialization it errors out because one of the template argument would be unused in the SNFINAE case.

Given that this is essentially soon to be reworked code I am actually considering just hand rolling the 4 combinations of _Elem* and _Elem* const and be done with.

@StephanTLavavej
Copy link
Member

Ah, I see - it's a partial specialization of a variable template, so you can't really interfere with the partial-specialization-ness.

What about the following? (I can prepare a fully worked example later if you want.) Give the primary template a default template argument of the form bool = conjunction_v<is_pointer<ITER1>, is_pointer<ITER2>> and make that the "true" version, which then uses remove_pointer_t to remove any top-level const, followed by the pointer, and then deals with the (possibly const) element type. Then, provide a partial specialization for <ITER1, ITER2, false> that just answers false. (This reverses the current pattern, because of the stylistic preference of having the primary template be used for a true bool.)

@miscco
Copy link
Contributor Author

miscco commented May 20, 2020

@StephanTLavavej thanks a lot that is exactly what I needed 😲

Copy link
Contributor Author

@miscco miscco left a comment

Choose a reason for hiding this comment

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

I again managed to screw the push up, the review comments should now all be satisfied and this ready for final review

@miscco
Copy link
Contributor Author

miscco commented May 23, 2020

@CaseyCarter this should now close #819

@CaseyCarter
Copy link
Contributor

@CaseyCarter this should now close #819

Thanks for the reminder; I've attached #819 and tagged it as "work in progress."

@miscco
Copy link
Contributor Author

miscco commented May 28, 2020

@StephanTLavavej Thanks for improving the comments

@StephanTLavavej
Copy link
Member

I am working on overhauling the metaprogramming - I believe I verified the correctness of your additions yesterday, but the original design was messy to extend and it was exceeding my ability to easily reason about. I think I have something simpler that preserves your additions and enhancements, and avoids replicating too much code for concepts and non-concepts. Will add comments, test, and push soon.

@miscco
Copy link
Contributor Author

miscco commented May 29, 2020

Given that you had trouble we can estimate hoe long I struggled to get something working.

Thanks a lot for improving it further

Copy link
Contributor Author

@miscco miscco left a comment

Choose a reason for hiding this comment

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

That is indeed so much better. Thanks a lot

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.

I'm fixing the test coverage.

Include `<cstddef>` for `byte`. Test it when it's available.

Style: Use `is_unsigned_v`.

Add more test cases for mixed `int` and `long`.

Remove a duplicated line (the cv pattern is clear).
@StephanTLavavej StephanTLavavej self-assigned this May 29, 2020
@StephanTLavavej
Copy link
Member

I'm porting this to MSVC and will need to update that internal PR if any more changes are pushed here.

@StephanTLavavej StephanTLavavej merged commit afaac16 into microsoft:master May 30, 2020
@StephanTLavavej
Copy link
Member

Thanks for enhancing this optimization! Now, users of C++20 ranges will get the same codegen as for classic algorithms. 😸

@miscco
Copy link
Contributor Author

miscco commented May 30, 2020

I would also like to thank me for pushing you to write awesome code

@miscco miscco deleted the equal_memcmp branch June 3, 2020 13:29
@cpplearner
Copy link
Contributor

This seems to cause DevCom-1263877 (std::equal does not work with custom contiguous iterators).

#1433 contains a fix, but a smaller PR might be preferable.

@AdamBucior
Copy link
Contributor

I don't think smaller PR would be preferable, #1433 is not huge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Something can be improved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

<xutility>: Teach _Equal_memcmp_is_safe about contiguous iterators and ranges::equal_to

6 participants