Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
remove_duplicates.hpp
Go to the documentation of this file.
1// Copyright 2024, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_TYPE_LIST_REMOVE_DUPLICATES_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_TYPE_LIST_REMOVE_DUPLICATES_HPP_
7
8// IWYU pragma: private, include "arene/base/type_list.hpp"
9// IWYU pragma: friend "(arene/base(?!/tests)|stdlib/include/stdlib_detail)/.*"
10
11// parasoft-begin-suppress AUTOSAR-A16_2_2-a-2 "Arene Base aggregate headers permitted by A16-2-2 Permit #1"
12// parasoft-begin-suppress CERT_C-EXP37-a "False positive: The rule does not mention naming all parameters"
13// parasoft-begin-suppress AUTOSAR-M2_10_1-a-2 "Similar names permitted by M2-10-1 Permit #1"
14
15#include "arene/base/algorithm/copy_if.hpp"
16#include "arene/base/detail/dynamic_extent.hpp"
17#include "arene/base/functional/bind_back.hpp"
18#include "arene/base/stdlib_choice/cstddef.hpp"
19#include "arene/base/stdlib_choice/ignore.hpp"
20#include "arene/base/stdlib_choice/integer_sequence.hpp"
21#include "arene/base/stdlib_choice/not_equal_to.hpp"
22#include "arene/base/type_list/at.hpp"
23#include "arene/base/type_list/size.hpp"
24#include "arene/base/type_traits/index_of.hpp"
25#include "arene/base/type_traits/type_identity.hpp"
26
27namespace arene {
28namespace base {
29
30namespace type_lists {
31
32/// @cond INTERNAL
33
34namespace remove_duplicates_detail {
35
36/// @brief collapse L0 (aka TypeList0<T...>) to TypeList0<U...>, where U... does
37/// not contain any duplicates of the same type
38template <class L0>
39struct remove_duplicates_impl;
40
41// The overall approach of this algorithm is:
42// Starting from a list of types Ts...
43//
44// * create a masked index array where the value is either:
45// ** I if T is the *last* occurence of T in Ts...
46// ** dynamic_extent if T is *not* the last occurence of T in Ts...
47//
48// * create a unique index array by dropping all the dynamic_extent entries
49//
50// * create a new type list by mapping the indices back to the type in Ts...
51
52/// @brief return the number of elements in the range matching a value
53/// @tparam Range range type
54/// @param range range of elements to examine
55/// @param value value to count
56/// @return number of elements in @c range equal to @c value
57///
58/// @note arene::base::count is not defined
59///
60template <class Range>
61constexpr auto count(Range const& range, std::size_t value) //
62 -> std::size_t //
63{
64 std::size_t num{};
65 // parasoft-begin-suppress AUTOSAR-A7_1_5-a "False Positive: 'element' has return type of range element, which may
66 // be non-fundamental"
67 for (auto const& elem : range) {
68 if (elem == value) {
69 ++num;
70 }
71 }
72 // parasoft-end-suppress AUTOSAR-A7_1_5-a
73 return num;
74}
75
76// parasoft-begin-suppress AUTOSAR-A11_0_2-a "It is consistent with developer
77// expectations that a type should be defined as a struct if it has no
78// invariants to maintain"
79
80/// @brief an array of indices
81/// @tparam N array size
82///
83/// Simple wrapper around a C array.
84///
85/// @note This avoids using 'arene::base::array' to reduce dependencies.
86///
87template <std::size_t N>
88struct index_array {
89 // parasoft-begin-suppress AUTOSAR-A2_10_1-d "False positive: this identifier does not hide anything"
90 /// @brief value_type
91 using value_type = std::size_t;
92 // parasoft-end-suppress AUTOSAR-A2_10_1-d
93
94 // parasoft-begin-suppress AUTOSAR-A18_1_1-a "This must work in contexts where std::array is not available."
95 /// @brief array values
96 // NOLINTNEXTLINE(hicpp-avoid-c-arrays,misc-non-private-member-variables-in-classes)
97 value_type values[N]{};
98 // parasoft-end-suppress AUTOSAR-A18_1_1-a
99
100 /// @brief iterator to the beginning of the range
101 /// @return iterator to the beginning of the range
102 constexpr auto begin() noexcept -> value_type* { return values; }
103 /// @brief iterator to the beginning of the range
104 /// @return iterator to the beginning of the range
105 constexpr auto begin() const noexcept -> value_type const* { return values; }
106
107 // parasoft-begin-suppress AUTOSAR-M5_0_15-a "The incremented pointer does point to an array"
108 /// @brief iterator past the end of the range
109 /// @return iterator past the end of the range
110 constexpr auto end() noexcept -> value_type* { return begin() + N; }
111 /// @brief iterator past the end of the range
112 /// @return iterator past the end of the range
113 constexpr auto end() const noexcept -> value_type const* { return begin() + N; }
114 // parasoft-end-suppress AUTOSAR-M5_0_15-a
115};
116
117// parasoft-end-suppress AUTOSAR-A11_0_2-a
118
119// parasoft-begin-suppress AUTOSAR-M3_3_2-a "False positive: inline function used in multiple translation units"
120/// @brief returns indices of the last occurence of each type in a parameter pack
121/// @tparam Ts types
122/// @tparam Is indices
123/// @return a masked array of indices
124///
125/// Returns an array of indices. For each type @c T in @c Ts..., if @c T is the
126/// *last* occurence, the element in the returned array is equal to its index in
127/// @c Ts... . Otherwise the element is equal to @c dynamic_extent.
128///
129/// For example, the returned array for type sequence 'int, double, double', is
130/// '[0, dynamic_extent, 2]'.
131///
132template <class... Ts, std::size_t... Is>
133constexpr auto masked_indices_of_impl(std::index_sequence<Is...>) //
134 -> index_array<sizeof...(Ts)> //
135{ //
136 // parasoft-begin-suppress AUTOSAR-A5_16_1-a "Alternatives to ?: make the code less readable and harder to maintain"
137 // parasoft-begin-suppress AUTOSAR-M8_5_2-a "False positive: there is no subobject here that can be initialized"
138 return { //
139 ((Is == arene::base::last_index_of_v<Ts, Ts...>) //
140 ? Is
141 : dynamic_extent)...
142 };
143 // parasoft-end-suppress AUTOSAR-M8_5_2-a
144 // parasoft-end-suppress AUTOSAR-A5_16_1-a
145}
146// parasoft-end-suppress AUTOSAR-M3_3_2-a
147
148/// @brief masked array containing indices of the last occurence of each type in a type list
149/// @note The primary template is a dummy, 'remove_duplicates' will always use
150/// specialization below
151///
152template <class>
153extern constexpr auto masked_indices_of = nullptr;
154
155/// @brief masked array containing indices of the last occurence of each type in a type list
156/// @tparam List type list
157/// @tparam Ts types
158///
159template <template <class...> class List, class... Ts>
160extern constexpr auto masked_indices_of<List<Ts...>> = //
161 masked_indices_of_impl<Ts...>(std::index_sequence_for<Ts...>{});
162
163/// @brief obtains the unique number of types in a type list
164/// @tparam List type list
165///
166template <class List>
167extern constexpr auto unique_count_v = type_lists::size_v<List> - count(masked_indices_of<List>, dynamic_extent);
168
169// parasoft-begin-suppress AUTOSAR-M3_3_2-a "False positive: cannot apply static here"
170/// @brief returns indices of the last occurence of each type in a type list
171/// @tparam Ts types
172/// @tparam Is indices
173/// @return an array of indices
174///
175/// Returns an array of indices. Each index is the last occurence of a type @c T
176/// in @c Ts... .
177///
178/// For example, the returned array for type list '<int, double, double>', is
179/// '[0, 2]'.
180///
181template <class... Ts>
182constexpr auto unique_indices_impl() -> index_array<unique_count_v<Ts...>> //
183{
184 auto filtered = index_array<unique_count_v<Ts...>>{};
185 std::ignore = arene::base::copy_if( //
186 masked_indices_of<Ts...>.begin(),
187 masked_indices_of<Ts...>.end(),
188 filtered.begin(),
189 arene::base::bind_back(std::not_equal_to<>{}, dynamic_extent)
190 );
191 return filtered;
192}
193// parasoft-end-suppress AUTOSAR-M3_3_2-a
194
195/// @brief array containing indices of the last occurence of each type in a type list
196/// @tparam Ts types
197///
198template <class... Ts>
199extern constexpr auto unique_indices = unique_indices_impl<Ts...>();
200
201// parasoft-begin-suppress AUTOSAR-M3_3_2-a "False positive: cannot apply static here"
202/// @brief implementation function for @c remove_duplicates
203/// @tparam List type list
204/// @tparam Ts types
205/// @tparam Js unique indices
206/// @return type list with duplicates removed
207///
208template <template <class...> class List, class... Ts, std::size_t... Js>
209auto remove_duplicates_from_impl(std::index_sequence<Js...>) //
210 -> List< //
211 type_lists::at_t< //
212 List<Ts...>,
213 unique_indices<List<Ts...>>.begin()[Js]>...>;
214// parasoft-end-suppress AUTOSAR-M3_3_2-a
215
216// parasoft-begin-suppress AUTOSAR-M3_3_2-a "False positive: cannot apply static here"
217/// @brief implementation function for @c remove_duplicates
218/// @tparam List type list
219/// @tparam T0 first type
220/// @tparam Tn remaining types
221/// @return type list with duplicates removed
222///
223template <template <class...> class List, class T0, class... Tn>
224auto remove_duplicates_from(type_identity<List<T0, Tn...>>) //
225 -> decltype( //
226 remove_duplicates_from_impl<List, T0, Tn...>( //
227 std::make_index_sequence<unique_count_v<List<T0, Tn...>>>{} //
228 )
229 );
230// parasoft-end-suppress AUTOSAR-M3_3_2-a
231
232/// @brief implementation function for @c remove_duplicates
233/// @tparam List type list
234/// @return empty type list
235/// @note overload for an empty type list
236///
237template <template <class...> class List>
238auto remove_duplicates_from(type_identity<List<>>) -> List<>;
239
240/// @brief implementation type for @c remove_duplicates
241/// @tparam List type list
242/// @tparam Ts types
243///
244template <template <class...> class List, class... Ts>
245struct remove_duplicates_impl<List<Ts...>> {
246 static_assert(
247 sizeof...(Ts) != dynamic_extent, //
248 "cannot handle 'std::size_t(-1)' number of types"
249 );
250
251 /// @brief type list with duplicates removed
252 using type = decltype(remove_duplicates_from(type_identity<List<Ts...>>{}));
253};
254
255} // namespace remove_duplicates_detail
256
257/// @endcond
258
259/// @brief Filter a type-list to remove duplicates of elements.
260/// @details Only keeps the *last* instance of a type in the type list.
261///
262/// @tparam L0 An instantiation of a type-list that holds the types to filter
263/// @pre @c L0 is a type-list
264/// @return TypeList<T...>
265template <class L0>
266using remove_duplicates = arene::base::type_lists::remove_duplicates_detail::remove_duplicates_impl<L0>;
267
268/// @brief Filter a type-list to remove duplicates of elements.
269/// @details Only keeps the *last* instance of a type in the type list.
270///
271/// @tparam L0 An instantiation of a type-list that holds the types to filter
272/// @pre @c L0 is a type-list
273/// @return TypeList<T...>
274template <class L0>
276
277} // namespace type_lists
278
279} // namespace base
280} // namespace arene
281
282#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_TYPE_LIST_REMOVE_DUPLICATES_HPP_
Definition apply_all.hpp:14
Definition array_exceptions_disabled.cpp:11
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10