@@ -73,37 +73,62 @@ template <typename ObjectType> class PerThread {
7373 }
7474};
7575
76+ template <typename ContainerTy> struct ContainerConcepts {
77+ template <typename , template <typename > class , typename = std::void_t <>>
78+ struct has : std::false_type {};
79+ template <typename Ty, template <typename > class Op >
80+ struct has <Ty, Op, std::void_t <Op<Ty>>> : std::true_type {};
81+
82+ template <typename Ty> using IteratorTypeCheck = typename Ty::iterator;
83+ template <typename Ty> using MappedTypeCheck = typename Ty::mapped_type;
84+ template <typename Ty> using ValueTypeCheck = typename Ty::value_type;
85+ template <typename Ty> using KeyTypeCheck = typename Ty::key_type;
86+ template <typename Ty> using SizeTyCheck = typename Ty::size_type;
87+
88+ template <typename Ty>
89+ using ClearCheck = decltype (std::declval<Ty>().clear());
90+ template <typename Ty>
91+ using ClearAllCheck = decltype (std::declval<Ty>().clearAll(1 ));
92+ template <typename Ty>
93+ using ReserveCheck = decltype (std::declval<Ty>().reserve(1 ));
94+ template <typename Ty>
95+ using ResizeCheck = decltype (std::declval<Ty>().resize(1 ));
96+
97+ static constexpr bool hasIterator =
98+ has<ContainerTy, IteratorTypeCheck>::value;
99+ static constexpr bool hasClear = has<ContainerTy, ClearCheck>::value;
100+ static constexpr bool hasClearAll = has<ContainerTy, ClearAllCheck>::value;
101+ static constexpr bool isAssociative =
102+ has<ContainerTy, MappedTypeCheck>::value;
103+ static constexpr bool hasReserve = has<ContainerTy, ReserveCheck>::value;
104+ static constexpr bool hasResize = has<ContainerTy, ResizeCheck>::value;
105+
106+ template <typename , template <typename > class , typename = std::void_t <>>
107+ struct has_type {
108+ using type = void ;
109+ };
110+ template <typename Ty, template <typename > class Op >
111+ struct has_type <Ty, Op, std::void_t <Op<Ty>>> {
112+ using type = Op<Ty>;
113+ };
114+
115+ using iterator = typename has_type<ContainerTy, IteratorTypeCheck>::type;
116+ using value_type = typename std::conditional_t <
117+ isAssociative, typename has_type<ContainerTy, MappedTypeCheck>::type,
118+ typename has_type<ContainerTy, ValueTypeCheck>::type>;
119+ using key_type = typename std::conditional_t <
120+ isAssociative, typename has_type<ContainerTy, KeyTypeCheck>::type,
121+ typename has_type<ContainerTy, SizeTyCheck>::type>;
122+ };
123+
76124// Using an STL container (such as std::vector) indexed by thread ID has
77125// too many race conditions issues so we store each thread entry into a
78126// thread_local variable.
79127// ContainerType is the container type used to store the objects, e.g.,
80128// std::vector, std::set, etc. by each thread. ObjectType is the type of the
81129// stored objects e.g., omp_interop_val_t *, ...
82130template <typename ContainerType, typename ObjectType> class PerThreadTable {
83- using iterator = typename ContainerType::iterator;
84-
85- template <typename , typename = std::void_t <>>
86- struct has_iterator : std::false_type {};
87- template <typename T>
88- struct has_iterator <T, std::void_t <typename T::iterator>> : std::true_type {};
89-
90- template <typename T, typename = std::void_t <>>
91- struct has_clear : std::false_type {};
92- template <typename T>
93- struct has_clear <T, std::void_t <decltype (std::declval<T>().clear())>>
94- : std::true_type {};
95-
96- template <typename T, typename = std::void_t <>>
97- struct has_clearAll : std::false_type {};
98- template <typename T>
99- struct has_clearAll <T, std::void_t <decltype (std::declval<T>().clearAll(1 ))>>
100- : std::true_type {};
101-
102- template <typename , typename = std::void_t <>>
103- struct is_associative : std::false_type {};
104- template <typename T>
105- struct is_associative <T, std::void_t <typename T::mapped_type>>
106- : std::true_type {};
131+ using iterator = typename ContainerConcepts<ContainerType>::iterator;
107132
108133 struct PerThreadData {
109134 size_t NElements = 0 ;
@@ -188,12 +213,12 @@ template <typename ContainerType, typename ObjectType> class PerThreadTable {
188213 for (std::shared_ptr<PerThreadData> ThreadData : ThreadDataList) {
189214 if (!ThreadData->ThreadEntry || ThreadData->NElements == 0 )
190215 continue ;
191- if constexpr (has_clearAll <ContainerType>::value ) {
216+ if constexpr (ContainerConcepts <ContainerType>::hasClearAll ) {
192217 ThreadData->ThreadEntry ->clearAll (ClearFunc);
193- } else if constexpr (has_iterator <ContainerType>::value &&
194- has_clear <ContainerType>::value ) {
218+ } else if constexpr (ContainerConcepts <ContainerType>::hasIterator &&
219+ ContainerConcepts <ContainerType>::hasClear ) {
195220 for (auto &Obj : *ThreadData->ThreadEntry ) {
196- if constexpr (is_associative <ContainerType>::value ) {
221+ if constexpr (ContainerConcepts <ContainerType>::isAssociative ) {
197222 ClearFunc (Obj.second );
198223 } else {
199224 ClearFunc (Obj);
@@ -215,7 +240,7 @@ template <typename ContainerType, typename ObjectType> class PerThreadTable {
215240 if (!ThreadData->ThreadEntry || ThreadData->NElements == 0 )
216241 continue ;
217242 for (auto &Obj : *ThreadData->ThreadEntry ) {
218- if constexpr (is_associative <ContainerType>::value ) {
243+ if constexpr (ContainerConcepts <ContainerType>::isAssociative ) {
219244 if (auto Err = DeinitFunc (Obj.second ))
220245 return Err;
221246 } else {
@@ -228,49 +253,24 @@ template <typename ContainerType, typename ObjectType> class PerThreadTable {
228253 }
229254};
230255
231- template <typename T, typename = std::void_t <>> struct ContainerValueType {
232- using type = typename T::value_type;
233- };
234- template <typename T>
235- struct ContainerValueType <T, std::void_t <typename T::mapped_type>> {
236- using type = typename T::mapped_type;
237- };
238-
239256template <typename ContainerType, size_t ReserveSize = 0 >
240257class PerThreadContainer
241- : public PerThreadTable<ContainerType,
242- typename ContainerValueType<ContainerType>::type> {
258+ : public PerThreadTable<ContainerType, typename ContainerConcepts<
259+ ContainerType>::value_type> {
260+
261+ using IndexType = typename ContainerConcepts<ContainerType>::key_type;
262+ using ObjectType = typename ContainerConcepts<ContainerType>::value_type;
243263
244- // helpers
245- template <typename T, typename = std::void_t <>> struct indexType {
246- using type = typename T::size_type;
247- };
248- template <typename T> struct indexType <T, std::void_t <typename T::key_type>> {
249- using type = typename T::key_type;
250- };
251- template <typename T, typename = std::void_t <>>
252- struct has_resize : std::false_type {};
253- template <typename T>
254- struct has_resize <T, std::void_t <decltype (std::declval<T>().resize(1 ))>>
255- : std::true_type {};
256-
257- template <typename T, typename = std::void_t <>>
258- struct has_reserve : std::false_type {};
259- template <typename T>
260- struct has_reserve <T, std::void_t <decltype (std::declval<T>().reserve(1 ))>>
261- : std::true_type {};
262-
263- using IndexType = typename indexType<ContainerType>::type;
264- using ObjectType = typename ContainerValueType<ContainerType>::type;
265264public:
266265 // Get the object for the given index in the current thread
267266 ObjectType &get (IndexType Index) {
268267 ContainerType &Entry = this ->getThreadEntry ();
269268
270269 // specialized code for vector-like containers
271- if constexpr (has_resize <ContainerType>::value ) {
270+ if constexpr (ContainerConcepts <ContainerType>::hasResize ) {
272271 if (Index >= Entry.size ()) {
273- if constexpr (has_reserve<ContainerType>::value && ReserveSize > 0 )
272+ if constexpr (ContainerConcepts<ContainerType>::hasReserve &&
273+ ReserveSize > 0 )
274274 Entry.reserve (ReserveSize);
275275
276276 // If the index is out of bounds, try resize the container
0 commit comments