All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
operations.h
Go to the documentation of this file.
1 /**
2  * @file larcorealg/CoreUtils/operations.h
3  * @brief Provides a few simple operations for use in generic programming.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date June 5, 2019
6  *
7  * This is a header-only library.
8  */
9 
10 #ifndef LARCOREALG_COREUTILS_OPERATIONS_H
11 #define LARCOREALG_COREUTILS_OPERATIONS_H
12 
13 
14 // C++ standard library
15 #include <memory> // std::addressof()
16 
17 
18 namespace util {
19 
20  //----------------------------------------------------------------------------
21  /**
22  * @brief Functor returning the address in memory of the operand.
23  * @see `util::takeAddress()`
24  *
25  * Example:
26  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
27  * std::vector<int*> ptrs(data.size());
28  * std::transform
29  * (data.begin(), data.end(), ptrs.begin(), util::AddressTaker{});
30  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31  * will fill the vector `ptrs` with pointers to the elements of `data`.
32  *
33  * @note The address is extracted via `std::addressof()` and it bypasses the
34  * `operator&()` of the operand.
35  */
36  struct AddressTaker {
37 
38  /// Returns the address of the argument.
39  template <typename T>
40  auto operator() (T& ref) const { return std::addressof(ref); }
41 
42  }; // struct AddressTaker
43 
44 
45 
46  /**
47  * @brief Returns a functor that returns the address of its argument.
48  * @see `util::AddressTaker`
49  *
50  * Example:
51  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
52  * std::vector<int*> ptrs(data.size());
53  * std::transform
54  * (data.begin(), data.end(), ptrs.begin(), util::takeAddress());
55  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56  * will fill the vector `ptrs` with pointers to the elements of `data`.
57  *
58  * Why bother?
59  * ------------
60  *
61  * C++ already provides a tool to effectively take an address,
62  * `std::addressof`. The reason for `takeAddress()` is that `std::addressof()`
63  * is a function, with many overloads, and to use it in a STL algorithm the
64  * overload has to be resolved first. For example:
65  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
66  * using addressof_t = int const*(*)(int const&);
67  *
68  * std::transform(data.cbegin(), data.cend(), std::back_inserter(dataPtr),
69  * ((addressof_t) &std::addressof));
70  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
71  * One important limit is that the type of the argument (in this case
72  * `int const&`) needs to be known or deduced in a quite precise way, in
73  * particular regarding constantness and referenceness.
74  * This is unconvenient and convoluted enough that one would rather create
75  * a new function, like:
76  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
77  * auto takeAddress = [](auto&& ref){ return std::addressof(ref); };
78  *
79  * std::vector<int const*> dataPtr;
80  * std::transform(data.cbegin(), data.cend(), std::back_inserter(dataPtr),
81  * takeAddress);
82  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83  * This `util::takeAddress()` operates in a very similar way to the lambda in
84  * the last example.
85  */
86  decltype(auto) takeAddress() { return AddressTaker(); }
87 
88 
89  //----------------------------------------------------------------------------
90  /**
91  * @brief Functor dereferencing the operand.
92  * @see `util::dereference()`
93  *
94  * Example:
95  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
96  * std::vector<int> values(ptrs.size());
97  * std::transform
98  * (ptrs.cbegin(), ptrs.cend(), values.begin(), util::Dereferencer{});
99  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
100  * will fill the vector `values` with the values pointed by the elements in
101  * `ptrs`.
102  */
103  struct Dereferencer {
104 
105  /// Returns `*ptr`.
106  template <typename T>
107  decltype(auto) operator() (T&& ptr) const { return *ptr; }
108 
109  }; // struct Dereferencer
110 
111 
112  /**
113  * @brief Returns a functor that returns `*ptr` of its argument `ptr`.
114  * @see `util::Dereferencer`
115  *
116  * Example:
117  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
118  * std::vector<int> values(ptrs.size());
119  * std::transform
120  * (ptrs.cbegin(), ptrs.cend(), values.begin(), util::dereference());
121  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122  * will fill the vector `values` with the values pointed by the elements in
123  * `ptrs`.
124  */
125  decltype(auto) dereference() { return Dereferencer(); }
126 
127 
128  //----------------------------------------------------------------------------
129 
130 } // namespace util
131 
132 
133 #endif // LARCOREALG_COREUTILS_OPERATIONS_H
decltype(auto) takeAddress()
Returns a functor that returns the address of its argument.
Definition: operations.h:86
Functor returning the address in memory of the operand.
Definition: operations.h:36
auto operator()(T &ref) const
Returns the address of the argument.
Definition: operations.h:40
decltype(auto) dereference()
Returns a functor that returns *ptr of its argument ptr.
Definition: operations.h:125
Functor dereferencing the operand.
Definition: operations.h:103