From 6423138702831ec96b9eab5c9a72798779609918 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Tue, 25 Aug 2026 10:12:56 -0600 Subject: [PATCH] :sparkles: Add `filtered_index` Problem: - After filtering a type list, it's sometimes useful to know where an index in the original list mapped to in the filtered list. Solution: - Add `filtered_index` for this purpose. --- CMakeLists.txt | 5 +++++ docs/design/meta.md | 41 ++++++++++++++++++++++++++++++++++++++++ docs/header_graph.mmd | 6 ++++-- docs/index.adoc | 1 + docs/intro.adoc | 2 ++ docs/meta.adoc | 40 +++++++++++++++++++++++++++++++++++++++ include/stdx/meta.hpp | 38 +++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/meta.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 docs/design/meta.md create mode 100644 docs/meta.adoc create mode 100644 include/stdx/meta.hpp create mode 100644 test/meta.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b6a02b..53d91cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ target_sources( include/stdx/intrusive_list.hpp include/stdx/iterator.hpp include/stdx/latched.hpp + include/stdx/meta.hpp include/stdx/numeric.hpp include/stdx/optional.hpp include/stdx/panic.hpp @@ -89,6 +90,10 @@ target_sources( include/stdx/utility.hpp) if(PROJECT_IS_TOP_LEVEL) + set_target_properties(stdx PROPERTIES VERIFY_INTERFACE_HEADER_SETS true) + add_dependencies(${INFRA_TARGET_NAMESPACE}quality + stdx_verify_interface_header_sets) + include(CTest) add_docs(docs) add_subdirectory(test) diff --git a/docs/design/meta.md b/docs/design/meta.md new file mode 100644 index 0000000..9616ae4 --- /dev/null +++ b/docs/design/meta.md @@ -0,0 +1,41 @@ +# meta + +Source code: https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/meta.hpp +Documentation: https://intel.github.io/cpp-std-extensions/#_meta_hpp + +## Filtering out `void` + +`filtered_index` solves a subproblem of aggregating function results. You have a +list of functions: + +```cpp +// f0 :: () -> int +// f1 :: () -> void +// f2 :: () -> int + +using func_list_t = std::tuple; +``` + +Some of those functions return `void`. It is easy enough to make a `tuple` of +results with the `void` elements filtered out: + +```cpp +template +using returns_void = std::is_void>; + +using non_void_func_list_t = boost::mp11::mp_remove_if; +// -> std::tuple + +using results_t = boost::mp11::mp_transform; +// -> std::tuple +``` + +The problem is: after running say `f2` and getting the result, where in the +results tuple should it be placed? This is what `filtered_index` answers: where +does the original index corresponding to `f2` map to in the filtered list? + +If this problem seems solvable a simpler way, imagine that the functions are +asynchronous, we need to provision the results tuple space up front, and handle +asynchronous placement of the results as they come in. We need to deal with +indices because any of the function types or result types might coincide, i.e. +we can't find them reliably by type. diff --git a/docs/header_graph.mmd b/docs/header_graph.mmd index 6663afb..c38efc0 100644 --- a/docs/header_graph.mmd +++ b/docs/header_graph.mmd @@ -6,13 +6,15 @@ flowchart BT array(array.hpp) atomic(atomic.hpp) ct_conversions(ct_conversions.hpp) - priority(priority.hpp) + meta(meta.hpp) numeric(numeric.hpp) + priority(priority.hpp) array ~~~ compiler atomic ~~~ compiler ct_conversions --> compiler - priority ~~~ compiler + meta ~~~ compiler numeric ~~~ compiler + priority ~~~ compiler %% level 2 type_traits(type_traits.hpp) diff --git a/docs/index.adoc b/docs/index.adoc index d781b1a..edacf67 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -33,6 +33,7 @@ include::intrusive_forward_list.adoc[] include::intrusive_list.adoc[] include::iterator.adoc[] include::latched.adoc[] +include::meta.adoc[] include::numeric.adoc[] include::optional.adoc[] include::panic.adoc[] diff --git a/docs/intro.adoc b/docs/intro.adoc index b11a48d..d284f3d 100644 --- a/docs/intro.adoc +++ b/docs/intro.adoc @@ -19,6 +19,7 @@ The following compilers are supported: * clang 18 * clang 19 * clang 20 +* clang 21 * clang 22 * gcc 12 * gcc 13 @@ -88,6 +89,7 @@ The following headers are available: * https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/intrusive_list.hpp[`intrusive_list.hpp`] * https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/iterator.hpp[`iterator.hpp`] * https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/latched.hpp[`latched.hpp`] +* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/meta.hpp[`meta.hpp`] * https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/numeric.hpp[`numeric.hpp`] * https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/optional.hpp[`optional.hpp`] * https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/panic.hpp[`panic.hpp`] diff --git a/docs/meta.adoc b/docs/meta.adoc new file mode 100644 index 0000000..acc832b --- /dev/null +++ b/docs/meta.adoc @@ -0,0 +1,40 @@ + +== `meta.hpp` + +https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/meta.hpp[`meta.hpp`] +defines some metafunctions intended for compatibility with https://www.boost.org/libs/mp11[`boost::mp11`]. + +The metafunctions are in the `stdx::mp` namespace. + +=== `filtered_index` + +`filtered_index` answers the question: given a list and a predicate, if the list +were filtered (as if by `boost::mp11::mp_copy_if`) by that predicate, where +would an index in the unfiltered list map to in the filtered list? + +`filtered_index_c` is like `filtered_index`, but instead of taking a +`std::integral_constant`, it takes a plain integral type. + +[source,cpp] +---- +// list: (boost::mp_int<> of) 0, 1, 2, 3, 4 +using list_t = boost::mp11::mp_iota_c<5>; + +// predicate +template using is_even = boost::mp11::mp_bool; + +// filtered list would be (boost::mp_int<> of) 0, 2, 4 +using index_of_0 = stdx::mp::filtered_index_c; +using index_of_2 = stdx::mp::filtered_index_c; +using index_of_4 = stdx::mp::filtered_index_c; +static_assert(std::same_as>); +static_assert(std::same_as>); +static_assert(std::same_as>); + +// filtered items don't exist in the list -- by boost convention, the index +// returned is equal to the (filtered) list size +using index_of_1 = stdx::mp::filtered_index_c; +using index_of_3 = stdx::mp::filtered_index_c; +static_assert(std::same_as>); +static_assert(std::same_as>); +---- diff --git a/include/stdx/meta.hpp b/include/stdx/meta.hpp new file mode 100644 index 0000000..5df8979 --- /dev/null +++ b/include/stdx/meta.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include + +namespace stdx { +inline namespace v1 { +namespace mp { +namespace detail { +template