Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-21 09:58:12

0001 // SPDX-License-Identifier: MIT
0002 // Copyright 2015,2018-2020 Moritz Kiehn
0003 //
0004 // Permission is hereby granted, free of charge, to any person obtaining a copy
0005 // of this software and associated documentation files (the "Software"), to deal
0006 // in the Software without restriction, including without limitation the rights
0007 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0008 // copies of the Software, and to permit persons to whom the Software is
0009 // furnished to do so, subject to the following conditions:
0010 //
0011 // The above copyright notice and this permission notice shall be included in
0012 // all copies or substantial portions of the Software.
0013 //
0014 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0017 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0018 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0019 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0020 // SOFTWARE.
0021 
0022 /// \file
0023 /// \brief   Allow structs to be accessed like std::tuple
0024 /// \author  Moritz Kiehn <msmk@cern.ch>
0025 /// \date    2015-06-00, Initial version
0026 /// \date    2018-02-09, Major rework
0027 /// \date    2019-09-09, Split i/o components into separate libraries
0028 /// \date    2020-10-13, Add support for direct use of `get<I>(named_tuple)`
0029 
0030 #pragma once
0031 
0032 #include <array>
0033 #include <cassert>
0034 #include <ostream>
0035 #include <string>
0036 #include <tuple>
0037 #include <utility>
0038 
0039 /// Enable tuple-like access and conversion for selected class/struct members.
0040 ///
0041 /// This allows access to the selected members via `.get<I>()` or `get<I>(...)`,
0042 /// conversion to equivalent `std::tuple<...>` via implicit conversion or
0043 /// explicitely via `.tuple()`,  and assignment from equivalent tuples.
0044 /// The names can be accessed via `::names()`.
0045 #define DFE_NAMEDTUPLE(name, ...) \
0046   using Tuple = decltype(::std::make_tuple(__VA_ARGS__)); \
0047   static ::std::array<::std::string, ::std::tuple_size<Tuple>::value> \
0048   names() { \
0049     return ::dfe::namedtuple_impl::unstringify< \
0050       ::std::tuple_size<Tuple>::value>((#__VA_ARGS__)); \
0051   } \
0052   template<typename... U> \
0053   name& operator=(const ::std::tuple<U...>& other) { \
0054     ::std::tie(__VA_ARGS__) = other; \
0055     return *this; \
0056   } \
0057   template<typename... U> \
0058   name& operator=(::std::tuple<U...>&& other) { \
0059     ::std::tie(__VA_ARGS__) = ::std::forward<std::tuple<U...>>(other); \
0060     return *this; \
0061   } \
0062   operator Tuple() const { return ::std::make_tuple(__VA_ARGS__); } \
0063   Tuple tuple() const { return ::std::make_tuple(__VA_ARGS__); } \
0064   template<std::size_t I> \
0065   constexpr ::std::tuple_element_t<I, Tuple>& get() { \
0066     return ::std::get<I>(std::tie(__VA_ARGS__)); \
0067   } \
0068   template<::std::size_t I> \
0069   constexpr const ::std::tuple_element_t<I, Tuple>& get() const { \
0070     return ::std::get<I>(std::tie(__VA_ARGS__)); \
0071   } \
0072   template<::std::size_t I> \
0073   friend constexpr ::std::tuple_element_t<I, Tuple>& get(name& nt) { \
0074     return nt.template get<I>(); \
0075   } \
0076   template<::std::size_t I> \
0077   friend constexpr const ::std::tuple_element_t<I, Tuple>& get( \
0078     const name& nt) { \
0079     return nt.template get<I>(); \
0080   } \
0081   friend inline ::std::ostream& operator<<(::std::ostream& os, const name& nt) \
0082     __attribute__((unused)) { \
0083     return ::dfe::namedtuple_impl::print_tuple( \
0084       os, nt.names(), nt.tuple(), \
0085       ::std::make_index_sequence<::std::tuple_size<Tuple>::value>{}); \
0086   }
0087 
0088 // implementation helpers
0089 namespace dfe {
0090 namespace namedtuple_impl {
0091 
0092 // Reverse macro stringification.
0093 //
0094 // Splits a string of the form `a, b, c` into components a, b, and c.
0095 template<std::size_t N>
0096 constexpr std::array<std::string, N>
0097 unstringify(const char* str) {
0098   assert(str and "Input string must be non-null");
0099 
0100   std::array<std::string, N> out;
0101 
0102   for (std::size_t idx = 0; idx < N; ++idx) {
0103     // skip leading whitespace
0104     while ((*str != '\0') and (*str == ' ')) {
0105       ++str;
0106     }
0107     // find the next separator or end-of-string
0108     const char* sep = str;
0109     while ((*sep != '\0') and (*sep != ',')) {
0110       ++sep;
0111     }
0112     // store component w/o the separator
0113     out[idx].assign(str, sep - str);
0114     // we can quit as soon as we reached the end of the input
0115     if (*sep == '\0') {
0116       break;
0117     }
0118     // start search for next component after the separator
0119     str = ++sep;
0120   }
0121   // TODO handle inconsistent number of entries? can it occur in expected use?
0122   return out;
0123 }
0124 
0125 // modified from http://stackoverflow.com/a/6245777
0126 template<typename Names, typename Values, std::size_t... I>
0127 inline std::ostream&
0128 print_tuple(
0129   std::ostream& os, const Names& n, const Values& v,
0130   std::index_sequence<I...>) {
0131   // we want to execute some expression for every entry in the index pack. this
0132   // requires a construction that can take a variable number of arguments into
0133   // which we can unpack the indices. inside a function, constructing an
0134   // array with an initializer list will do the job, i.e. we will effectively
0135   // create the following statement
0136   //
0137   //     int x[] = {...};
0138   //
0139   // since we do not care about the actual values within the array, the
0140   // initializer list is cast twice: once to the array type and then to void.
0141   // this ignores the actual values and silences warnings about unused
0142   // variables. to get the correct initializer list syntax, the array type
0143   // must be typedef'd as a single type. what we get is
0144   //
0145   //     using Vacuum = int[];
0146   //     (void)Vacuum{...};
0147   //
0148   // in order for this to work, the expression that we want to instantiate
0149   // needs to evaluate to the element type of the array (here: `int`). this can
0150   // be done with the comma operator (yep, `,` is a weird but helpful operator)
0151   // for arbitrary expressions. `(<expr1>, <expr2>)` executes both expressions
0152   // but evaluates only to the return value of the second expression. thus,
0153   // `(<expr>, 0)` executes `<expr>` but always evalutes to an integer of value
0154   // zero. if <expr> uses the index pack variable `I` in the following setup
0155   //
0156   //     (void)Vacuum{(<expr>, 0)...};
0157   //
0158   // it is instantiatied for each element within the pack (with appropriate ,
0159   // placements). thus, effectively looping over every entry in the pack and
0160   // calling <expr> for each (here: printing to os);
0161   using std::get;
0162   using Vacuum = int[];
0163   (void)Vacuum{
0164     (os << ((0 < I) ? " " : "") << get<I>(n) << "=" << get<I>(v), 0)...};
0165   return os;
0166 }
0167 
0168 } // namespace namedtuple_impl
0169 } // namespace dfe