Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RRangeCast.hxx
Go to the documentation of this file.
1/// \file ROOT/RRangeCast.hxx
2/// \ingroup Base StdExt
3/// \author Jonas Rembser <jonas.rembser@cern.ch>
4/// \date 2021-08-04
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#ifndef ROOT_RRangeCast
15#define ROOT_RRangeCast
16
17#include "ROOT/RSpan.hxx"
18
19#include <cassert>
20#include <iterator>
21#include <type_traits>
22#include <utility>
23
24namespace ROOT {
25namespace Internal {
26
27template <typename T>
28struct RBaseType {
29 using type = typename std::remove_pointer<typename std::decay<T>::type>::type;
30};
31
32#if (__cplusplus < 201700L)
33
34template <typename T, bool isDynamic = true, bool isPolymorphic = std::is_polymorphic<RBaseType<T>>::value>
35struct RCast {
36 template <typename U>
37 static T cast(U &&u)
38 {
39 return dynamic_cast<T>(u);
40 }
41};
42
43template <typename T>
44struct RCast<T, false, false> {
45 template <typename U>
46 static T cast(U &&u)
47 {
48 return static_cast<T>(u);
49 }
50};
51
52template <typename T>
53struct RCast<T, false, true> {
54 template <typename U>
55 static T cast(U &&u)
56 {
57 assert(dynamic_cast<T>(u));
58 return static_cast<T>(u);
59 }
60};
61
62#endif
63
64// For SFINAE-based checks for the existence of the `begin` and `end` methods.
65template <typename T>
66constexpr auto hasBeginEnd(int) -> decltype(std::begin(std::declval<T>()), std::end(std::declval<T>()), true)
67{
68 return true;
69}
70
71template <typename>
72constexpr bool hasBeginEnd(...)
73{
74 return false;
75}
76
77template <typename T, typename WrappedIterator_t, bool isDynamic>
78class TypedIter {
79
80public:
81 TypedIter(WrappedIterator_t const &iter) : fIter{iter} {}
82
84 {
85 ++fIter;
86 return *this;
87 }
89 {
90 TypedIter tmp(*this);
91 operator++();
92 return tmp;
93 }
94 bool operator==(const TypedIter &rhs) const { return fIter == rhs.fIter; }
95 bool operator!=(const TypedIter &rhs) const { return fIter != rhs.fIter; }
96
97 void swap(TypedIter &other) { fIter.swap(other.fIter); }
98
99 // We want to know at compile time whether dynamic_cast or static_cast is
100 // used. First of all to avoid overhead, but also to avoid a compiler
101 // error when using dynamic_cast on a non-polymorphic class. In C++17,
102 // this can be done easily with `if constexpr`, but for the older
103 // standards we have to use a more verbose alternative. Both ways are
104 // explicitely implemented for different standards, so that when the
105 // minimum C++ standard for ROOT is raised to C++17 it's easy to remember
106 // that we can avoid much boilerplate code in this file.
107#if (__cplusplus < 201700L)
109#else
110 T operator*()
111 {
112 if constexpr (isDynamic) {
113 return dynamic_cast<T>(*fIter);
114 } else {
115 if constexpr (std::is_polymorphic<RBaseType<T>>::value) {
116 assert(dynamic_cast<T>(*fIter));
117 }
118 return static_cast<T>(*fIter);
119 }
120 }
121#endif
122
123private:
124 WrappedIterator_t fIter;
125};
126
127} // namespace Internal
128
129/// Wraps any collection that can be used in range-based loops and applies
130/// `static_cast<T>` or `dynamic_cast<T>` to each element.
131/// \tparam T The new type to convert to.
132/// \tparam isDynamic If `true`, `dynamic_cast` is used, otherwise `static_cast` is used.
133/// \tparam Range_t The type of the input range, which should be usually a reference type to avoid copying.
134template <typename T, bool isDynamic, typename Range_t>
136
137public:
138 RRangeCast(Range_t &&inputRange) : fInputRange{inputRange}
139 {
140 static_assert(ROOT::Internal::hasBeginEnd<Range_t>(0),
141 "Type with no `begin` or `end` method passed to `RRangeCast`");
142 }
143
145 const_iterator begin() const { return std::cbegin(fInputRange); }
146 const_iterator end() const { return std::cend(fInputRange); }
147
149 iterator begin() { return std::begin(fInputRange); }
150 iterator end() { return std::end(fInputRange); }
151
152private:
153 Range_t fInputRange;
154};
155
156/// Takes any collection that can be used in range-based loops and applies
157/// static_cast<T> to each element. This function can be used for example to
158/// cast all objects in a RooAbsCollection when iterating over them.
159/// Example:
160/// ~~~{.cpp}
161/// class ClassA {
162/// public:
163/// virtual ~ClassA() {}
164/// };
165/// class ClassB : public ClassA {
166/// };
167///
168/// B b1, b2, b3;
169/// std::vector<A const*> vec{&b1, &b2, &b3};
170///
171/// for(auto *b : ROOT::RangeStaticCast<B const*>(vec)) {
172/// // do something with b
173/// }
174/// ~~~
175/// Make sure to not use `auto const&` in the range-based loop, as this will
176/// cause a range-loop-bind-reference warning with the clang compiler.
177template <typename T, typename Range_t>
179{
180 return std::forward<Range_t>(coll);
181}
182// Overload for C-style arrays. It's not possible to make an overload of the
183// RRangeCast constructor itself, because when the C-style array is forwarded
184// it might decay depending on the compiler version.
185template <typename T, typename U, std::size_t N>
187{
188 return std::span<U>(arr, arr + N);
189}
190
191/// Takes any collection that can be used in range-based loops and applies
192/// dynamic_cast<T> to each element. This function can be used for example to
193/// cast all objects in a RooAbsCollection when iterating over them.
194/// Example:
195/// ~~~{.cpp}
196///
197/// class ClassA {
198/// public:
199/// virtual ~ClassA() {}
200/// };
201/// class ClassB : public ClassA {
202/// };
203///
204/// A a1, a2;
205/// B b1, b2, b3;
206/// std::vector<A const*> vec{&b1, &a1, &b2, &a2, &b3};
207///
208/// for(auto *b : ROOT::RangeDynCast<B const*>(vec)) {
209/// if(b) {
210/// // do something with b
211/// }
212/// }
213/// ~~~
214/// Make sure to not use `auto const&` in the range-based loop, as this will
215/// cause a range-loop-bind-reference warning with the clang compiler.
216template <typename T, typename Range_t>
218{
219 return std::forward<Range_t>(coll);
220}
221// Overload for C-style arrays. It's not possible to make an overload of the
222// RRangeCast constructor itself, because when the C-style array is forwarded
223// it might decay depending on the compiler version.
224template <typename T, typename U, std::size_t N>
226{
227 return std::span<U>(arr, arr + N);
228}
229
230} // namespace ROOT
231
232#endif
#define N
bool operator!=(const TypedIter &rhs) const
TypedIter(WrappedIterator_t const &iter)
bool operator==(const TypedIter &rhs) const
TypedIter operator++(int)
WrappedIterator_t fIter
void swap(TypedIter &other)
Wraps any collection that can be used in range-based loops and applies static_cast<T> or dynamic_cast...
const_iterator begin() const
const_iterator end() const
RRangeCast(Range_t &&inputRange)
constexpr auto hasBeginEnd(int) -> decltype(std::begin(std::declval< T >()), std::end(std::declval< T >()), true)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
RRangeCast< T, false, Range_t > RangeStaticCast(Range_t &&coll)
Takes any collection that can be used in range-based loops and applies static_cast<T> to each element...
RRangeCast< T, true, Range_t > RangeDynCast(Range_t &&coll)
Takes any collection that can be used in range-based loops and applies dynamic_cast<T> to each elemen...
typename std::remove_pointer< typename std::decay< T >::type >::type type
static T cast(U &&u)