ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dfe_namedtuple.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file dfe_namedtuple.hpp
1 // SPDX-License-Identifier: MIT
2 // Copyright 2015,2018-2020 Moritz Kiehn
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 // SOFTWARE.
21 
29 
30 #pragma once
31 
32 #include <array>
33 #include <cassert>
34 #include <ostream>
35 #include <string>
36 #include <tuple>
37 #include <utility>
38 
45 #define DFE_NAMEDTUPLE(name, ...) \
46  using Tuple = decltype(::std::make_tuple(__VA_ARGS__)); \
47  static ::std::array<::std::string, ::std::tuple_size<Tuple>::value> \
48  names() { \
49  return ::dfe::namedtuple_impl::unstringify< \
50  ::std::tuple_size<Tuple>::value>((#__VA_ARGS__)); \
51  } \
52  template<typename... U> \
53  name& operator=(const ::std::tuple<U...>& other) { \
54  ::std::tie(__VA_ARGS__) = other; \
55  return *this; \
56  } \
57  template<typename... U> \
58  name& operator=(::std::tuple<U...>&& other) { \
59  ::std::tie(__VA_ARGS__) = ::std::forward<std::tuple<U...>>(other); \
60  return *this; \
61  } \
62  operator Tuple() const { return ::std::make_tuple(__VA_ARGS__); } \
63  Tuple tuple() const { return ::std::make_tuple(__VA_ARGS__); } \
64  template<std::size_t I> \
65  constexpr ::std::tuple_element_t<I, Tuple>& get() { \
66  return ::std::get<I>(std::tie(__VA_ARGS__)); \
67  } \
68  template<::std::size_t I> \
69  constexpr const ::std::tuple_element_t<I, Tuple>& get() const { \
70  return ::std::get<I>(std::tie(__VA_ARGS__)); \
71  } \
72  template<::std::size_t I> \
73  friend constexpr ::std::tuple_element_t<I, Tuple>& get(name& nt) { \
74  return nt.template get<I>(); \
75  } \
76  template<::std::size_t I> \
77  friend constexpr const ::std::tuple_element_t<I, Tuple>& get( \
78  const name& nt) { \
79  return nt.template get<I>(); \
80  } \
81  friend inline ::std::ostream& operator<<(::std::ostream& os, const name& nt) \
82  __attribute__((unused)) { \
83  return ::dfe::namedtuple_impl::print_tuple( \
84  os, nt.names(), nt.tuple(), \
85  ::std::make_index_sequence<::std::tuple_size<Tuple>::value>{}); \
86  }
87 
88 // implementation helpers
89 namespace dfe {
90 namespace namedtuple_impl {
91 
92 // Reverse macro stringification.
93 //
94 // Splits a string of the form `a, b, c` into components a, b, and c.
95 template<std::size_t N>
96 constexpr std::array<std::string, N>
97 unstringify(const char* str) {
98  assert(str and "Input string must be non-null");
99 
100  std::array<std::string, N> out;
101 
102  for (std::size_t idx = 0; idx < N; ++idx) {
103  // skip leading whitespace
104  while ((*str != '\0') and (*str == ' ')) {
105  ++str;
106  }
107  // find the next separator or end-of-string
108  const char* sep = str;
109  while ((*sep != '\0') and (*sep != ',')) {
110  ++sep;
111  }
112  // store component w/o the separator
113  out[idx].assign(str, sep - str);
114  // we can quit as soon as we reached the end of the input
115  if (*sep == '\0') {
116  break;
117  }
118  // start search for next component after the separator
119  str = ++sep;
120  }
121  // TODO handle inconsistent number of entries? can it occur in expected use?
122  return out;
123 }
124 
125 // modified from http://stackoverflow.com/a/6245777
126 template<typename Names, typename Values, std::size_t... I>
127 inline std::ostream&
129  std::ostream& os, const Names& n, const Values& v,
130  std::index_sequence<I...>) {
131  // we want to execute some expression for every entry in the index pack. this
132  // requires a construction that can take a variable number of arguments into
133  // which we can unpack the indices. inside a function, constructing an
134  // array with an initializer list will do the job, i.e. we will effectively
135  // create the following statement
136  //
137  // int x[] = {...};
138  //
139  // since we do not care about the actual values within the array, the
140  // initializer list is cast twice: once to the array type and then to void.
141  // this ignores the actual values and silences warnings about unused
142  // variables. to get the correct initializer list syntax, the array type
143  // must be typedef'd as a single type. what we get is
144  //
145  // using Vacuum = int[];
146  // (void)Vacuum{...};
147  //
148  // in order for this to work, the expression that we want to instantiate
149  // needs to evaluate to the element type of the array (here: `int`). this can
150  // be done with the comma operator (yep, `,` is a weird but helpful operator)
151  // for arbitrary expressions. `(<expr1>, <expr2>)` executes both expressions
152  // but evaluates only to the return value of the second expression. thus,
153  // `(<expr>, 0)` executes `<expr>` but always evalutes to an integer of value
154  // zero. if <expr> uses the index pack variable `I` in the following setup
155  //
156  // (void)Vacuum{(<expr>, 0)...};
157  //
158  // it is instantiatied for each element within the pack (with appropriate ,
159  // placements). thus, effectively looping over every entry in the pack and
160  // calling <expr> for each (here: printing to os);
161  using std::get;
162  using Vacuum = int[];
163  (void)Vacuum{
164  (os << ((0 < I) ? " " : "") << get<I>(n) << "=" << get<I>(v), 0)...};
165  return os;
166 }
167 
168 } // namespace namedtuple_impl
169 } // namespace dfe