[libcxx-commits] [libcxx] [libc++] Define an internal API for std::invoke and friends (PR #116637)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Nov 22 07:28:36 PST 2024


================
@@ -225,31 +252,50 @@ struct __invoke_void_return_wrapper<_Ret, true> {
   }
 };
 
+template <class _Func, class... _Args>
+inline const bool __is_invocable_v = __is_invocable<_Func, _Args...>::value;
+
+template <class _Ret, class _Func, class... _Args>
+inline const bool __is_invocable_r_v = __invokable_r<_Ret, _Func, _Args...>::value;
+
+template <class _Func, class... _Args>
+inline const bool __is_nothrow_invocable_v = __nothrow_invokable<_Func, _Args...>::value;
+
+template <class _Func, class... _Args>
+struct __invoke_result
+    : enable_if<__is_invocable_v<_Func, _Args...>, typename __invokable_r<void, _Func, _Args...>::_Result> {};
+
+template <class _Func, class... _Args>
+using __invoke_result_t = typename __invoke_result<_Func, _Args...>::type;
+
+template <class _Ret, class... _Args>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... __args) {
+  return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
+}
+
 #if _LIBCPP_STD_VER >= 17
 
 // is_invocable
 
 template <class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_invocable : integral_constant<bool, __invokable<_Fn, _Args...>::value> {};
+struct _LIBCPP_TEMPLATE_VIS is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...>> {};
 
 template <class _Ret, class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_invocable_r : integral_constant<bool, __invokable_r<_Ret, _Fn, _Args...>::value> {};
+struct _LIBCPP_TEMPLATE_VIS is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
 
 template <class _Fn, class... _Args>
-inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value;
+inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>;
 
 template <class _Ret, class _Fn, class... _Args>
-inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value;
+inline constexpr bool is_invocable_r_v = __is_invocable_r_v<_Ret, _Fn, _Args...>;
 
 // is_nothrow_invocable
 
 template <class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : integral_constant<bool, __nothrow_invokable<_Fn, _Args...>::value> {
-};
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : __nothrow_invokable<_Fn, _Args...> {};
 
 template <class _Ret, class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r
-    : integral_constant<bool, __nothrow_invokable_r<_Ret, _Fn, _Args...>::value> {};
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r : __nothrow_invokable_r<_Ret, _Fn, _Args...> {};
 
 template <class _Fn, class... _Args>
 inline constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<_Fn, _Args...>::value;
----------------
ldionne wrote:

Chris had a patch on Clang at some point where we discussed this and I voiced concerns. Here it is: https://reviews.llvm.org/D130867

Here are some of my concerns:

1. Implementing more stuff in the compiler adds complexity to the compiler, and does not decrease the complexity of the library because libc++ still needs to implement invoke on other compilers.
2. It duplicates the tests, since we need to test the builtin and the standard library interface to the same level.
3. Duplicating functionality in libc++ and in the compiler is confusing. For instance, it sets up the stage for people not knowing where a bug in `std::invoke` should be fixed. Is it in the compiler? In the library? In both? And the set of folks who can fix a bug in the compiler is a lot smaller than the set of folks who can do that in the library.
4. It creates another level of coupling between the library and the compiler version-wise. For example, if we have a LWG issue fixing `std::invoke`, we must now fix it in Clang and whether the issue is fixed is not something we can determine from the library, it becomes something that depends on the compiler. This is technically a pre-existing issue with the builtin type traits, but in practice the type traits have a simple API and they don't change much, unlike `std::invoke` which has a complex simplification and could be the target of changes.


https://github.com/llvm/llvm-project/pull/116637


More information about the libcxx-commits mailing list