This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Implement P1425R4 (Iterator pair constructors for std::stack and std::queue)
ClosedPublic

Authored by philnik on Dec 17 2021, 3:29 PM.

Details

Diff Detail

Event Timeline

philnik created this revision.Dec 17 2021, 3:29 PM
philnik requested review of this revision.Dec 17 2021, 3:29 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 17 2021, 3:29 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
philnik updated this revision to Diff 395233.Dec 17 2021, 3:33 PM

Updated synopsis

philnik updated this revision to Diff 395242.Dec 17 2021, 4:10 PM
  • renamed shadowing variable
jloser added a subscriber: jloser.Dec 17 2021, 5:35 PM

Please consider adding tests for the deduction guides.

philnik updated this revision to Diff 395269.Dec 18 2021, 1:19 AM
  • Added deduction guide tests (I think)
Mordante requested changes to this revision.Dec 18 2021, 7:51 AM
Mordante added a subscriber: Mordante.

Thanks for your contribution! It still needs some more work.

libcxx/include/queue
77

This deduction guide has also been added. A quick scan it seems there are more deduction guides not added to the synopsis.

261

Can you move the new constructors to this location, that seems more consistent.

libcxx/test/std/containers/container.adaptors/queue/queue.cons.alloc/ctor_iterators.pass.cpp
42

I miss a test to validate the queue has the expected values.

44

The deduction guides should be their own test, queue.cons/deduct.pass.cpp already contains test for the deduction guides, please add them there.

libcxx/test/std/containers/container.adaptors/queue/queue.cons/ctor_iterators.pass.cpp
29

I think it would be good to test all elements in the queue to have the expected value.

libcxx/test/std/containers/container.adaptors/stack/stack.cons.alloc/ctor_iterators.pass.cpp
42

I miss a test to validate the queue has the expected values.

libcxx/test/std/containers/container.adaptors/stack/stack.cons/ctor_iterators.pass.cpp
28

I think it would be good to test all elements in the queue to have the expected value.

This revision now requires changes to proceed.Dec 18 2021, 7:51 AM
philnik updated this revision to Diff 395297.Dec 18 2021, 9:17 AM
philnik marked 7 inline comments as done.
  • Added deduction guides to synopsis
  • Moved queue constructors
  • Moved deduction guide tests
  • Checking elements
Quuxplusone requested changes to this revision.Dec 18 2021, 9:40 AM

Thanks, this looks good, except for the style not matching the style of the surrounding code.

libcxx/include/queue
55

Nits: please indent to match the surrounding style, and please last); // since C++23 (although I see you're aligning the comments, I don't think it's beneficial here).

Also, please shuffle the order of these new constructors to match what's in the paper and/or Standard synopsis. The (first, last) one should be circa line 44, and the (first, last, const Alloc&) one down here.

81–83

Here and line 79 also, please indent the second line by 2 spaces to match the surrounding style.
Consider line-breaking before deque just to improve readability.

libcxx/include/stack
188

Ditto below.

libcxx/test/std/containers/container.adaptors/queue/queue.cons.alloc/ctor_iterators.pass.cpp
31–32
libcxx/test/std/containers/container.adaptors/queue/queue.cons/ctor_iterators.pass.cpp
25

Testing that std::queue{it, it} produces a queue of ints rather than a queue of iterators is a good test, but it belongs in deduct.pass.cpp, not here. In this file, you should focus on the non-CTAD happy path, std::queue<int>(a.begin(), a.end()). Keep it simple:

const int a[] = {4, 3, 2, 1};
std::queue<int> queue(a, a+4);
assert(queue.front() == 4); // etc.
libcxx/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
159–164 ↗(On Diff #395297)

Let's use std::list<int> as our type here, since we do that above. Also, let's match the style of surrounding code.

{
    typedef short T;
    typedef test_allocator<T> Alloc;
    std::list<T> source;
    {
    std::queue que(source.begin(), source.end());
    static_assert(std::is_same_v<decltype(que), std::queue<T>>);
    }
    {
    std::queue que(source.begin(), source.end(), Alloc(2));
    static_assert(std::is_same_v<decltype(que), std::queue<T, std::deque<T, Alloc>>>);
    }
}
libcxx/test/std/containers/container.adaptors/stack/stack.cons.alloc/ctor_iterators.pass.cpp
42

I miss variable names that make it clear that the thing under test here is not a queue. ;)
(This test should be refactored simpler along the same lines I suggest for the queue test, above.)

libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
163–168 ↗(On Diff #395297)

Same comments here as on the queue deduct.pass.cpp.

This revision now requires changes to proceed.Dec 18 2021, 9:40 AM
libcxx/include/queue
390–397

Yipes, almost missed this, except that the failing CI alerted me. All these deduction guides need to use __enable_if_t to disable themselves when _InputIterator is not an iterator or _Alloc is not an allocator. Look at the guides for deque or priority_queue and do as they do.

Please also add some SFINAES_away tests to deduct.pass.cpp proving that the deduction guides indeed SFINAE away in those cases.

philnik updated this revision to Diff 395315.Dec 19 2021, 3:05 AM
philnik marked 9 inline comments as done.
  • Updated synopsis
  • Added SFINAE stuff
  • Style changes
Quuxplusone requested changes to this revision.Dec 19 2021, 8:25 AM
Quuxplusone added inline comments.
libcxx/include/queue
267

Does this overload need , __enable_if_t<uses_allocator<container_type, _Alloc>::value>* = 0 similar to the below overloads? I suspect it does. (And then please add a regression test. The regression test will use SFINAE but not CTAD.)

390–397

Please follow the pattern on lines 385–397 above (and in e.g. <vector>, which I assume is more or less where you copied these particular constraints from):

template<class _InputIterator,
         class = enable_if_t<__is_cpp17_input_iterator<_InputIterator>::value>
>
queue(_InputIterator, _InputIterator)
    -> queue<__iter_value_type<_InputIterator>>;

template<class _InputIterator,
         class _Alloc,
         class = enable_if_t<__is_cpp17_input_iterator<_InputIterator>::value>,
         class = enable_if_t<__is_allocator<_Alloc>::value>
>
queue(_InputIterator, _InputIterator, _Alloc)
    -> queue<__iter_value_type<_InputIterator>, deque<__iter_value_type<_InputIterator>, _Alloc>>;

Both for consistency, and because _LIBCPP_HAS_NO_CONCEPTS is still a thing.

libcxx/test/std/containers/container.adaptors/stack/stack.cons.alloc/ctor_iterators.pass.cpp
24

iter should now be just const int*, which means you don't need the typedef.

libcxx/test/std/containers/container.adaptors/stack/stack.cons/ctor_iterators.pass.cpp
28–31

This is okay, but personally I'd rather see lines 25,27,28,29,30 replaced with

assert(stack.top() == 1); stack.pop();
assert(stack.top() == 2); stack.pop();
assert(stack.top() == 3); stack.pop();
assert(stack.top() == 4); stack.pop();
assert(stack.empty());

and then you can remove the include of <ranges>. And similarly in the queue test:

assert(queue.front() == 4); queue.pop();
assert(queue.front() == 3); queue.pop();
assert(queue.front() == 2); queue.pop();
assert(queue.front() == 1); queue.pop();
assert(queue.empty());
libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
19 ↗(On Diff #395315)

This can be removed again now. Ditto in queue.cons/ctor_iterators.pass.cpp.

159 ↗(On Diff #395315)

Throw in <std::stack, NotIter, Iter> while you're here.
I like the renaming of BadAlloc to NotAlloc. I wouldn't complain if you replaced the uses of AllocAsCont with just Alloc, either (I don't think the AsCont improves my understanding of that case).

This revision now requires changes to proceed.Dec 19 2021, 8:25 AM
philnik updated this revision to Diff 396015.Dec 23 2021, 5:48 AM
philnik marked 5 inline comments as done.
  • Addressed comments
libcxx/include/queue
267

Actually even (hand-rolled) SFINAE is unnecessary. A regression test for this could be as simple as

static_assert(!std::is_constructible_v<std::queue<int>, int, int, std::allocator<int>>);
static_assert(!std::is_constructible_v<std::queue<int>, int*, int*, int>);
static_assert( std::is_constructible_v<std::queue<int, std::deque<int, test_allocator<int>>>, int, int, test_allocator<int>>);
static_assert(!std::is_constructible_v<std::queue<int, std::deque<int, test_allocator<int>>>, int, int, std::allocator<int>>);

and likewise for stack. (Preemptive whitespace advice: These lines are long, but I wouldn't break them.)

philnik updated this revision to Diff 396061.Dec 23 2021, 11:36 AM
  • Added regression tests and __enable_ifs to constructors
philnik marked 2 inline comments as done.Dec 30 2021, 3:48 AM

ping

Getting there! Definitely wait for buildkite to be back online and green before landing this, though, because I can totally imagine buildkite not liking it.

libcxx/include/queue
219

Why all of <memory>? (Same question below in stack.)

219

Here and <stack>, <version> should be included now.
(That is, please just make sure the merge-conflict with D116172 doesn't somehow get lost in the shuffle.)

265

Please linebreak before queue, for consistency. (Here and line 283, and presumably also in stack)

393
399–400
libcxx/include/stack
259–268

Please follow the enable_if_t pattern here as well.

philnik updated this revision to Diff 396860.Jan 1 2022, 7:26 AM
philnik marked 5 inline comments as done.
  • Addessed comments
libcxx/include/queue
219

I'll just wait for D116172 do be landed. It looks like it's just waiting for CI to pass.

philnik updated this revision to Diff 397717.Jan 5 2022, 2:32 PM
philnik marked an inline comment as done.

Rebased. Any more comments @Quuxplusone?

jloser added a comment.Jan 5 2022, 3:39 PM

I haven't finished reviewing this in full yet, but do we need to use enable_if_t instead of concepts/requires since this is C++23 code? I.e. is our requirement of using older AppleClang version in C++23 mode an issue with this (and therefore we must use enable_if_t still)? The consistency argument is fine - I'm asking purely from a technical standpoint.

I think it could also be guarded with _LIBCPP_HAS_NO_CONCEPTS. libc++ still supports compilers where concepts are not working.

Quuxplusone requested changes to this revision.Jan 5 2022, 4:13 PM

I haven't finished reviewing this in full yet, but do we need to use enable_if_t instead of concepts/requires since this is C++23 code? I.e. is our requirement of using older AppleClang version in C++23 mode an issue with this (and therefore we must use enable_if_t still)? The consistency argument is fine - I'm asking purely from a technical standpoint.

If you want it to be present in -std=c++2b mode, on old compilers without full concepts support, then it can't depend on full concepts support (although technically maybe those old compilers support enough of concepts that a simple requires-clause would be OK... but let's not go down that rabbit hole, let's just wait a couple more years). If you guard it under _LIBCPP_HAS_NO_CONCEPTS, then it won't be present on old compilers without full concepts support, so I don't think that would be a good idea either. Finally, you could provide two different implementations — one under _LIBCPP_HAS_NO_CONCEPTS and one not — but that would be twice the work, and require going down a rabbit hole involving "twice the tests," so let's not do that either.

libcxx/include/queue
82
219

stack still includes <memory>; it probably shouldn't, right?

271

This line shouldn't be here; and please add a regression test if possible.
An example of a regression test would be

int a[10] = {};
std::pmr::queue<int> q(a, a+10, std::pmr::new_delete_resource());  // should be OK

except that that'll be hard to put in a test since we don't support std::pmr. :P

The deduction guides are appropriately constrained so that (if they don't take a Container parameter) Alloc must be an allocator; but the constructors are not so constrained. https://wg21.link/p1518r1 is related.

libcxx/include/stack
195

and regression-test

This revision now requires changes to proceed.Jan 5 2022, 4:13 PM
philnik updated this revision to Diff 397840.Jan 6 2022, 3:37 AM
philnik marked 4 inline comments as done.
  • Addressed comments
libcxx/include/queue
219

Yep, forgot that.

Mordante accepted this revision as: Mordante.Jan 6 2022, 9:33 AM

I haven't finished reviewing this in full yet, but do we need to use enable_if_t instead of concepts/requires since this is C++23 code? I.e. is our requirement of using older AppleClang version in C++23 mode an issue with this (and therefore we must use enable_if_t still)? The consistency argument is fine - I'm asking purely from a technical standpoint.

If you want it to be present in -std=c++2b mode, on old compilers without full concepts support, then it can't depend on full concepts support (although technically maybe those old compilers support enough of concepts that a simple requires-clause would be OK... but let's not go down that rabbit hole, let's just wait a couple more years). If you guard it under _LIBCPP_HAS_NO_CONCEPTS, then it won't be present on old compilers without full concepts support, so I don't think that would be a good idea either. Finally, you could provide two different implementations — one under _LIBCPP_HAS_NO_CONCEPTS and one not — but that would be twice the work, and require going down a rabbit hole involving "twice the tests," so let's not do that either.

IIRC the only compiler using _LIBCPP_HAS_NO_CONCEPTS is AppleClang. Unfortunately our CI isn't running the latest AppleClang yet so we need to support not concept builds.

LGTM, just make sure the Apple build failure isn't due to your changes.

CI failed with the same problems in D116691, so I don't think they are related to this patch.

Quuxplusone accepted this revision.Jan 6 2022, 9:40 AM

The span.cons/initializer_list.pass.cpp failure is my fault, and I believe I just fixed it in 570ed38b6e387644c38561e0cb019c94d7b5c0bb. Feel free to poke CI again if you want, but I don't think it's necessary — I mean, if CI is "green except for span.cons/initializer_list.pass.cpp" on your most recent diff, then you can feel free to treat that as green.

This revision is now accepted and ready to land.Jan 6 2022, 9:40 AM
This revision was landed with ongoing or failed builds.Jan 6 2022, 9:56 AM
This revision was automatically updated to reflect the committed changes.