187cc95a9SLouis Dionne //===----------------------------------------------------------------------===//
287cc95a9SLouis Dionne //
387cc95a9SLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
487cc95a9SLouis Dionne // See https://llvm.org/LICENSE.txt for license information.
587cc95a9SLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
687cc95a9SLouis Dionne //
787cc95a9SLouis Dionne //===----------------------------------------------------------------------===//
887cc95a9SLouis Dionne
987cc95a9SLouis Dionne // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
1087cc95a9SLouis Dionne
1187cc95a9SLouis Dionne // <functional>
1287cc95a9SLouis Dionne
1387cc95a9SLouis Dionne // template<class R, class F, class... Args>
1487cc95a9SLouis Dionne // constexpr R invoke_r(F&& f, Args&&... args) // C++23
1587cc95a9SLouis Dionne // noexcept(is_nothrow_invocable_r_v<R, F, Args...>);
1687cc95a9SLouis Dionne
1787cc95a9SLouis Dionne #include <cassert>
1887cc95a9SLouis Dionne #include <concepts>
1987cc95a9SLouis Dionne #include <functional>
2087cc95a9SLouis Dionne #include <type_traits>
2187cc95a9SLouis Dionne #include <utility> // declval
2287cc95a9SLouis Dionne
2387cc95a9SLouis Dionne template <class R, class F, class ...Args>
2487cc95a9SLouis Dionne concept can_invoke_r = requires {
2587cc95a9SLouis Dionne { std::invoke_r<R>(std::declval<F>(), std::declval<Args>()...) } -> std::same_as<R>;
2687cc95a9SLouis Dionne };
2787cc95a9SLouis Dionne
test()2887cc95a9SLouis Dionne constexpr bool test() {
2987cc95a9SLouis Dionne // Make sure basic functionality works (i.e. we actually call the function and
3087cc95a9SLouis Dionne // return the right result).
3187cc95a9SLouis Dionne {
3287cc95a9SLouis Dionne auto f = [](int i) { return i + 3; };
3387cc95a9SLouis Dionne assert(std::invoke_r<int>(f, 4) == 7);
3487cc95a9SLouis Dionne }
3587cc95a9SLouis Dionne
3687cc95a9SLouis Dionne // Make sure invoke_r is SFINAE-friendly
3787cc95a9SLouis Dionne {
3887cc95a9SLouis Dionne auto f = [](int) -> char* { return nullptr; };
3987cc95a9SLouis Dionne static_assert( can_invoke_r<char*, decltype(f), int>);
4087cc95a9SLouis Dionne static_assert( can_invoke_r<void*, decltype(f), int>);
4187cc95a9SLouis Dionne static_assert( can_invoke_r<void, decltype(f), int>); // discard return type
4287cc95a9SLouis Dionne static_assert(!can_invoke_r<char*, decltype(f), void*>); // wrong argument type
4387cc95a9SLouis Dionne static_assert(!can_invoke_r<char*, decltype(f)>); // missing argument
4487cc95a9SLouis Dionne static_assert(!can_invoke_r<int*, decltype(f), int>); // incompatible return type
4587cc95a9SLouis Dionne static_assert(!can_invoke_r<void, decltype(f), void*>); // discard return type, invalid argument type
4687cc95a9SLouis Dionne }
4787cc95a9SLouis Dionne
4887cc95a9SLouis Dionne // Make sure invoke_r has the right noexcept specification
4987cc95a9SLouis Dionne {
5087cc95a9SLouis Dionne auto f = [](int) noexcept(true) -> char* { return nullptr; };
5187cc95a9SLouis Dionne auto g = [](int) noexcept(false) -> char* { return nullptr; };
5287cc95a9SLouis Dionne struct ConversionNotNoexcept {
5387cc95a9SLouis Dionne constexpr ConversionNotNoexcept(char*) noexcept(false) { }
5487cc95a9SLouis Dionne };
5587cc95a9SLouis Dionne static_assert( noexcept(std::invoke_r<char*>(f, 0)));
5687cc95a9SLouis Dionne static_assert(!noexcept(std::invoke_r<char*>(g, 0))); // function call is not noexcept
5787cc95a9SLouis Dionne static_assert(!noexcept(std::invoke_r<ConversionNotNoexcept>(f, 0))); // function call is noexcept, conversion isn't
5887cc95a9SLouis Dionne static_assert(!noexcept(std::invoke_r<ConversionNotNoexcept>(g, 0))); // function call and conversion are both not noexcept
5987cc95a9SLouis Dionne }
6087cc95a9SLouis Dionne
6187cc95a9SLouis Dionne // Make sure invoke_r works with cv-qualified void return type
6287cc95a9SLouis Dionne {
6387cc95a9SLouis Dionne auto check = []<class CV_Void> {
6487cc95a9SLouis Dionne bool was_called = false;
6587cc95a9SLouis Dionne auto f = [&](int) -> char* { was_called = true; return nullptr; };
6687cc95a9SLouis Dionne std::invoke_r<CV_Void>(f, 3);
6787cc95a9SLouis Dionne assert(was_called);
6887cc95a9SLouis Dionne static_assert(std::is_void_v<decltype(std::invoke_r<CV_Void>(f, 3))>);
6987cc95a9SLouis Dionne };
7087cc95a9SLouis Dionne check.template operator()<void>();
7187cc95a9SLouis Dionne check.template operator()<void const>();
7287cc95a9SLouis Dionne // volatile void is deprecated, so not testing it
7387cc95a9SLouis Dionne // const volatile void is deprecated, so not testing it
7487cc95a9SLouis Dionne }
7587cc95a9SLouis Dionne
7687cc95a9SLouis Dionne // Make sure invoke_r forwards its arguments
7787cc95a9SLouis Dionne {
7887cc95a9SLouis Dionne struct NonCopyable {
7987cc95a9SLouis Dionne NonCopyable() = default;
8087cc95a9SLouis Dionne NonCopyable(NonCopyable const&) = delete;
8187cc95a9SLouis Dionne NonCopyable(NonCopyable&&) = default;
8287cc95a9SLouis Dionne };
8387cc95a9SLouis Dionne // Forward argument, with void return
8487cc95a9SLouis Dionne {
8587cc95a9SLouis Dionne bool was_called = false;
8687cc95a9SLouis Dionne auto f = [&](NonCopyable) { was_called = true; };
8787cc95a9SLouis Dionne std::invoke_r<void>(f, NonCopyable());
8887cc95a9SLouis Dionne assert(was_called);
8987cc95a9SLouis Dionne }
9087cc95a9SLouis Dionne // Forward argument, with non-void return
9187cc95a9SLouis Dionne {
9287cc95a9SLouis Dionne bool was_called = false;
9387cc95a9SLouis Dionne auto f = [&](NonCopyable) -> int { was_called = true; return 0; };
94*346a2990SStephan T. Lavavej (void)std::invoke_r<int>(f, NonCopyable());
9587cc95a9SLouis Dionne assert(was_called);
9687cc95a9SLouis Dionne }
9787cc95a9SLouis Dionne // Forward function object, with void return
9887cc95a9SLouis Dionne {
9987cc95a9SLouis Dionne struct MoveOnlyVoidFunction {
10087cc95a9SLouis Dionne bool& was_called;
10187cc95a9SLouis Dionne constexpr void operator()() && { was_called = true; }
10287cc95a9SLouis Dionne };
10387cc95a9SLouis Dionne bool was_called = false;
10487cc95a9SLouis Dionne std::invoke_r<void>(MoveOnlyVoidFunction{was_called});
10587cc95a9SLouis Dionne assert(was_called);
10687cc95a9SLouis Dionne }
10787cc95a9SLouis Dionne // Forward function object, with non-void return
10887cc95a9SLouis Dionne {
10987cc95a9SLouis Dionne struct MoveOnlyIntFunction {
11087cc95a9SLouis Dionne bool& was_called;
11187cc95a9SLouis Dionne constexpr int operator()() && { was_called = true; return 0; }
11287cc95a9SLouis Dionne };
11387cc95a9SLouis Dionne bool was_called = false;
114*346a2990SStephan T. Lavavej (void)std::invoke_r<int>(MoveOnlyIntFunction{was_called});
11587cc95a9SLouis Dionne assert(was_called);
11687cc95a9SLouis Dionne }
11787cc95a9SLouis Dionne }
11887cc95a9SLouis Dionne
11987cc95a9SLouis Dionne // Make sure invoke_r performs an implicit conversion of the result
12087cc95a9SLouis Dionne {
12187cc95a9SLouis Dionne struct Convertible {
12287cc95a9SLouis Dionne constexpr operator int() const { return 42; }
12387cc95a9SLouis Dionne };
12487cc95a9SLouis Dionne auto f = []() -> Convertible { return Convertible{}; };
12587cc95a9SLouis Dionne int result = std::invoke_r<int>(f);
12687cc95a9SLouis Dionne assert(result == 42);
12787cc95a9SLouis Dionne }
12887cc95a9SLouis Dionne
12987cc95a9SLouis Dionne // Note: We don't test that `std::invoke_r` works with all kinds of callable types here,
13087cc95a9SLouis Dionne // since that is extensively tested in the `std::invoke` tests.
13187cc95a9SLouis Dionne
13287cc95a9SLouis Dionne return true;
13387cc95a9SLouis Dionne }
13487cc95a9SLouis Dionne
main(int,char **)13587cc95a9SLouis Dionne int main(int, char**) {
13687cc95a9SLouis Dionne test();
13787cc95a9SLouis Dionne static_assert(test());
13887cc95a9SLouis Dionne return 0;
13987cc95a9SLouis Dionne }
140