From 0e762161e13a9c656e0eea13c30c55e07ef50e8c Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Fri, 15 Sep 2023 15:13:48 -0600 Subject: [PATCH 1/2] :sparkles: Add `is_constant_evaluated` --- docs/type_traits.adoc | 1 + include/stdx/type_traits.hpp | 3 +++ test/CMakeLists.txt | 1 + test/is_constant_evaluated.cpp | 23 +++++++++++++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 test/is_constant_evaluated.cpp diff --git a/docs/type_traits.adoc b/docs/type_traits.adoc index 040fdfd..54dc5a1 100644 --- a/docs/type_traits.adoc +++ b/docs/type_traits.adoc @@ -10,6 +10,7 @@ contains a few things from the standard: (implemented with fewer template instantiations than a typical standard implementation) * https://en.cppreference.com/w/cpp/types/is_function[`is_function_v`] (implemented with Walter Brown's method) +* https://en.cppreference.com/w/cpp/types/is_constant_evaluated[`is_constant_evaluated`] (from C++20) It also contains `always_false_v`, a variable template that can be instantiated with any number of type arguments and always evaluates to false at compile-time. diff --git a/include/stdx/type_traits.hpp b/include/stdx/type_traits.hpp index ec6f5d9..e187a63 100644 --- a/include/stdx/type_traits.hpp +++ b/include/stdx/type_traits.hpp @@ -56,5 +56,8 @@ constexpr bool is_function_object_v = detail::is_func_obj; template constexpr bool is_callable_v = is_function_v or is_function_object_v; +constexpr auto is_constant_evaluated() noexcept -> bool { + return __builtin_is_constant_evaluated(); +} } // namespace v1 } // namespace stdx diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6df4917..5346e28 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ add_tests( default_panic function_traits intrusive_list + is_constant_evaluated overload panic priority diff --git a/test/is_constant_evaluated.cpp b/test/is_constant_evaluated.cpp new file mode 100644 index 0000000..c0e9615 --- /dev/null +++ b/test/is_constant_evaluated.cpp @@ -0,0 +1,23 @@ +#include + +#include + +namespace { +constexpr auto f() -> int { + if (stdx::is_constant_evaluated()) { + return 42; + } else { + return 0; + } +} +} // namespace + +TEST_CASE("constexpr context", "[is_constant_evaluated]") { + constexpr auto n = f(); + CHECK(n == 42); +} + +TEST_CASE("runtime context", "[is_constant_evaluated]") { + auto n = f(); + CHECK(n == 0); +} From a96fbfbba69d48c16d3ecfcd9b75bd59eac3a137 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Fri, 15 Sep 2023 15:54:12 -0600 Subject: [PATCH 2/2] :sparkles: Add `for_each_n_args` --- docs/for_each_n_args.adoc | 29 +++++++++++++++ docs/function_traits.adoc | 9 ++++- docs/index.adoc | 1 + include/stdx/for_each_n_args.hpp | 64 ++++++++++++++++++++++++++++++++ include/stdx/function_traits.hpp | 2 + test/CMakeLists.txt | 1 + test/detail/tuple_types.hpp | 6 ++- test/for_each_n_args.cpp | 40 ++++++++++++++++++++ test/function_traits.cpp | 19 ++++++++++ 9 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 docs/for_each_n_args.adoc create mode 100644 include/stdx/for_each_n_args.hpp create mode 100644 test/for_each_n_args.cpp diff --git a/docs/for_each_n_args.adoc b/docs/for_each_n_args.adoc new file mode 100644 index 0000000..b875c65 --- /dev/null +++ b/docs/for_each_n_args.adoc @@ -0,0 +1,29 @@ + +== `for_each_n_args.hpp` + +https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/for_each_n_args.hpp[`for_each_n_args.hpp`] +provides a method for calling a function (or other callable) with batches of +arguments from a parameter pack. + +Examples: +[source,cpp] +---- +auto f(int x, int y) -> void { /* do something with x and y */ } +stdx::for_each_n_args(f, 1, 2, 3, 4); // this calls f(1, 2) and f(3, 4) +---- + +The number of arguments passed to `for_each_n_args` must be a multiple of the +argument "batch size" - which by default is the arity of the passed function. + +Sometimes, the passed callable is a generic function where the arity cannot be +automatically determined, or sometimes it may be a function with default +arguments which we want to use. In that case it is possible to override the +default batch size: +[source,cpp] +---- +auto f(auto x, auto y) -> void { /* do something with x and y */ } +stdx::for_each_n_args<2>(f, 1, 2, 3, 4); // this calls f(1, 2) and f(3, 4) + +auto g(int x, int y, int z = 42) -> void { /* do something with x, y and z */ } +stdx::for_each_n_args<2>(g, 1, 2, 3, 4); // this calls g(1, 2, 42) and g(3, 4, 42) +---- diff --git a/docs/function_traits.adoc b/docs/function_traits.adoc index 988a83c..9736417 100644 --- a/docs/function_traits.adoc +++ b/docs/function_traits.adoc @@ -24,13 +24,20 @@ using l_args = stdx::args_t; // std::tuple `stdx::args_t` returns a list of the function arguments. `std::decayed_args_t` returns the same list, but with `std::decay_t` applied to each element. This is useful for example when you need to copy and store a tuple of the arguments. - [source,cpp] ---- auto f(int&, std::string&) -> void {} using f_args = stdx::decayed_args_t; // std::tuple ---- +`stdx::arity_t` returns the arity of a function (as a `std::integral_constant`). +[source,cpp] +---- +auto f(int&, std::string&) -> void {} +using f_arity = stdx::arity_t; // std::integral_constant +---- + + NOTE: Function traits work on functions (and function objects): not function templates or overload sets. For instance therefore, they will not work on generic lambda expressions. diff --git a/docs/index.adoc b/docs/index.adoc index df873cd..b5e37ff 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -18,6 +18,7 @@ include::cx_multimap.adoc[] include::cx_queue.adoc[] include::cx_set.adoc[] include::cx_vector.adoc[] +include::for_each_n_args.adoc[] include::function_traits.adoc[] include::functional.adoc[] include::intrusive_list.adoc[] diff --git a/include/stdx/for_each_n_args.hpp b/include/stdx/for_each_n_args.hpp new file mode 100644 index 0000000..97580f9 --- /dev/null +++ b/include/stdx/for_each_n_args.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +#if __cplusplus >= 202002L +#include +#define TUPLE_T stdx::tuple +#define TUPLE_GET stdx::get +#else +#include +#define TUPLE_T std::tuple +#define TUPLE_GET std::get +#endif + +#include +#include +#include + +namespace stdx { +inline namespace v1 { + +namespace detail { +template struct for_each_n_args; + +template +struct for_each_n_args, + std::index_sequence> { + template static auto apply(F &&f, T &&t) -> void { + (exec(f, std::forward(t)), ...); + } + + private: + template + static auto exec(F &&f, T &&t) -> void { + std::invoke(f, TUPLE_GET(std::forward(t))...); + } +}; +} // namespace detail + +template +void for_each_n_args(F &&f, Args &&...args) { + constexpr auto batch_size = [] { + if constexpr (N == 0) { + return arity_t::value; + } else { + return N; + } + }(); + static_assert(sizeof...(Args) % batch_size == 0, + "for_each_n_args: number of args must be a multiple of the " + "given N (or function arity)"); + + using tuple_t = TUPLE_T; + detail::for_each_n_args< + std::make_index_sequence, + std::make_index_sequence>::apply(std::forward(f), + tuple_t{std::forward( + args)...}); +} +} // namespace v1 +} // namespace stdx + +#undef TUPLE_T +#undef TUPLE_GET diff --git a/include/stdx/function_traits.hpp b/include/stdx/function_traits.hpp index 57b5a02..e40ac2a 100644 --- a/include/stdx/function_traits.hpp +++ b/include/stdx/function_traits.hpp @@ -16,6 +16,7 @@ struct function_traits> { template