Skip to content

Commit 7175c6c

Browse files
committed
Fixed an issue where TypeList::Unique had factorial compilation time complexity
Signed-off-by: Nick Avramoussis <[email protected]>
1 parent 57cb9d6 commit 7175c6c

File tree

1 file changed

+77
-28
lines changed

1 file changed

+77
-28
lines changed

openvdb/openvdb/TypeList.h

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -112,36 +112,85 @@ struct TSHasTypeImpl<TypeList<T, Ts...>, T, Idx>
112112
};
113113

114114

115-
/// @brief Remove any duplicate types from a @c TypeList.
116-
/// @details This implementation effectively rebuilds a @c TypeList by starting
117-
/// with an empty @c TypeList and recursively defining an expanded
118-
/// @c TypeList for every type (first to last), only if the type does
119-
/// not already exist in the new @c TypeList. This has the effect of
120-
/// dropping all but the first of duplicate types.
121-
/// @note Each type must define a new instantiation of this object.
122-
/// @tparam ListT The starting @c TypeList, usually (but not limited to) an
123-
/// empty @c TypeList
124-
/// @tparam Ts The list of types to make unique
125-
template <typename ListT, typename... Ts>
126-
struct TSMakeUniqueImpl {
127-
using type = ListT;
115+
/// @brief Similar to TsAppendImpl but only appends types to a list if the
116+
/// type does not alreay exist in the list.
117+
/// @details Defines a new @c TypeList with non-unique types appended
118+
/// @tparam U Type to append
119+
/// @tparam ListT The @c TypeList to append to
120+
template <typename U, typename ListT,
121+
bool ListContainsType = TSHasTypeImpl<ListT, U>::Value>
122+
struct TSAppendUniqueImpl;
123+
124+
/// @brief Partial specialization where the currently evaluating type @c U in
125+
/// a @c TypeList already exists in the list. Returns the unmodified list.
126+
/// @tparam U Type to append
127+
/// @tparam Ts Other types within the @c TypeList
128+
template <typename U, typename... Ts>
129+
struct TSAppendUniqueImpl<U, TypeList<Ts...>, true> {
130+
private:
131+
using RemovedU = typename TypeList<Ts...>::template Remove<U>;
132+
public:
133+
/// @note It's simpler to remove the current type U and append the rest by
134+
/// just having "using type = TypeList<Ts...>". However this ends up with
135+
/// with keeping the last seen type rather than the first which this
136+
/// method historically did. e.g:
137+
/// TypeList<float, int, float>::Unique<> can become:
138+
/// a) TypeList<float, int> currently
139+
/// b) TypeList<int, float> if we used the afformentioned technique
140+
/// Might be useful to have both? Complexity in (a) is currently linear so
141+
/// this shouldn't be a problem, but be careful this doesn't change.
142+
//using type = TypeList<Ts...>;
143+
using type = typename TypeList<U>::template Append<RemovedU>;
144+
};
145+
146+
/// @brief Partial specialization where the currently evaluating type @c U in
147+
/// a @c TypeList does not exists in the list. Returns the appended list.
148+
/// @tparam U Type to append
149+
/// @tparam Ts Other types within the @c TypeList
150+
template <typename U, typename... Ts>
151+
struct TSAppendUniqueImpl<U, TypeList<Ts...>, false> {
152+
using type = TypeList<U, Ts...>;
153+
};
154+
155+
/// @brief Reconstruct a @c TypeList containing only unique types.
156+
/// @details This implementation effectively rebuilds a @c TypeList by
157+
/// starting with an empty @c TypeList and recursively defining an expanded
158+
/// @c TypeList for every type (first to last), only if the type does not
159+
/// already exist in the new @c TypeList. This has the effect of dropping all
160+
/// but the first of duplicate types.
161+
/// @warning This implementation previously used an embdedded std::conditional
162+
/// which resulted in drastically slow compilation times. If you're changing
163+
/// this implementation make sure to profile compile times with larger lists.
164+
/// @tparam Ts Types within the @c TypeList
165+
template <typename... Ts>
166+
struct TSRecurseAppendUniqueImpl;
167+
168+
/// @brief Terminate type recursion when the end of a @c TypeList is reached.
169+
template <>
170+
struct TSRecurseAppendUniqueImpl<> {
171+
using type = TypeList<>;
172+
};
173+
174+
/// @brief Merge and unpack an initial @c TypeList from the first argument if
175+
/// such a @c TypeList has been provided.
176+
/// @tparam Ts Types within the first @c TypeList
177+
/// @tparam OtherTs Other types
178+
template <typename... Ts, typename... OtherTs>
179+
struct TSRecurseAppendUniqueImpl<TypeList<Ts...>, OtherTs...> {
180+
using type = typename TSRecurseAppendUniqueImpl<OtherTs..., Ts...>::type;
128181
};
129182

130-
/// @brief Partial specialization for type packs, where by the next type @c U
131-
/// is checked in the existing type set @c Ts for duplication. If the
132-
/// type does not exist, it is added to the new @c TypeList definition,
133-
/// otherwise it is dropped. In either case, this class is recursively
134-
/// defined with the remaining types @c Us.
135-
/// @tparam Ts Current types in the @c TypeList
136-
/// @tparam U Type to check for duplication in @c Ts
137-
/// @tparam Us Remaining types
138-
template <typename... Ts, typename U, typename... Us>
139-
struct TSMakeUniqueImpl<TypeList<Ts...>, U, Us...>
183+
/// @brief Recursively call TSRecurseAppendUniqueImpl with each type in the
184+
/// provided @c TypeLists, rebuilding a new list with only the unique set
185+
/// of types.
186+
/// @tparam U Next type to check for uniqueness and append
187+
/// @tparam Ts Remaining types within the @c TypeList
188+
template <typename U, typename... Ts>
189+
struct TSRecurseAppendUniqueImpl<U, Ts...>
140190
{
141-
using type = typename std::conditional<
142-
TSHasTypeImpl<TypeList<Ts...>, U>::Value,
143-
typename TSMakeUniqueImpl<TypeList<Ts...>, Us...>::type,
144-
typename TSMakeUniqueImpl<TypeList<Ts..., U>, Us...>::type >::type;
191+
using type = typename TSAppendUniqueImpl<U,
192+
typename TSRecurseAppendUniqueImpl<Ts...>::type
193+
>::type;
145194
};
146195

147196

@@ -425,7 +474,7 @@ struct TypeList
425474
/// }
426475
/// @endcode
427476
template<typename ListT = TypeList<>>
428-
using Unique = typename typelist_internal::TSMakeUniqueImpl<ListT, Ts...>::type;
477+
using Unique = typename typelist_internal::TSRecurseAppendUniqueImpl<ListT, Ts...>::type;
429478

430479
/// @brief Append types, or the members of another TypeList, to this list.
431480
/// @details Example:

0 commit comments

Comments
 (0)