timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
data.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25#pragma once
26
28#include "timemory/mpl/math.hpp"
30#include "timemory/tpls/cereal/cereal.hpp"
32
33#include <array>
34
35namespace tim
36{
37namespace component
38{
39//
40template <typename Tp, size_t Sz>
41struct base_data;
42//
43template <typename Tp>
44struct base_data<Tp, 0>
45{
47
48 TIMEMORY_INLINE value_type get_value() const { return value_type{}; }
49 TIMEMORY_INLINE value_type get_accum() const { return value_type{}; }
50 TIMEMORY_INLINE value_type get_last() const { return value_type{}; }
51 TIMEMORY_INLINE void set_value(value_type) {}
52 TIMEMORY_INLINE void set_accum(value_type) {}
53 TIMEMORY_INLINE void set_last(value_type) {}
54
55 base_data() = default;
56 ~base_data() = default;
57
58 base_data& operator=(base_data&&) = default; // NOLINT
59 base_data(base_data&&) = default; // NOLINT
60
61 base_data(const base_data&) = default;
62 base_data& operator=(const base_data&) = default;
63
64 TIMEMORY_INLINE value_type load(bool) { return value_type{}; }
65 TIMEMORY_INLINE value_type load(bool) const { return value_type{}; }
66
67 void plus(const value_type&) {}
68 void minus(const value_type&) {}
69 void multiply(const value_type&) {}
70 void divide(const value_type&) {}
71
72 template <typename Up>
73 void plus(Up&&)
74 {}
75
76 template <typename Up>
77 void minus(Up&&)
78 {}
79
80 template <typename Up>
81 void multiply(Up&&)
82 {}
83
84 template <typename Up>
85 void divide(Up&&)
86 {}
87
88protected:
89 value_type value = {}; // NOLINT
90 value_type accum = {}; // NOLINT
91 value_type last = {}; // NOLINT
92};
93//
94template <typename Tp>
95struct base_data<Tp, 1>
96{
97 using empty_type = std::tuple<>;
98 using value_type = Tp;
101
102 TIMEMORY_INLINE const value_type& get_value() const { return value; }
103 TIMEMORY_INLINE const value_type& get_accum() const { return value; }
104 TIMEMORY_INLINE const value_type& get_last() const { return value; }
105 TIMEMORY_INLINE void set_value(value_type v) { value = v; }
106 TIMEMORY_INLINE void set_accum(value_type) {}
107 TIMEMORY_INLINE void set_last(value_type) {}
108
109 base_data() = default;
110 ~base_data() = default;
111
112 base_data& operator=(base_data&&) = default; // NOLINT
113 base_data(base_data&&) = default; // NOLINT
114
115 base_data(const base_data&) = default;
116 base_data& operator=(const base_data&) = default;
117
118 template <typename ArchiveT>
119 void serialize(ArchiveT& ar, const unsigned int = 0)
120 {
121 ar(cereal::make_nvp("value", value));
122 }
123
124 TIMEMORY_INLINE value_type& load(bool) { return value; }
125 TIMEMORY_INLINE const value_type& load(bool) const { return value; }
126
127 void plus(const value_type& lhs)
128 {
129 value = math::compute<value_type>::plus(value, lhs);
130 }
131
132 void minus(const value_type& lhs)
133 {
134 value = math::compute<value_type>::minus(value, lhs);
135 }
136
137 void multiply(const value_type& lhs)
138 {
139 value = math::compute<value_type>::multiply(value, lhs);
140 }
141
142 void divide(const value_type& lhs)
143 {
144 value = math::compute<value_type>::divide(value, lhs);
145 }
146
147 template <typename Up>
148 auto plus(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(), void())
149 {
150 value = math::compute<value_type>::plus(value, std::forward<Up>(rhs).get_value());
151 }
152
153 template <typename Up>
154 auto minus(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(), void())
155 {
156 value =
157 math::compute<value_type>::minus(value, std::forward<Up>(rhs).get_value());
158 }
159
160 template <typename Up>
161 auto multiply(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(), void())
162 {
163 value =
164 math::compute<value_type>::multiply(value, std::forward<Up>(rhs).get_value());
165 }
166
167 template <typename Up>
168 auto divide(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(), void())
169 {
170 value =
171 math::compute<value_type>::divide(value, std::forward<Up>(rhs).get_value());
172 }
173
174 // we need 'using base_data<Tp>::accum' to be valid so make a function call
175 value_type& accum() { return value; }
176
177 // we need 'using base_data<Tp>::last' to be valid so make a function call
178 value_type& last() { return value; }
179
180protected:
181 void reset() { value = Tp{}; }
182
183protected:
184 value_type value = Tp{}; // NOLINT
185};
186//
187template <typename Tp>
188struct base_data<Tp, 2>
189{
190 template <typename Up, typename ValueT>
191 friend struct base;
192
193 using empty_type = std::tuple<>;
194 using value_type = Tp;
195 using accum_type = Tp;
197
198 TIMEMORY_INLINE const value_type& get_value() const { return value; }
199 TIMEMORY_INLINE const value_type& get_accum() const { return accum; }
200 TIMEMORY_INLINE const value_type& get_last() const { return value; }
201 TIMEMORY_INLINE void set_value(value_type v) { value = v; }
202 TIMEMORY_INLINE void set_accum(value_type v) { accum = v; }
203 TIMEMORY_INLINE void set_last(value_type) {}
204
205 base_data() = default;
206 ~base_data() = default;
207
208 base_data& operator=(base_data&&) = default; // NOLINT
209 base_data(base_data&&) = default; // NOLINT
210
211 base_data(const base_data& rhs) = default;
212 base_data& operator=(const base_data& rhs) = default;
213
214 template <typename ArchiveT>
215 void serialize(ArchiveT& ar, const unsigned int = 0)
216 {
217 ar(cereal::make_nvp("value", value), cereal::make_nvp("accum", accum));
218 }
219
220 TIMEMORY_INLINE value_type& load(bool is_transient)
221 {
222 return (is_transient) ? accum : value;
223 }
224 TIMEMORY_INLINE const value_type& load(bool is_transient) const
225 {
226 return (is_transient) ? accum : value;
227 }
228
229 void plus(const value_type& rhs)
230 {
231 value = math::compute<value_type>::plus(value, rhs);
232 accum = math::compute<value_type>::plus(accum, rhs);
233 }
234
235 void minus(const value_type& rhs)
236 {
237 value = math::compute<value_type>::minus(value, rhs);
238 accum = math::compute<value_type>::minus(accum, rhs);
239 }
240
241 void multiply(const value_type& rhs)
242 {
243 value = math::compute<value_type>::multiply(value, rhs);
244 accum = math::compute<value_type>::multiply(accum, rhs);
245 }
246
247 void divide(const value_type& rhs)
248 {
249 value = math::compute<value_type>::divide(value, rhs);
250 accum = math::compute<value_type>::divide(accum, rhs);
251 }
252
253 template <typename Up>
254 auto plus(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
255 (void) std::forward<Up>(rhs).get_accum(), void())
256 {
257 value = math::compute<value_type>::plus(value, std::forward<Up>(rhs).get_value());
258 accum = math::compute<value_type>::plus(accum, std::forward<Up>(rhs).get_accum());
259 }
260
261 template <typename Up>
262 auto minus(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
263 (void) std::forward<Up>(rhs).get_accum(), void())
264 {
265 value =
266 math::compute<value_type>::minus(value, std::forward<Up>(rhs).get_value());
267 accum =
268 math::compute<value_type>::minus(accum, std::forward<Up>(rhs).get_accum());
269 }
270
271 template <typename Up>
272 auto multiply(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
273 (void) std::forward<Up>(rhs).get_accum(), void())
274 {
275 value =
276 math::compute<value_type>::multiply(value, std::forward<Up>(rhs).get_value());
277 accum =
278 math::compute<value_type>::multiply(accum, std::forward<Up>(rhs).get_accum());
279 }
280
281 template <typename Up>
282 auto divide(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
283 (void) std::forward<Up>(rhs).get_accum(), void())
284 {
285 value =
286 math::compute<value_type>::divide(value, std::forward<Up>(rhs).get_value());
287 accum =
288 math::compute<value_type>::divide(accum, std::forward<Up>(rhs).get_accum());
289 }
290
291 // we need 'using base_data<Tp>::last' to be valid so make a function call
292 value_type& last() { return value; }
293
294protected:
295 void reset()
296 {
297 value = Tp{};
298 accum = Tp{};
299 }
300
301protected:
302 value_type value = Tp{}; // NOLINT
303 value_type accum = Tp{}; // NOLINT
304};
305//
306template <typename Tp>
307struct base_data<Tp, 3>
308{
309 template <typename Up, typename ValueT>
310 friend struct base;
311
312 using value_type = Tp;
313 using accum_type = Tp;
314 using last_type = Tp;
315
316 TIMEMORY_INLINE const value_type& get_value() const { return value; }
317 TIMEMORY_INLINE const value_type& get_accum() const { return accum; }
318 TIMEMORY_INLINE const value_type& get_last() const { return last; }
319 TIMEMORY_INLINE void set_value(value_type v) { value = v; }
320 TIMEMORY_INLINE void set_accum(value_type v) { accum = v; }
321 TIMEMORY_INLINE void set_last(value_type v) { last = v; }
322
323 base_data() = default;
324 ~base_data() = default;
325
326 base_data& operator=(base_data&&) = default; // NOLINT
327 base_data(base_data&&) = default; // NOLINT
328
329 base_data(const base_data& rhs) = default;
330 base_data& operator=(const base_data& rhs) = default;
331
332 template <typename ArchiveT>
333 void serialize(ArchiveT& ar, const unsigned int = 0)
334 {
335 ar(cereal::make_nvp("value", value), cereal::make_nvp("accum", accum),
336 cereal::make_nvp("last", last));
337 }
338
339 TIMEMORY_INLINE value_type& load(bool is_transient)
340 {
341 return (is_transient) ? accum : value;
342 }
343 TIMEMORY_INLINE const value_type& load(bool is_transient) const
344 {
345 return (is_transient) ? accum : value;
346 }
347
348 void plus(const value_type& rhs)
349 {
350 value = math::compute<value_type>::plus(value, rhs);
351 accum = math::compute<value_type>::plus(accum, rhs);
352 }
353
354 void minus(const value_type& rhs)
355 {
356 value = math::compute<value_type>::minus(value, rhs);
357 accum = math::compute<value_type>::minus(accum, rhs);
358 }
359
360 void multiply(const value_type& rhs)
361 {
362 value = math::compute<value_type>::multiply(value, rhs);
363 accum = math::compute<value_type>::multiply(accum, rhs);
364 }
365
366 void divide(const value_type& rhs)
367 {
368 value = math::compute<value_type>::divide(value, rhs);
369 accum = math::compute<value_type>::divide(accum, rhs);
370 }
371
372 template <typename Up>
373 auto plus(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
374 (void) std::forward<Up>(rhs).get_accum(), void())
375 {
376 value = math::compute<value_type>::plus(value, std::forward<Up>(rhs).get_value());
377 accum = math::compute<value_type>::plus(accum, std::forward<Up>(rhs).get_accum());
378 }
379
380 template <typename Up>
381 auto minus(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
382 (void) std::forward<Up>(rhs).get_accum(), void())
383 {
384 value =
385 math::compute<value_type>::minus(value, std::forward<Up>(rhs).get_value());
386 accum =
387 math::compute<value_type>::minus(accum, std::forward<Up>(rhs).get_accum());
388 }
389
390 template <typename Up>
391 auto multiply(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
392 (void) std::forward<Up>(rhs).get_accum(), void())
393 {
394 value =
395 math::compute<value_type>::multiply(value, std::forward<Up>(rhs).get_value());
396 accum =
397 math::compute<value_type>::multiply(accum, std::forward<Up>(rhs).get_accum());
398 }
399
400 template <typename Up>
401 auto divide(Up&& rhs) -> decltype((void) std::forward<Up>(rhs).get_value(),
402 (void) std::forward<Up>(rhs).get_accum(), void())
403 {
404 value =
405 math::compute<value_type>::divide(value, std::forward<Up>(rhs).get_value());
406 accum =
407 math::compute<value_type>::divide(accum, std::forward<Up>(rhs).get_accum());
408 }
409
410protected:
411 void reset()
412 {
413 value = Tp{};
414 accum = Tp{};
415 last = Tp{};
416 }
417
418protected:
419 value_type value = Tp{}; // NOLINT
420 value_type accum = Tp{}; // NOLINT
421 value_type last = Tp{}; // NOLINT
422};
423/// \struct base_state
424/// \brief Provide state configuration options for a component instance.
425/// The current states are:
426///
427/// 1. `get_is_running()` : returns true if a component has started collection.
428///
429/// 2. `get_is_on_stack()` : returns true if a component has bookmarked a storage
430/// location.
431///
432/// 3. `get_is_transient()` : returns true if the value returned from `get()` (or similar)
433/// represents a phase measurement.
434///
435/// 4. `get_is_flat()` : returns true if the component bookmarked storage at a
436/// call-stack depth of zero explicitly.
437///
438/// 5. `get_depth_change()` : used internally by components to determine if a push
439/// operation incremented/decremented the call-stack depth.
440///
441/// 6. `get_is_invalid()` : used to indicate that the metric does not have a valid
442/// state. If set to false before a push operation, this will
443/// suppress insertion into storage.
444///
446{
447protected:
449 using base_type::set;
450 using base_type::test;
451
452public:
453 using base_type::reset;
454
455 TIMEMORY_INLINE bool get_is_running() const { return test<RunningIdx>(); }
456 TIMEMORY_INLINE bool get_is_on_stack() const { return test<OnStackIdx>(); }
457 TIMEMORY_INLINE bool get_is_transient() const { return test<TransientIdx>(); }
458 TIMEMORY_INLINE bool get_is_flat() const { return test<FlatIdx>(); }
459 TIMEMORY_INLINE bool get_depth_change() const { return test<DepthIdx>(); }
460 TIMEMORY_INLINE bool get_is_invalid() const { return test<InvalidIdx>(); }
461
462 TIMEMORY_INLINE void set_is_running(bool v) { set<RunningIdx>(v); }
463 TIMEMORY_INLINE void set_is_on_stack(bool v) { set<OnStackIdx>(v); }
464 TIMEMORY_INLINE void set_is_transient(bool v) { set<TransientIdx>(v); }
465 TIMEMORY_INLINE void set_is_flat(bool v) { set<FlatIdx>(v); }
466 TIMEMORY_INLINE void set_depth_change(bool v) { set<DepthIdx>(v); }
467 TIMEMORY_INLINE void set_is_invalid(bool v) { set<InvalidIdx>(v); }
468
469protected:
470 enum State
471 {
478 };
479};
480//
481namespace internal
482{
483template <typename Tp, typename ValueT>
484struct base_data;
485//
486template <typename Tp>
487struct base_data<Tp, void>
488{
490};
491//
492template <typename Tp>
493struct base_data<Tp, type_list<>>
494{
496};
497//
498template <typename Tp>
499struct base_data<Tp, null_type>
500{
502};
503//
504template <typename Tp, typename ValueT>
505struct base_data
506{
507 static constexpr auto has_accum = trait::base_has_accum<Tp>::value;
508 static constexpr auto has_last = trait::base_has_last<Tp>::value;
509 static constexpr size_t array_size = 1 + ((has_accum) ? 1 : 0) + ((has_last) ? 1 : 0);
510
512
513 static_assert(!(has_last && !has_accum), "Error! base cannot have last w/o accum");
514};
515//
516} // namespace internal
517//
518template <typename Tp, typename ValueT>
520//
521} // namespace component
522} // namespace tim
typename internal::base_data< Tp, ValueT >::type base_data_t
Definition: data.hpp:519
Definition: kokkosp.cpp:39
void divide(const value_type &)
Definition: data.hpp:70
base_data(const base_data &)=default
void plus(const value_type &)
Definition: data.hpp:67
value_type get_last() const
Definition: data.hpp:50
value_type get_accum() const
Definition: data.hpp:49
value_type load(bool) const
Definition: data.hpp:65
void set_accum(value_type)
Definition: data.hpp:52
base_data & operator=(base_data &&)=default
void minus(const value_type &)
Definition: data.hpp:68
void multiply(const value_type &)
Definition: data.hpp:69
void set_last(value_type)
Definition: data.hpp:53
base_data(base_data &&)=default
base_data & operator=(const base_data &)=default
void set_value(value_type)
Definition: data.hpp:51
value_type get_value() const
Definition: data.hpp:48
void serialize(ArchiveT &ar, const unsigned int=0)
Definition: data.hpp:119
auto divide(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(), void())
Definition: data.hpp:168
base_data & operator=(const base_data &)=default
value_type & load(bool)
Definition: data.hpp:124
base_data & operator=(base_data &&)=default
void multiply(const value_type &lhs)
Definition: data.hpp:137
void plus(const value_type &lhs)
Definition: data.hpp:127
void set_accum(value_type)
Definition: data.hpp:106
void set_value(value_type v)
Definition: data.hpp:105
const value_type & load(bool) const
Definition: data.hpp:125
const value_type & get_value() const
Definition: data.hpp:102
auto minus(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(), void())
Definition: data.hpp:154
void minus(const value_type &lhs)
Definition: data.hpp:132
const value_type & get_last() const
Definition: data.hpp:104
base_data(base_data &&)=default
auto multiply(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(), void())
Definition: data.hpp:161
const value_type & get_accum() const
Definition: data.hpp:103
auto plus(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(), void())
Definition: data.hpp:148
void divide(const value_type &lhs)
Definition: data.hpp:142
base_data(const base_data &)=default
base_data & operator=(const base_data &rhs)=default
void plus(const value_type &rhs)
Definition: data.hpp:229
void set_value(value_type v)
Definition: data.hpp:201
auto multiply(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:272
base_data(const base_data &rhs)=default
auto minus(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:262
const value_type & get_value() const
Definition: data.hpp:198
void multiply(const value_type &rhs)
Definition: data.hpp:241
void minus(const value_type &rhs)
Definition: data.hpp:235
const value_type & load(bool is_transient) const
Definition: data.hpp:224
base_data(base_data &&)=default
value_type & load(bool is_transient)
Definition: data.hpp:220
const value_type & get_accum() const
Definition: data.hpp:199
void divide(const value_type &rhs)
Definition: data.hpp:247
auto divide(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:282
base_data & operator=(base_data &&)=default
void set_accum(value_type v)
Definition: data.hpp:202
const value_type & get_last() const
Definition: data.hpp:200
void serialize(ArchiveT &ar, const unsigned int=0)
Definition: data.hpp:215
auto plus(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:254
const value_type & get_value() const
Definition: data.hpp:316
base_data(const base_data &rhs)=default
void set_accum(value_type v)
Definition: data.hpp:320
value_type & load(bool is_transient)
Definition: data.hpp:339
void divide(const value_type &rhs)
Definition: data.hpp:366
const value_type & get_accum() const
Definition: data.hpp:317
auto multiply(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:391
base_data(base_data &&)=default
void set_value(value_type v)
Definition: data.hpp:319
auto minus(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:381
const value_type & get_last() const
Definition: data.hpp:318
void serialize(ArchiveT &ar, const unsigned int=0)
Definition: data.hpp:333
void set_last(value_type v)
Definition: data.hpp:321
void plus(const value_type &rhs)
Definition: data.hpp:348
auto plus(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:373
void minus(const value_type &rhs)
Definition: data.hpp:354
auto divide(Up &&rhs) -> decltype((void) std::forward< Up >(rhs).get_value(),(void) std::forward< Up >(rhs).get_accum(), void())
Definition: data.hpp:401
base_data & operator=(const base_data &rhs)=default
base_data & operator=(base_data &&)=default
void multiply(const value_type &rhs)
Definition: data.hpp:360
const value_type & load(bool is_transient) const
Definition: data.hpp:343
Provide state configuration options for a component instance. The current states are:
Definition: data.hpp:446
void set_is_transient(bool v)
Definition: data.hpp:464
bool get_is_flat() const
Definition: data.hpp:458
void set_is_on_stack(bool v)
Definition: data.hpp:463
bool get_depth_change() const
Definition: data.hpp:459
void set_is_running(bool v)
Definition: data.hpp:462
bool get_is_on_stack() const
Definition: data.hpp:456
bool get_is_transient() const
Definition: data.hpp:457
void set_depth_change(bool v)
Definition: data.hpp:466
bool get_is_running() const
Definition: data.hpp:455
bool get_is_invalid() const
Definition: data.hpp:460
void set_is_invalid(bool v)
Definition: data.hpp:467
void set_is_flat(bool v)
Definition: data.hpp:465
static decltype(auto) minus(type &_l, const V &_r)
Definition: compute.hpp:99
static decltype(auto) plus(type &_l, const V &_r)
Definition: compute.hpp:93
static decltype(auto) multiply(type &_l, const V &_r)
Definition: compute.hpp:105
static decltype(auto) divide(type &_l, const V &_r)
Definition: compute.hpp:111
this is a placeholder type for optional type-traits. It is used as the default type for the type-trai...
Definition: types.hpp:225
type_list
Definition: types.hpp:211