├── .gitignore ├── LICENSE ├── README.md └── StructToTuple ├── CMakeLists.txt ├── StructToTuple.cpp └── StructToTuple.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | *.vs/ 35 | out/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Rhidian De Wit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StructToTuple 2 | A C++20 header-only library to convert Structs to Tuples, and Tuples to Structs! 3 | Nested structs and tuples are also fully supported! 4 | 5 | The functions are fully `constexpr` so they can be used at both compile- and runtime. A C++20 compiler is required, but changes to make it C++17 friendly should be very simple. 6 | 7 | ## Usage 8 | 9 | Converting any Struct to a std::tuple is as simple as calling `StT::StructToTuple(struct)` 10 | ```cpp 11 | struct FooBar 12 | { 13 | int a; 14 | int b; 15 | int c; 16 | }; 17 | auto [a, b, c] = StT::StructToTuple(FooBar{}); 18 | std::tuple = StT::StructToTuple(FooBar{}); 19 | ``` 20 | 21 | Converting any tuple to a struct is as simple as calling `StT::TupleToStruct(tuple)`. Note that the Struct must be given as a template parameter 22 | ```cpp 23 | struct FooBar 24 | { 25 | int a; 26 | int b; 27 | int c; 28 | }; 29 | FooBar foo = StT::TupleToStruct(std::tuple()); 30 | ``` 31 | -------------------------------------------------------------------------------- /StructToTuple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.12) 2 | 3 | project ("StructToTuple") 4 | 5 | add_executable (StructToTuple "StructToTuple.cpp") 6 | 7 | set_property(TARGET StructToTuple PROPERTY CXX_STANDARD 20) -------------------------------------------------------------------------------- /StructToTuple/StructToTuple.cpp: -------------------------------------------------------------------------------- 1 | #include "StructToTuple.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace StT; 7 | 8 | struct Foo 9 | { 10 | int A; 11 | float B; 12 | }; 13 | 14 | struct Bar 15 | { 16 | std::string A; 17 | Foo B; 18 | }; 19 | 20 | struct Baz 21 | { 22 | bool A; 23 | int B; 24 | }; 25 | 26 | struct FooBar 27 | { 28 | Baz A; 29 | Baz B; 30 | std::vector C; 31 | }; 32 | 33 | #define ASSERT(expr) static_assert(expr); assert(expr); 34 | 35 | int main() 36 | { 37 | // =================================================================== 38 | // =================================================================== 39 | // TEST STRUCT TO TUPLE 40 | // =================================================================== 41 | // =================================================================== 42 | ASSERT(std::get<0>(StructToTuple(Foo{ 42, 85.5415f })) == 42); 43 | ASSERT(std::get<1>(StructToTuple(Foo{ 42, 85.5415f })) == 85.5415f); 44 | 45 | ASSERT(std::get<0>(StructToTuple(Bar{ "Hello World!", Foo{15, 42.f} })) == "Hello World!"); 46 | ASSERT(std::get<0>(std::get<1>(StructToTuple(Bar{ "Hello World!", Foo{15, 42.f} }))) == 15); 47 | ASSERT(std::get<1>(std::get<1>(StructToTuple(Bar{ "Hello World!", Foo{15, 42.f} }))) == 42.f); 48 | 49 | ASSERT(std::get<0>(std::get<0>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))) == true); 50 | ASSERT(std::get<1>(std::get<0>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))) == 3); 51 | ASSERT(std::get<0>(std::get<1>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))) == false); 52 | ASSERT(std::get<1>(std::get<1>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))) == 5); 53 | ASSERT(std::get<0>(std::get<2>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))[0]) == true); 54 | ASSERT(std::get<1>(std::get<2>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))[0]) == 6); 55 | ASSERT(std::get<0>(std::get<2>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))[1]) == true); 56 | ASSERT(std::get<1>(std::get<2>(StructToTuple(FooBar{ Baz{true, 3}, Baz{false, 5}, std::vector{{true, 6}, {true,9} } }))[1]) == 9); 57 | 58 | 59 | 60 | // =================================================================== 61 | // =================================================================== 62 | // TEST TUPLE TO STRUCT 63 | // =================================================================== 64 | // =================================================================== 65 | ASSERT(TupleToStruct(std::tuple(42, 85.5415f)).A == 42); 66 | ASSERT(TupleToStruct(std::tuple(42, 85.5415f)).B == 85.5415f); 67 | 68 | using BarTuple = std::tuple>; 69 | 70 | ASSERT(TupleToStruct(BarTuple("Hello World!", { 15, 42.f })).A == "Hello World!"); 71 | ASSERT(TupleToStruct(BarTuple("Hello World!", { 15, 42.f })).B.A == 15); 72 | ASSERT(TupleToStruct(BarTuple("Hello World!", { 15, 42.f })).B.B == 42.f); 73 | 74 | using FooBarTuple = std::tuple, std::tuple, std::vector>>; 75 | 76 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).A.A == true); 77 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).A.B == 3); 78 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).B.A == false); 79 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).B.B == 5); 80 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).C[0].A == true); 81 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).C[0].B == 6); 82 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).C[1].A == true); 83 | ASSERT(TupleToStruct(FooBarTuple({ true, 3 }, { false, 5 }, { {true, 6}, { true, 9 } })).C[1].B == 9); 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /StructToTuple/StructToTuple.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace StT 8 | { 9 | template typename Template> 10 | constexpr bool IS_SPECIALISATION = false; 11 | 12 | template typename Template, typename... Args> 13 | constexpr bool IS_SPECIALISATION, Template> = true; 14 | 15 | template typename Template> 16 | concept IsSpecialisation = IS_SPECIALISATION; 17 | 18 | struct Any 19 | { 20 | template 21 | constexpr operator T() const; 22 | }; 23 | 24 | template 25 | concept Aggregate = std::is_aggregate_v; 26 | 27 | template 28 | concept AggregateInitializable = requires { T{ std::declval()... }; }; 29 | 30 | template 31 | using IndexedAny = Any; 32 | template 33 | struct AggregateInitializableFromIndices; 34 | template 35 | struct AggregateInitializableFromIndices> : std::bool_constant...>> 36 | { 37 | }; 38 | 39 | template 40 | concept AggregateInitializibleWithNArgs = Aggregate && AggregateInitializableFromIndices>::value; 41 | 42 | template 43 | struct FieldCount : FieldCount> 44 | { 45 | }; 46 | 47 | template 48 | struct FieldCount : std::integral_constant 49 | { 50 | }; 51 | 52 | template 53 | constexpr size_t FieldCount_v = FieldCount::value; 54 | 55 | template 56 | concept IsStruct = std::is_class_v && Aggregate; 57 | 58 | #define CONCAT(a, b) a##b 59 | #define GENERATE_VAR(n) CONCAT(t, n) 60 | #define STRUCT_BIND_START auto [ 61 | #define STRUCT_BIND_END ] = Struct; 62 | 63 | #define STRUCT_BIND_1 t1 64 | #define STRUCT_BIND_2 t1, t2 65 | #define STRUCT_BIND_3 t1, t2, t3 66 | #define STRUCT_BIND_4 t1, t2, t3, t4 67 | #define STRUCT_BIND_5 t1, t2, t3, t4, t5 68 | #define STRUCT_BIND_6 t1, t2, t3, t4, t5, t6 69 | #define STRUCT_BIND_7 t1, t2, t3, t4, t5, t6, t7 70 | #define STRUCT_BIND_8 t1, t2, t3, t4, t5, t6, t7, t8 71 | 72 | #define GET_BIND_MACRO(n) CONCAT(STRUCT_BIND_, n) 73 | #define STRUCT_BIND(N) \ 74 | STRUCT_BIND_START GET_BIND_MACRO(N) \ 75 | STRUCT_BIND_END \ 76 | return std::make_tuple(GET_BIND_MACRO(N)); 77 | 78 | #define GET_NTH_ELEMENT(I, N) \ 79 | STRUCT_BIND_START GET_BIND_MACRO(N) \ 80 | STRUCT_BIND_END \ 81 | return std::get(std::make_tuple(GET_BIND_MACRO(N))); 82 | 83 | // This will make a struct that looks like: 84 | /* 85 | template 86 | struct StructUnpacker 87 | { 88 | // The amount of t1, t2, ... depends on N which is given as a parameter to the macro 89 | constexpr auto operator()(T Struct) { auto [t1, t2] = Struct; return std::make_tuple(t1, t2); } 90 | }; 91 | */ 92 | #define STRUCT_UNPACKER(N) \ 93 | template \ 94 | struct StructUnpacker \ 95 | { \ 96 | constexpr auto operator()(T Struct) \ 97 | { \ 98 | STRUCT_BIND(N) \ 99 | } \ 100 | template \ 101 | static constexpr auto GetNthElement(T Struct) \ 102 | { \ 103 | GET_NTH_ELEMENT(I, N) \ 104 | } \ 105 | }; 106 | 107 | template 108 | struct StructUnpacker 109 | { 110 | static_assert(N != 0, "Can't have 0 elements in a struct"); 111 | // static_assert(false, "A new STRUCT_UNPACKER is necessary!"); 112 | }; 113 | 114 | STRUCT_UNPACKER(1) 115 | STRUCT_UNPACKER(2) 116 | STRUCT_UNPACKER(3) 117 | STRUCT_UNPACKER(4) 118 | STRUCT_UNPACKER(5) 119 | STRUCT_UNPACKER(6) 120 | STRUCT_UNPACKER(7) 121 | STRUCT_UNPACKER(8) 122 | STRUCT_UNPACKER(9) 123 | STRUCT_UNPACKER(10) 124 | STRUCT_UNPACKER(11) 125 | STRUCT_UNPACKER(12) 126 | STRUCT_UNPACKER(13) 127 | STRUCT_UNPACKER(14) 128 | STRUCT_UNPACKER(15) 129 | STRUCT_UNPACKER(16) 130 | STRUCT_UNPACKER(17) 131 | STRUCT_UNPACKER(18) 132 | STRUCT_UNPACKER(19) 133 | STRUCT_UNPACKER(20) 134 | 135 | template 136 | constexpr auto StructToTuple(T const& struct_); 137 | 138 | template 139 | constexpr std::vector StructToTupleVectorImpl(std::vector const& vec) 140 | { 141 | std::vector newVec; 142 | newVec.reserve(vec.size()); 143 | 144 | for (auto const& val : vec) 145 | { 146 | newVec.emplace_back(StructToTuple(val)); 147 | } 148 | 149 | return newVec; 150 | } 151 | 152 | template 153 | constexpr auto StructToTupleImpl(std::tuple const& tuple, std::index_sequence const&) 154 | { 155 | #define GET_TYPE(x) std::remove_cvref_t 156 | #define CURR_ELEM std::get(tuple) 157 | #define CURR_ELEM_TYPE GET_TYPE(CURR_ELEM) 158 | 159 | return std::make_tuple( 160 | [&]() 161 | { 162 | if constexpr (IsStruct) 163 | { 164 | return StructToTupleImpl(StructUnpacker> {}(CURR_ELEM), 165 | std::make_index_sequence> {}); 166 | } 167 | else if constexpr (IsSpecialisation) 168 | { 169 | #define VECTOR_TYPE typename CURR_ELEM_TYPE::value_type 170 | if constexpr (IsStruct) 171 | { 172 | return StructToTupleVectorImpl < VECTOR_TYPE, 173 | decltype(StructToTupleImpl(StructUnpacker> {}(std::declval()), 174 | std::make_index_sequence> {})) > (CURR_ELEM); 175 | } 176 | else 177 | { 178 | return CURR_ELEM; 179 | } 180 | #undef VECTOR_TYPE 181 | } 182 | else 183 | { 184 | return CURR_ELEM; 185 | } 186 | }()...); 187 | 188 | #undef CURR_ELEM 189 | #undef CURR_ELEM_TYPE 190 | } 191 | 192 | template 193 | constexpr auto StructToTuple(T const& struct_) 194 | { 195 | constexpr size_t fieldCount = FieldCount_v; 196 | return StructToTupleImpl(StructUnpacker {}(struct_), std::make_index_sequence {}); 197 | } 198 | 199 | template 200 | constexpr auto StructToTuple(std::vector const& vec) 201 | { 202 | return StructToTupleVectorImpl()))>(vec); 203 | } 204 | 205 | template 206 | constexpr T TupleToStruct(std::tuple const& tuple); 207 | 208 | template 209 | constexpr std::vector TupleToStructVectorImpl(std::vector> const& vec) 210 | { 211 | std::vector newVec; 212 | newVec.reserve(vec.size()); 213 | 214 | for (auto const& tuple : vec) 215 | { 216 | newVec.push_back(TupleToStruct(tuple)); 217 | } 218 | 219 | return newVec; 220 | } 221 | 222 | template 223 | constexpr T TupleToStructImpl(std::tuple const& tuple, std::index_sequence) 224 | { 225 | #define CURR_ELEM std::get(tuple) 226 | #define CURR_TYPE std::remove_cvref_t 227 | #define CURR_STRUCT_MEMBER_TYPE std::remove_cvref_t>::template GetNthElement(std::declval()))> 228 | return T{ [&]() 229 | { 230 | if constexpr (IsSpecialisation) 231 | { 232 | // Nested struct, we need to get the nested struct's type 233 | // We can assume that T is our 'parent' struct, and that Is is the 'index' of the member in the 'parent' struct 234 | return TupleToStructImpl(CURR_ELEM, std::make_index_sequence> {}); 235 | } 236 | else if constexpr (IsSpecialisation) 237 | { 238 | return TupleToStructVectorImpl(CURR_ELEM); 239 | } 240 | else 241 | { 242 | return std::get(tuple); 243 | } 244 | }()... }; 245 | #undef CURR_ELEM 246 | #undef CURR_TYPE 247 | #undef CURR_STRUCT_MEMBER_TYPE 248 | } 249 | 250 | template 251 | constexpr T TupleToStruct(std::tuple const& tuple) 252 | { 253 | return TupleToStructImpl(tuple, std::make_index_sequence {}); 254 | } 255 | 256 | template 257 | constexpr std::vector TupleToStruct(std::vector> const& vec) 258 | { 259 | std::vector newVec; 260 | newVec.reserve(vec.size()); 261 | 262 | for (auto const& tuple : vec) 263 | { 264 | newVec.push_back(TupleToStructImpl(tuple, std::make_index_sequence {})); 265 | } 266 | 267 | return newVec; 268 | } 269 | } // namespace StT --------------------------------------------------------------------------------