Page MenuHomePhabricator

[libc++] Add the __bind_back and __compose helpers

Authored by ldionne on Aug 9 2021, 3:02 PM.



Those are going to be used to implement range adaptors,
see D107098 for details.

Diff Detail

Event Timeline

ldionne created this revision.Aug 9 2021, 3:02 PM
ldionne requested review of this revision.Aug 9 2021, 3:02 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 9 2021, 3:02 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
Quuxplusone requested changes to this revision.Aug 9 2021, 5:13 PM
Quuxplusone added a subscriber: Quuxplusone.

Marking "request changes" so it shows up correctly in queues; but there's nothing big or urgent among my comments. Just consider enabling in C++14 mode, and (almost certainly IMHO) include the new files in <functional>.


Could this be > 11?
Could it be > 11 if you used _VSTD::__invoke instead of _VSTD::invoke?
(Besides just feeling nicer, keeping our dependencies minimal might one day help us clean up old code faster. As soon as we drop support for C++11, the whole #if goes away. Whereas if you make it depend on C++20, then we have to keep the #if in place for another 9 years until we drop support for C++17.)


Same here: could this be > 11?


If you do enable __bind_back back to C++14, then this line will need to change and you'll need to use more ::value and less _v in this test.


What's the ruling on whether <foo> should include <__foo/internal_detail.h>? I know when we were originally debating the micro-headers, the idea was floated (but not necessarily agreed upon or even remarked upon) that <foo> should eventually be a simple mechanical listing of all <__foo/*.h>, i.e., if any <__foo/internal_detail.h> were missing from <foo>, we'd call that a bug.
This also applies to D107584: Should <concepts> include <__concepts/boolean_testable.h>? Should it include <__concepts/class_or_enum.h>? Right now I've answered "yes" to both, following what seems like precedent:

  • <algorithm> includes <__algorithm/comp_ref_type.h> and <__algorithm/half_positive.h>;
  • <memory> includes <__memory/compressed_pair.h>;
  • <iterator> includes <__iterator/wrap_iter.h>.

If this is enough precedent for you, I think we should adopt it as official (unwritten) policy and this TODO should just get DONE. :)


Nit: You could flip these around:

auto ret = std::__bind_back(std::move(value), 1);
using RetT = decltype(ret);
static_assert( std::is_move_constructible<RetT>::value);  // etc.

Nit: Lines 386–390 are about whether the unspecified type's operator() is SFINAE-friendly.
Lines 394–406 are about whether the std::__bind_back function itself is SFINAE-friendly.
Tests for both are needed and good, of course; the comment is just a little icky to conflate them.


Maybe worth testing some ref-qualified member functions too?
I notice we're not testing any of the perfect-forwarding-call-wrapper stuff here; e.g. we're never checking whether std::move(c) is invocable; nor whether it's not invocable when F2 is not move-invocable; etc. etc. That lack may be intentional, given that we have older tests for perfect-forwarding-call-wrapper stuff elsewhere in the test suite.

This revision now requires changes to proceed.Aug 9 2021, 5:13 PM
ldionne updated this revision to Diff 365516.Aug 10 2021, 9:42 AM
ldionne marked 5 inline comments as done.

Address review comments except bumping the required Standard.


I would agree because of the "dropping #ifs faster" story, but I think the benefit of using standard facilities (i.e. invoke instead of __invoke) outweighs that. WDYT?


There isn't a huge benefit in doing this, since if Barry's paper gets in, bind_back would be enabled in C++20 (or 23) only. I'd basically rename it from __bind_back to bind_back and expose it publicly.


Sold, let's mechanically include everything. I will still include __functional/compose.h in the compose.pass.cpp test, since there's no plan to make it part of <functional> in the spec. I'll still include it in <functional> just for consistency, but I think it's better to include the minimal header if we're testing a pure implementation detail.


Yes, it is intentional. I think I should actually put it in a test that is specific to __perfect_forward, but I'm on the fence because I don't want to have the tests in three places once bind_back makes it to the standard (bind_front, bind_back and __perfect_forward).

Quuxplusone added inline comments.

I see cost-but-no-benefit to using _VSTD::invoke over _VSTD::__invoke. We have __invoke precisely so that we can use it in places where invoke wouldn't have been available until C++17. Also invoke is heavier-weight than __invoke; we'd rather use __invoke than invoke throughout the library.


I doubt bind_back would be "DR'ed" back to C++20 at this point — but that doesn't stop libc++ from supporting it in C++20 anyway. Leaving __bind_back untested in '14 and '17 SGTM.

ldionne updated this revision to Diff 365620.Aug 10 2021, 3:48 PM

Try to fix the modules build


__perfect_forward uses is_invocable, the wording for bind_front uses std::invoke, and so does the wording for bind_back in the paper. It just feels better to use something that everybody knows the semantics of at first glance (std::invoke) over something that might or might not be exactly what you'd expect (you can't know without looking). I think that's the primary reason why I find it easier to understand what's going on when std::invoke is used over __invoke. IMO, we should strive to use standard APIs when we can and when it makes sense to do that, instead of using internal variants of those.

zoecarver accepted this revision as: zoecarver.Aug 10 2021, 4:38 PM
zoecarver added a subscriber: zoecarver.

LGTM. Thanks!

ldionne accepted this revision as: Restricted Project.Aug 11 2021, 7:08 AM
This revision is now accepted and ready to land.Aug 11 2021, 7:08 AM
This revision was automatically updated to reflect the committed changes.