Line data Source code
1 : // *****************************************************************************
2 : /*!
3 : \file src/Base/TaggedTuple.hpp
4 : \copyright 2012-2015 J. Bakosi,
5 : 2016-2018 Los Alamos National Security, LLC.,
6 : 2019-2021 Triad National Security, LLC.,
7 : 2022-2025 J. Bakosi
8 : All rights reserved. See the LICENSE file for details.
9 : \brief Tagged tuple allowing tag-based access
10 : \details Tagged tuple allowing tag-based access. This is very much like
11 : [std::tuple](http://en.cppreference.com/w/cpp/utility/tuple), but instead of
12 : having to index the elements by integers, it allows access by a tag, which
13 : can be an empty struct with a unique name. Credit goes to
14 : ecatmur_at_stackoverflow.com, for more details, see
15 : http://stackoverflow.com/questions/13065166/c11-tagged-tuple.
16 : */
17 : // *****************************************************************************
18 : #pragma once
19 :
20 : #include <type_traits>
21 : #include <tuple>
22 :
23 : #include <brigand/adapted/tuple.hpp>
24 :
25 : #include "NoWarning/any.hpp"
26 : #include "NoWarning/partition.hpp"
27 : #include "NoWarning/index_of.hpp"
28 :
29 : #include "PUPUtil.hpp"
30 : #include "Exception.hpp"
31 :
32 : namespace tag {
33 : //! Printable tag for TaggedTuple that returns its name
34 : #define DEFTAG(n) struct n { static const char* key() { return #n; } }
35 : } // tag::
36 :
37 : namespace tk {
38 :
39 : //! \brief Tagged tuple, allowing tag-based access
40 : //! \details "Tag" here is any type, but mostly an empty struct with a good name
41 : //! for the data member
42 : //! \tparam List Type list as brigand::list
43 : //! \see https://stackoverflow.com/a/42988897
44 : //! \see https://gist.github.com/underdoeg/4c5c616c1ad4cbb718f787eefcab902d
45 : template< class List >
46 : class TaggedTuple {
47 :
48 : private:
49 : //! Generate index for every 2nd type of a type list
50 : template< typename T >
51 : using is_odd = brigand::size_t< (brigand::index_of<List,T>::value%2) != 0 >;
52 :
53 : //! Partition a type list into two lists with the even and the odd types
54 : using Pair = brigand::partition< List, brigand::bind<is_odd,brigand::_1> >;
55 :
56 : //! List of member types
57 : using Data = typename Pair::first_type;
58 :
59 : //! Tuple of member types
60 : using Tuple = brigand::as_tuple< Data >;
61 :
62 : //! False-overload for detecting if T is a tagged tuple
63 : template< typename T, typename = std::void_t<> >
64 : struct is_tagged_tuple_t : std::false_type {};
65 :
66 : //! True-overload for detecting if T is a tagged tuple
67 : template< typename T >
68 : struct is_tagged_tuple_t< T, std::void_t< typename T::i_am_tagged_tuple > >
69 : : std::true_type {};
70 :
71 : //! Member data as a tuple
72 : Tuple m_members;
73 :
74 : public:
75 : //! List of key-value pairs
76 : using PairList = List;
77 :
78 : //! List of keys
79 : using Keys = typename Pair::second_type;
80 :
81 : //! Typedef defining self for identifying self
82 : using i_am_tagged_tuple = void;
83 :
84 : //! Access type in tuple behind tag
85 : template< typename Tag >
86 : using TupleElement =
87 : std::tuple_element_t< brigand::index_of<Keys,Tag>::value, Tuple >;
88 :
89 : //! Query if the type behind Tag is a TaggedTuple
90 : //! Usage: if constexpr( is_tagged_tuple<Tag>::value ) { ... }
91 : template< typename Tag >
92 : using is_tagged_tuple =
93 : is_tagged_tuple_t< std::decay_t< TupleElement<Tag> > >;
94 :
95 : //! Default constructor
96 32268 : explicit TaggedTuple() = default;
97 : //! Initializer constructor
98 949 : explicit TaggedTuple( Tuple&& tuple ) : m_members( std::move(tuple) ) {}
99 :
100 : //! Const-ref access to member tuple
101 1 : const Tuple& tuple() const { return m_members; }
102 :
103 : //! Const-reference data member accessor of field of tagged tuple at depth
104 : template< typename Tag, typename... Tags >
105 1111705 : const auto& get() const noexcept {
106 1111705 : constexpr std::size_t idx = brigand::index_of< Keys, Tag >::value;
107 : if constexpr( is_tagged_tuple<Tag>::value and sizeof...(Tags) != 0 )
108 : return std::get< idx >( m_members ).template get< Tags... >();
109 : else
110 1111705 : return std::get< idx >( m_members );
111 : }
112 :
113 : //! Reference data member accessor of field of tagged tuple at depth
114 : template< typename Tag, typename... Tags >
115 622981610 : auto& get() noexcept {
116 622981610 : constexpr std::size_t idx = brigand::index_of< Keys, Tag >::value;
117 : if constexpr( is_tagged_tuple<Tag>::value and sizeof...(Tags) != 0 )
118 6968810 : return std::get< idx >( m_members ).template get< Tags... >();
119 : else
120 616012800 : return std::get< idx >( m_members );
121 : }
122 :
123 : //! Operator == between two TaggedTuple objects
124 : //! \tparam L Type list as brigand::list for other TaggedTuple
125 : //! \return True if the lhs and rhs equal
126 : template< typename L >
127 1 : bool operator== ( const TaggedTuple< L >& t ) const {
128 : static_assert( std::is_same_v< L, List >, "Invoking operator== on "
129 : "TaggedTuple objects with different typelists" );
130 : static_assert( !brigand::any< List,
131 : std::is_floating_point<brigand::_1> >::value, "Invoking operator== on "
132 : "TaggedTuple objects containing a floating point type is unreliable" );
133 1 : return m_members == t.tuple();
134 : }
135 :
136 : //! Operator < between two TaggedTuple objects
137 : //! \tparam L Type list as brigand::list for other TaggedTuple
138 : //! \return True if lhs < rhs
139 : template< typename L >
140 : bool operator< ( const TaggedTuple< L >& t ) const {
141 : static_assert( std::is_same_v< L, List >, "Invoking operator< on "
142 : "TaggedTuple objects with different typelists" );
143 : return m_members < t.tuple();
144 : }
145 :
146 : //! Return number of tuple entries
147 1 : static constexpr std::size_t size() { return std::tuple_size_v< Tuple >; }
148 :
149 : //! Pack/Unpack
150 : /** @name Charm++ pack/unpack serializer member functions */
151 : ///@{
152 : //! \brief Pack/Unpack serialize member function
153 : //! \param[in,out] p Charm++'s PUP::er serializer object reference
154 : // cppcheck-suppress constParameter
155 83030 : void pup( PUP::er& p ) { p | m_members; }
156 : //! \brief Pack/Unpack serialize operator|
157 : //! \param[in,out] p Charm++'s PUP::er serializer object reference
158 : //! \param[in,out] t TaggedTuple object reference
159 81772 : friend void operator|( PUP::er& p, TaggedTuple<List>& t ) { t.pup(p); }
160 : //@}
161 : };
162 :
163 : } // tk::
|