├── README.md ├── intern.h ├── luple.h ├── nuple.h ├── struct-reader.h ├── test.cpp └── type-loophole.h /README.md: -------------------------------------------------------------------------------- 1 | 2 | This repository is home to separate but related projects: 3 | C++ Type Loophole, luple, nuple, C++ String Interning, Struct Reader. 4 | 5 | 6 | 7 | ## luple: A Lightweight (in terms of source code) Tuple (C++14) 8 | 9 | Header file: [luple.h][] 10 | 11 | Luple is a tuple-like data structure. Its primary advantage is that it's very thin and 12 | also non-recursive (uses multiple inheritance). std::tuple is usually implemented as a single 13 | inheritance hierarchy and as a result its layout can vary between compilers (see [this post][l]). 14 | Also it stores data in reverse order. Luple is built on multiple inheritance and its layout is 15 | consistent among compilers. It has the same layout as a non-virtual flat data structure with 16 | the same data members (formally luple is not a POD and we should keep that in mind when we 17 | reinterpret\_cast it). Luple can be used in constexpr functions if its data members are of 18 | literal type. Check the header for API description. 19 | 20 | See it in action [online at tio.run][l-tio] (also [Coliru][l-col] or [Wandbox][l-wan]). 21 | 22 | Read the header for API documentation. 23 | 24 | 25 | ## nuple: a Named Tuple (C++14) 26 | 27 | Header file: [nuple.h][] 28 | 29 | nuple extends luple and allows giving names to data members. It works by using C++ string 30 | interning (intern.h) which allows for a neat syntax. Check this [blog post][n]. 31 | 32 | See it in action [online at tio.run][n-tio] (also [Coliru][n-col] or [Wandbox][n-wan]). 33 | 34 | Also a nuple-to-json [example at tio.run][j-tio] (also [Coliru][j-col] or [Wandbox][j-wan]). 35 | 36 | Read the header for API documentation. 37 | 38 | 39 | ## C++ String Interning (C++14) 40 | 41 | Header file: [intern.h][] 42 | 43 | By turning a string literal into a type we can create an interned string in C++. This has 44 | a number of useful properties, nuple is one of them. Read more [here][i]. 45 | 46 | There is a limit on string length of 10 characters (sort of arbitrary), you can increase it by 47 | editing the $(...) macro in the intern.h header. Or #define N3599 to use [N3599][] 48 | proposal (adds string literal template to the language) which GCC and Clang implement as an 49 | extension, hopefully MSVC will support it too (Update: N3599 enabled by default). 50 | 51 | Also you can check an online example [here at tio.run][i-tio] (or at [Ideone][i-col]). 52 | 53 | Read the header for API documentation. 54 | 55 | 56 | ## Struct Reader (C++14) 57 | 58 | Header file: [struct-reader.h][] 59 | 60 | Struct Reader can detect and create a type list of the data member types of a flat data 61 | structure if they are literal, non-const/non-reference types. So, it's a form of primitive RTTI. 62 | We can turn the type list into a luple, so that means we can type cast our data to luple. 63 | Check the [blog post][b] for more details about the technique and online examples. 64 | The API description is in the header. 65 | 66 | After discovery of the Great Type Loophole there is not much value to Struct Reader, I guess. 67 | 68 | Read the header for API documentation. 69 | 70 | 71 | ## The C++ Type Loophole (C++14) 72 | 73 | Header file: [type-loophole.h][] 74 | 75 | Struct Reader is very limited in what it can do. You can't use std::string or a virtual type 76 | with it, you need to add types to a list before using them. While researching the thing I 77 | uncovered a **C++ Type Loophole**. Read more in the [blog post][e] with online examples. 78 | 79 | Read the header for API documentation. 80 | 81 | --- 82 | 83 | ## License 84 | 85 | The code in this repository is Public-domain software. 86 | 87 | ![Pubic domain software](http://alexpolt.github.io/images/public_domain_mark.png) 88 | 89 | 90 | [l]: http://alexpolt.github.io/struct-layout.html "Visual C++ Struct Layout Reminder" 91 | [b]: http://alexpolt.github.io/struct-tuple.html "Structure Data Members as a Type List Using Pure C++ (C++14)" 92 | [e]: http://alexpolt.github.io/type-loophole.html "The Great Type Loophole (C++14)" 93 | [n]: http://alexpolt.github.io/named-tuple.html "nuple: a Named Tuple" 94 | [i]: http://alexpolt.github.io/intern.html "Useful Properties of String Interning in C++" 95 | 96 | [l-tio]: https://goo.gl/pdvYee "luple Online Example at tio.run" 97 | [l-col]: http://coliru.stacked-crooked.com/a/f40d4c6f63cdf166 "luple Online Example at Coliru" 98 | [l-wan]: https://wandbox.org/permlink/Awss0a3gxcaFKN8i "luple Online Example at Wandbox" 99 | 100 | [n-tio]: https://goo.gl/zvo26z "nuple Online Example at tio.run" 101 | [n-col]: http://coliru.stacked-crooked.com/a/52984bf7d0b4db19 "nuple Online Example at Coliru" 102 | [n-wan]: https://wandbox.org/permlink/ghbTSf5LwztoyCta "nuple Online Example at Wandbox" 103 | 104 | [j-tio]: https://goo.gl/c8ofW5 "nuple Online Example at tio.run" 105 | [j-col]: http://coliru.stacked-crooked.com/a/8f2f84adae0cb751 "nuple Online Example at Coliru" 106 | [j-wan]: https://wandbox.org/permlink/NBHrlq8UJ9kDf0KS "nuple Online Example at Wandbox" 107 | 108 | [i-tio]: https://goo.gl/sBtBKn "C++ String Interning Online Example at tio.run" 109 | [i-col]: http://coliru.stacked-crooked.com/a/ad43084765b89d9c "C++ String Interning Online Example at Coliru" 110 | 111 | 112 | [luple.h]: https://github.com/alexpolt/luple/blob/master/luple.h 113 | [nuple.h]: https://github.com/alexpolt/luple/blob/master/nuple.h 114 | [intern.h]: https://github.com/alexpolt/luple/blob/master/intern.h 115 | 116 | [struct-reader.h]: https://github.com/alexpolt/luple/blob/master/struct-reader.h 117 | [type-loophole.h]: https://github.com/alexpolt/luple/blob/master/type-loophole.h 118 | 119 | [N3599]: http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3599.html "Literal operator templates for strings" 120 | 121 | 122 | -------------------------------------------------------------------------------- /intern.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | C++ String Interning 4 | 5 | Author: Alexandr Poltavsky, http://alexpolt.github.io 6 | 7 | License: Public-domain software 8 | 9 | Description: 10 | 11 | Turns a string literal into a type. 12 | 13 | Read more in the blog post http://alexpolt.github.io/intern.html 14 | 15 | The macro $(...) has a limit of 10 characters. Edit it for longer strings. 16 | 17 | Would've been easier if MSVC implemented the proposal N3599 by Richard Smith 18 | "Literal operator templates for strings". 19 | 20 | GCC and Clang implement it as an extension. This allows strings of any length. 21 | 22 | To use this extension: #define N3599 23 | 24 | Update: N3599 is now enabled by default, waiting for MSVC to implement it 25 | 26 | Dependencies: 27 | 28 | Usage: 29 | 30 | 1. interning 31 | 32 | std::cout << $("an interned string")::value; 33 | 34 | 2. as a template parameter 35 | 36 | some_template< $("tag") > 37 | 38 | 3. overload resolution 39 | 40 | void method( $("apple") ); 41 | void method( $("orange") ); 42 | 43 | method( $("apple"){} ); 44 | 45 | 4. nuple - a named tuple (nuple.h) 46 | 47 | nuple< $("first"), int, $("second"), float > p; 48 | 49 | get< int >(p) = 5; 50 | 51 | get< $("second") >(p) = 5.f; 52 | 53 | and other things like linking by name, string processing, etc. 54 | (see the proposal and read my blog post). 55 | 56 | */ 57 | 58 | 59 | //Use N3599 proposal by default on GCC and Clang 60 | 61 | #define N3599 62 | 63 | 64 | namespace intern { 65 | 66 | template struct string { 67 | 68 | static constexpr char const value[ sizeof...(NN) ]{NN...}; 69 | 70 | static_assert( value[ sizeof...(NN) - 1 ] == '\0', "interned string was too long, see $(...) macro" ); 71 | 72 | static constexpr auto data() { return value; } 73 | }; 74 | 75 | template constexpr char const string::value[]; 76 | 77 | template 78 | constexpr char ch ( char const(&s)[N], int i ) { return i < N ? s[i] : '\0'; } 79 | 80 | template struct is_string { 81 | static const bool value = false; 82 | }; 83 | 84 | template struct is_string< string > { 85 | static const bool value = true; 86 | }; 87 | 88 | } 89 | 90 | 91 | #if !defined( _MSC_VER ) && defined( N3599 ) 92 | 93 | template 94 | auto operator ""_intern() { 95 | return intern::string{}; 96 | } 97 | 98 | #define $( s ) decltype( s ## _intern ) 99 | 100 | #else 101 | 102 | //prefixing macros with a $ helps with readability 103 | #define $c( a, b ) intern::ch( a, b ) 104 | 105 | //10 characters + '\0', add $c(...) for bigger strings 106 | #define $( s ) intern::string< $c(s,0),$c(s,1),$c(s,2),$c(s,3),$c(s,4),$c(s,5),$c(s,6),$c(s,7),$c(s,8),$c(s,9),$c(s,10) > 107 | 108 | #endif 109 | 110 | 111 | -------------------------------------------------------------------------------- /luple.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | luple: a Lightweight Tuple (C++14) 4 | 5 | Author: Alexandr Poltavsky, http://alexpolt.github.io 6 | 7 | License: Public-domain software 8 | 9 | Description: 10 | 11 | Luple is a tuple-like data structure. Its primary advantage is that it's very thin and 12 | also non-recursive: uses multiple inheritance. Using multiple inheritance has the advantage 13 | of having the same layout as a non-virtual flat data structure with the same data members. 14 | With single inheritance compilers pack data in different ways. 15 | 16 | Still, luple is not a POD, so you're on your own if you reinterpret_cast it. Luple can be 17 | used in constexpr functions. 18 | 19 | Initially it was created as part of a structure data member types reading experiment. 20 | Check the struct-reader.h file for more details. 21 | 22 | Dependencies: 23 | 24 | utility: std::integer_sequence, std::forward, std::move 25 | type_traits: std::conditional_t, std::is_same, std::enable_if 26 | 27 | Usage: 28 | 29 | #include "luple.h" 30 | 31 | Construction: 32 | 33 | //luple<...> 34 | 35 | using person_t = luple< char const*, int id >; 36 | 37 | person_t p[] = { { "alex", 0 }, { "ivan", 1 } }; 38 | 39 | luple< std::string, int > l0{ "test", 10 }; 40 | luple< std::string, int > l0{ std::string{"test"}, 20 }; 41 | 42 | luple< std::string, int > l1{ l0 }; 43 | luple< std::string, int > l1{ std::move( l0 ) }; 44 | 45 | 46 | //luple_t< type_list<...> > 47 | 48 | //it's sometimes more useful to deal with a single T parameter 49 | //rather dealing with a parameter pack TT... 50 | 51 | using tlist = luple_ns::type_list< int, int, int >; 52 | 53 | luple_t< tlist > obj{ 1, 2, 3 }; 54 | 55 | template 56 | void print( luple_t< T > const& ) { ... } 57 | 58 | Assigment: 59 | 60 | l1 = l0; 61 | l1 = std::move( l0 ); 62 | 63 | Accesing data: 64 | 65 | using foo_t = luple< int, float, std::string >; 66 | 67 | foo_t f0{ 10, 10.f, "hello world" }; 68 | 69 | auto& n = get< 0 >( f0 ); 70 | auto& str = get< std::string >( f0 ); 71 | get< float >( f0 ) = 20.f; 72 | 73 | auto sz = size( f0 ); 74 | 75 | int int_index = index< std::string >( f0 ); 76 | 77 | static_assert( int_index != -1 , "not found" ); 78 | 79 | using element1_t = luple_ns::element_t< foo_t, 2 >; 80 | 81 | element1_t str{ "hello" }; 82 | 83 | luple_do( f0, []( auto& value ) { std::cout << value << ", "; } ); 84 | 85 | Comparison: 86 | 87 | using person_t = luple< char const*, int id >; 88 | 89 | person_t p[] = { { "alex", 0 }, { "ivan", 1 } }; 90 | 91 | bool less = p[0] < p[1]; 92 | bool equal = p[0] == p[1]; 93 | 94 | luple_tie ( similar to std::tie ): 95 | 96 | chat const* a; 97 | int b; 98 | 99 | luple_tie( a, b ) = p[0]; 100 | 101 | bool equal = luple_tie( a, b ) == luple_tie( a, b ); 102 | 103 | as_luple ( similar to make_tuple ): 104 | 105 | auto get_person( int id ) { 106 | 107 | return as_luple( std::string{ "alex"}, id ); 108 | } 109 | 110 | 111 | */ 112 | 113 | #ifndef LUPLE_LUPLE_H 114 | #define LUPLE_LUPLE_H 115 | 116 | #include 117 | #include 118 | 119 | 120 | namespace luple_ns { 121 | 122 | 123 | //type list 124 | template struct type_list { 125 | 126 | static const int size = sizeof...(TT); 127 | 128 | template struct add { 129 | 130 | using type = type_list< TT..., UU... >; 131 | }; 132 | 133 | }; 134 | 135 | 136 | //get element type by index 137 | template struct tlist_get; 138 | 139 | template struct tlist_get< type_list, N, M > { 140 | 141 | static_assert(N < (int) sizeof...(TT)+1 + M, "type index out of bounds"); 142 | 143 | using type = std::conditional_t< N == M, T, typename tlist_get< type_list, N, M + 1 >::type >; 144 | }; 145 | 146 | template struct tlist_get< type_list<>, N, M > { using type = void; }; 147 | 148 | template struct tlist_get< type_list<>, N, 0 > {}; 149 | 150 | template 151 | using tlist_get_t = typename tlist_get::type; 152 | 153 | 154 | //get element index by type 155 | template struct tlist_get_n; 156 | 157 | template struct tlist_get_n< type_list, U, N > { 158 | 159 | static const int value = std::is_same< T, U >::value ? N : tlist_get_n< type_list, U, N + 1 >::value; 160 | }; 161 | 162 | template struct tlist_get_n< type_list<>, U, N > { 163 | 164 | static const int value = -1; 165 | }; 166 | 167 | 168 | //helper template to check for a reference in a parameter pack 169 | template struct has_reference; 170 | 171 | template 172 | struct has_reference< T, TT... > : has_reference< TT... > {}; 173 | 174 | template 175 | struct has_reference< T &, TT... > { 176 | 177 | static const bool value = true; 178 | }; 179 | 180 | template 181 | struct has_reference< T &&, TT... > { 182 | 183 | static const bool value = true; 184 | }; 185 | 186 | template<> 187 | struct has_reference<> { 188 | 189 | static const bool value = false; 190 | }; 191 | 192 | 193 | //forward declaration 194 | template struct luple_t; 195 | 196 | 197 | //for sfinae 198 | template struct is_luple { 199 | 200 | static const bool value = false; 201 | }; 202 | 203 | template struct is_luple< luple_t > { 204 | 205 | static const bool value = true; 206 | }; 207 | 208 | 209 | //a building block that is used in multiple inheritane 210 | template struct luple_element { 211 | 212 | using value_type = tlist_get_t; 213 | 214 | value_type _value; 215 | }; 216 | 217 | 218 | //base of luple and also parent of luple_element's 219 | template struct luple_base; 220 | 221 | template 222 | struct luple_base< type_list, std::integer_sequence > : luple_element< type_list, NN >... { 223 | 224 | using tlist = type_list; 225 | 226 | //construction 227 | constexpr luple_base () {} 228 | 229 | template 230 | constexpr luple_base ( UU &&... args ) : luple_element< tlist, NN >{ std::forward( args ) }... {} 231 | 232 | //converting construction 233 | template 234 | constexpr luple_base ( luple_t const & o ) : luple_element< tlist, NN >{ TT( o.template get() ) }... {} 235 | 236 | template 237 | constexpr luple_base ( luple_t && o ) : luple_element< tlist, NN >{ TT( std::move( o.template get() ) ) }... { 238 | 239 | static_assert( ! has_reference::value, "a converting constructor can't be used with reference template parameters" ); 240 | } 241 | 242 | }; 243 | 244 | 245 | //luple implementation, T - type_list< ... > 246 | 247 | template struct luple_t : luple_base< T, std::make_integer_sequence > { 248 | 249 | using type_list = T; 250 | using base = luple_base< T, std::make_integer_sequence< int, T::size > >; 251 | 252 | static const int size = T::size; 253 | 254 | //constructing 255 | constexpr luple_t () {} 256 | 257 | template 258 | constexpr luple_t ( UU &&... args ) : base{ std::forward( args )... } { 259 | 260 | static_assert( sizeof...(UU) == size, "wrong number of arguments" ); 261 | } 262 | 263 | //converting construction 264 | template 265 | constexpr luple_t ( luple_t & o ) : luple_t{ const_cast< luple_t const & >( o ) } {} 266 | 267 | template 268 | constexpr luple_t ( luple_t const & o ) : base{ o } { 269 | 270 | static_assert( U::size == size, "sizes of luples do not match" ); 271 | } 272 | 273 | template 274 | constexpr luple_t ( luple_t && o ) : base{ std::move( o ) } { 275 | 276 | static_assert( U::size == size, "sizes of luples do not match" ); 277 | } 278 | 279 | //copying a different luple 280 | template 281 | auto & operator= ( luple_t const & r ) { 282 | 283 | static_assert( size == U::size, "sizes of luples do not match" ); 284 | 285 | return assign_( r, std::make_integer_sequence< int, size >{} ); 286 | } 287 | 288 | template 289 | auto & assign_ ( luple_t< U > const & r, std::integer_sequence< int, NN... > ) { 290 | 291 | char dummy[] = { ( get< NN >() = r.template get< NN >(), char{} )... }; 292 | (void) dummy; 293 | 294 | return *this; 295 | } 296 | 297 | //moving a different luple 298 | template 299 | auto & operator= ( luple_t && r ) { 300 | 301 | static_assert( size == U::size, "sizes of luples do not match" ); 302 | 303 | return assign_( std::move( r ), std::make_integer_sequence< int, size >{} ); 304 | } 305 | 306 | template 307 | auto & assign_ ( luple_t< U > && r, std::integer_sequence< int, NN... > ) { 308 | 309 | char dummy[] = { ( get< NN >() = std::move( r.template get< NN >() ), char{} )... }; 310 | (void) dummy; 311 | 312 | return *this; 313 | } 314 | 315 | //accessing data 316 | template constexpr auto & get () { 317 | 318 | static_assert( N < size, "luple::get -> out of bounds access" ); 319 | 320 | return luple_element< T, N >::_value; 321 | } 322 | 323 | template constexpr auto & get () { 324 | 325 | static_assert( tlist_get_n::value != -1, "no such type in type list" ); 326 | 327 | return luple_element< T, tlist_get_n< T, U >::value >::_value; 328 | } 329 | 330 | template constexpr auto & get () const { 331 | 332 | static_assert( N < T::size, "luple::get -> out of bounds access" ); 333 | 334 | return luple_element< T, N >::_value; 335 | } 336 | 337 | template constexpr auto & get () const { 338 | 339 | static_assert( tlist_get_n< T, U >::value != -1, "no such type in type list" ); 340 | 341 | return luple_element< T, tlist_get_n< T, U >::value >::_value; 342 | } 343 | 344 | }; 345 | 346 | //template alias to wrap types into type_list 347 | 348 | template 349 | using luple = luple_t< type_list< TT... > >; 350 | 351 | 352 | //get function helpers 353 | 354 | template constexpr auto & get ( luple_t & t ) { return t.template get(); } 355 | template constexpr auto & get ( luple_t & t ) { return t.template get(); } 356 | 357 | template constexpr auto & get ( luple_t const & t ) { return t.template get(); } 358 | template constexpr auto & get ( luple_t const & t ) { return t.template get(); } 359 | 360 | 361 | //luple size 362 | 363 | template constexpr auto size ( luple_t const & ) { return T::size; } 364 | 365 | 366 | //member index from type 367 | 368 | template constexpr auto index ( luple_t const & ) { return tlist_get_n< T, U >::value; } 369 | 370 | 371 | //type for index 372 | 373 | template 374 | using element_t = tlist_get_t< typename T::type_list, N >; 375 | 376 | 377 | //helper to run code for every member of luple 378 | 379 | template 380 | constexpr void luple_do_impl ( std::integer_sequence, T0 & t, T1 fn ) { 381 | 382 | //in C++17 we got folding expressions 383 | char dummy[] = { ( fn( get(t) ), char{} )... }; 384 | 385 | (void)dummy; 386 | } 387 | 388 | 389 | //helper to run code for every member of luple 390 | 391 | template 392 | constexpr void luple_do ( T0 & t, T1 fn ) { 393 | 394 | luple_do_impl( std::make_integer_sequence< int, T0::type_list::size >{}, t, fn ); 395 | } 396 | 397 | 398 | //tie 399 | 400 | template 401 | constexpr auto luple_tie ( TT &... args ) { 402 | 403 | return luple< TT &... >{ args... }; 404 | } 405 | 406 | 407 | //as_luple( value0, value1 ... ) -> luple< decltype(value0), decltype(value1) ... > 408 | 409 | template 410 | constexpr auto as_luple ( TT... args ) { 411 | 412 | return luple< TT... >{ std::move( args )... }; 413 | } 414 | 415 | 416 | //relational operators helpers 417 | 418 | template> 419 | constexpr bool luple_cmp_less ( T &, U & ) { return false; } 420 | 421 | template> 422 | constexpr bool luple_cmp_less ( luple_t< T > const & a, luple_t< U > const & b ) { 423 | 424 | bool less = get( a ) < get( b ); 425 | bool equal = get( a ) == get( b ); 426 | 427 | return less ? true : ( equal ? luple_cmp_less< N+1 >( a, b ) : false ); 428 | } 429 | 430 | template> 431 | constexpr bool luple_cmp_equal ( T &, U & ) { return true; } 432 | 433 | template> 434 | constexpr bool luple_cmp_equal ( luple_t const & a, luple_t const & b ) { 435 | 436 | bool equal = get( a ) == get( b ); 437 | 438 | return equal ? luple_cmp_equal< N + 1 >( a, b ) : false; 439 | } 440 | 441 | 442 | //relational operators 443 | 444 | template 445 | constexpr bool operator < ( luple_t const & a, luple_t const & b ) { 446 | 447 | static_assert( T::size > 0 && T::size == U::size, "sizes of luples don't match" ); 448 | 449 | return luple_cmp_less<0>( a, b ); 450 | } 451 | 452 | template 453 | constexpr bool operator == ( luple_t const & a, luple_t const & b ) { 454 | 455 | static_assert( T::size > 0 && T::size == U::size, "sizes of luples don't match" ); 456 | 457 | return luple_cmp_equal<0>( a, b ); 458 | } 459 | 460 | 461 | //the rest are easy 462 | 463 | template 464 | constexpr bool operator != ( luple_t const & a, luple_t const & b ) { return !( a == b ); } 465 | 466 | template 467 | constexpr bool operator > ( luple_t const & a, luple_t const & b ) { return b < a; } 468 | 469 | template 470 | constexpr bool operator <= ( luple_t const & a, luple_t const & b ) { return !( a > b ); } 471 | 472 | template 473 | constexpr bool operator >= ( luple_t const & a, luple_t const & b ) { return !( a < b ); } 474 | 475 | 476 | //swap 477 | 478 | template 479 | constexpr void swap( luple_t & l, luple_t & r ) { 480 | 481 | auto tmp = std::move( l ); 482 | l = std::move( r ); 483 | r = std::move( tmp ); 484 | } 485 | 486 | } 487 | 488 | 489 | //import into global namespace 490 | 491 | using luple_ns::luple; 492 | using luple_ns::luple_t; 493 | using luple_ns::get; 494 | using luple_ns::index; 495 | using luple_ns::luple_tie; 496 | using luple_ns::luple_do; 497 | using luple_ns::as_luple; 498 | 499 | #endif // LUPLE_LUPLE_H 500 | -------------------------------------------------------------------------------- /nuple.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | nuple: a named tuple (C++14) 4 | 5 | Author: Alexandr Poltavsky, http://alexpolt.github.io 6 | 7 | License: Public-domain software 8 | 9 | Description: 10 | 11 | nuple is a named tuple implementation. String interning (intern.h) makes for a neat 12 | interface. There is a blog post http://alexpolt.github.io/named-tuple.html 13 | 14 | Dependencies: 15 | 16 | luple.h: luple (lightweight tuple) 17 | intern.h: C++ string interning 18 | type_traits: std::enable_if 19 | 20 | Usage: 21 | 22 | #include "nuple.h" 23 | 24 | using nameid_t = nuple< $("name"), char const*, $("id"), int >; //for $(...) - see intern.h header 25 | 26 | 27 | nameid_t n[] = { {"alex", 1}, {"ivan", 2} }; 28 | 29 | for( auto const& v : n ) 30 | 31 | printf( "name: %s, id: %d\n", get<$("name")>(v), get<$("id")>(v) ); 32 | 33 | 34 | auto get_person ( int i ) { return nameid_t{ "john", i }; } 35 | 36 | //or - return nuple<$("name"), char const*, $("id"), int>{ "john", i }; 37 | 38 | //or - return as_nuple( $name("name"), "john", $name("id"), i ); 39 | 40 | 41 | auto p = get_person( 3 ); 42 | 43 | printf( "name: %s, id: %d\n", get<$("name")>( p ), get<$("id")>( p ) ); 44 | 45 | 46 | //as_nuple(...), similar to make_tuple, macro is $name(...) instead of $(...) 47 | 48 | auto get_person( int id ) { 49 | 50 | return as_nuple( $name("name"), std::string{ "john" }, $name("id"), id ); 51 | } 52 | 53 | 54 | //nuple extends luple, so luple methods work as usual (as in tuple) 55 | 56 | printf( "luple size: %d\n", size( p ) ); 57 | 58 | get<0>( p ) = "irene"; //or get<$("name")>( p ) = "irene"; 59 | 60 | get( p ) = 4; //or get<$("id")>( p ) = 4; 61 | 62 | bool is_equal = p == p; 63 | 64 | //see luple.h for more examples 65 | 66 | 67 | // nuple_ns::nuple_t - return type for tag name by index 68 | 69 | using field0_t = nuple_ns::name_t< nameid_t, 0 >; //the same as $("name") 70 | 71 | */ 72 | 73 | 74 | #include "luple.h" 75 | #include "intern.h" 76 | 77 | 78 | namespace nuple_ns { 79 | 80 | 81 | //filter template sorts nuple parameters into two lists: 82 | //types (passed to luple) and names (used for looking up member index) 83 | 84 | template struct filter_; 85 | 86 | template 87 | struct filter_< TL, NL, -1, T, TT... > : 88 | filter_< TL, NL, intern::is_string< T >::value, T, TT... > {}; 89 | 90 | template 91 | struct filter_< TL, NL, true, T, TT... > : 92 | filter_< TL, typename NL::template add::type, -1, TT... > {}; 93 | 94 | template 95 | struct filter_< TL, NL, false, T, TT... > : 96 | filter_< typename TL::template add::type, NL, -1, TT... > {}; 97 | 98 | template struct filter_< TL, NL, is_str > { 99 | using nlist = NL; 100 | using tlist = TL; 101 | }; 102 | 103 | template 104 | using filter = filter_< luple_ns::type_list<>, luple_ns::type_list<>, -1, TT... >; 105 | 106 | 107 | //nuple is just a thin layer on top of luple 108 | 109 | template 110 | struct nuple : luple_t< typename filter::tlist > { 111 | 112 | using name_list = typename filter::nlist; 113 | using base = luple_t< typename filter::tlist >; 114 | 115 | static_assert( name_list::size == base::type_list::size, "name and type list sizes don't match" ); 116 | 117 | using base::base; 118 | }; 119 | 120 | //turn a nuple member name into an index and call get< index >( ... ) 121 | 122 | template::value >, typename... TT> 123 | constexpr auto & get ( nuple & t ) { 124 | 125 | static_assert( luple_ns::tlist_get_n< typename nuple::name_list, T>::value != -1, "no such nuple name" ); 126 | 127 | return get< luple_ns::tlist_get_n< typename nuple::name_list, T >::value >( t ); 128 | } 129 | 130 | template::value >, typename... TT> 131 | constexpr auto & get ( nuple const & t ) { 132 | 133 | static_assert( luple_ns::tlist_get_n< typename nuple::name_list, T>::value != -1, "no such nuple name" ); 134 | 135 | return get< luple_ns::tlist_get_n< typename nuple::name_list, T >::value >( t ); 136 | } 137 | 138 | 139 | //nuple member name (interned string) for index 140 | template 141 | using name_t = luple_ns::tlist_get_t< typename T::name_list, N >; 142 | 143 | 144 | //as_nuple( ... ) 145 | 146 | #define $name(s) $(s){} 147 | 148 | template 149 | struct check_args { 150 | 151 | static const bool value = false; 152 | }; 153 | 154 | template 155 | struct check_args< luple_ns::type_list, N, 156 | 157 | std::enable_if_t< ( N%2 > 0 ) || ( N%2 == 0 && intern::is_string::value ) >> { 158 | 159 | static const bool value = true && check_args< luple_ns::type_list, N + 1 >::value; 160 | }; 161 | 162 | template 163 | struct check_args< luple_ns::type_list<>, N, void> { 164 | 165 | static const bool value = true; 166 | }; 167 | 168 | 169 | template 170 | auto as_nuple_( luple l, std::integer_sequence< int, NN... > ) { 171 | 172 | using param_list = luple_ns::type_list; 173 | 174 | static_assert( check_args::value, "order of arguments should be name, value..." ); 175 | 176 | return nuple< TT... >{ std::move( get( l ) )... }; 177 | } 178 | 179 | //as_nuple( $name("..."), value, ... ) -> nuple< $(...), decltype( value ), ... > 180 | //note the difference between $name(..) and $(...) 181 | 182 | template 183 | auto as_nuple( TT... args ) { 184 | 185 | static_assert( sizeof...(TT) % 2 == 0, "wrong number of arguments"); 186 | 187 | return as_nuple_( luple< TT... >{ std::move( args )... }, std::make_integer_sequence< int, sizeof...(TT)/2 >{} ); 188 | } 189 | 190 | } 191 | 192 | //import into global namespace 193 | 194 | using nuple_ns::nuple; 195 | using nuple_ns::get; 196 | using nuple_ns::as_nuple; 197 | 198 | 199 | -------------------------------------------------------------------------------- /struct-reader.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Struct Reader (C++14) 4 | 5 | Author: Alexandr Poltavsky, http://alexpolt.github.io 6 | 7 | License: Public-domain software 8 | 9 | Description: 10 | 11 | Reads the types of data members of a struct and returns a type list. A dozen lines of code. 12 | Limited to literal/non-const/non-literal/non-array types (arrays are unfold into separate members). 13 | All built-in C++ scalar types (bool, int, float, etc.), pointers to them (including to const 14 | variants) are supported. To support your custom type add it to type_list_t in the header. 15 | 16 | Read more in a blog post: http://alexpolt.github.io/struct-tuple.html 17 | 18 | UPD. Later I've come up with an even more sophisticated hack called The Great Type Loophole. 19 | check the blog post http://alexpolt.github.io/type-loophole.html 20 | 21 | Dependencies: 22 | 23 | luple.h (a lightweight tuple): luple, luple_t, luple_ns::type_list, luple_ns::tlist_get_n 24 | utility: std::integer_sequence 25 | type_traits: std::conditional_t, std::remove_pointer_t, std::remove_const_t, std::is_pointer, 26 | std::is_const, std::add_const_t, std::add_pointer_t 27 | 28 | Usage: 29 | 30 | #include "struct-reader.h" 31 | 32 | struct data { 33 | int a; 34 | char const* b; 35 | float c; 36 | //my_class d; //for my_class to be recognized you need to add it to struct_reader::type_list_t 37 | }; 38 | 39 | using data_tlist = struct_reader::as_type_list< data >; //returns luple_ns::type_list<...> 40 | 41 | //luple_t<...> - takes luple_ns::type_list< list of types >, luple<...> takes a list of types directly 42 | //for more information refer to luple.h 43 | 44 | using data_luple = luple_t< data_tlist >; 45 | 46 | data d{ 1, "test", 1.f }; 47 | 48 | auto& l = *reinterpret_cast< data_luple* >( &d ); 49 | 50 | //data_luple is not a POD but its layout matches struct data on Clang, GCC and MSVC 51 | //more details is in the blog post http://alexpolt.github.io/struct-layout.html 52 | 53 | get<2>(l) = 3.f; 54 | 55 | get(l) = 10; 56 | 57 | You can find links to online working examples in the blog post 58 | http://alexpolt.github.io/struct-tuple.html 59 | 60 | */ 61 | 62 | 63 | namespace struct_reader { 64 | 65 | using namespace luple_ns; 66 | 67 | //this is the main type list, add your own types here 68 | using type_list_t = type_list< 69 | void *, bool, char, unsigned char, signed char, short, int, long, long long, 70 | unsigned short, unsigned int, unsigned long, unsigned long long, 71 | float, double, long double 72 | >; 73 | 74 | //helper to get type using a templated conversion operator 75 | template 76 | struct read_type { 77 | template 78 | constexpr operator U() { 79 | using noptr = std::remove_pointer_t; 80 | using nocon = std::remove_const_t; 81 | static_assert( tlist_get_n::value != -1 || tlist_get_n::value != -1, "no such type in type list"); 82 | constexpr int const tid = 0xFFFF, is_ptr = 1 << 16, is_con = 1 << 17; 83 | data = tlist_get_n::value; 84 | if( data == -1 ) { 85 | data = tlist_get_n::value & tid; 86 | data = data | (std::is_pointer::value ? is_ptr : 0); 87 | data = data | (std::is_const::value ? is_con : 0); 88 | } 89 | return {}; 90 | } 91 | int data; 92 | }; 93 | 94 | using read_type_t = read_type< type_list_t >; 95 | 96 | //here we're using overload resolution to get a data member type 97 | template 98 | constexpr auto get_type_id(int n) { 99 | read_type_t tid[sizeof...(N)]{}; 100 | T{ tid[N]... }; 101 | return tid[n].data; 102 | } 103 | 104 | //helper to rebuild the type 105 | template 106 | constexpr auto get_type() { 107 | using type = tlist_get_t; 108 | using ctype = std::conditional_t< (bool)is_const, std::add_const_t, type >; 109 | using ptype = std::conditional_t< (bool)is_pointer, std::add_pointer_t, ctype >; 110 | return ptype{}; 111 | } 112 | 113 | static constexpr int const tid = 0xFFFF; 114 | static constexpr int const is_ptr = 1 << 16; 115 | static constexpr int const is_con = 1 << 17; 116 | 117 | //read struct data member types and put it into a type list 118 | template 119 | constexpr auto get_type_list(std::integer_sequence) { 120 | constexpr int t[] = { get_type_id(N)... }; 121 | (void)t; // maybe unused if N == 0 122 | return type_list< decltype(get_type())...>{}; 123 | } 124 | 125 | //get fields number using expression SFINAE 126 | template 127 | constexpr int fields_number(...) { return sizeof...(N)-1; } 128 | 129 | template 130 | constexpr auto fields_number(int) -> decltype(T{ (N,read_type_t{})... }, sizeof(0)) { return fields_number(0); } 131 | 132 | //and here is our hot and fresh out of kitchen type list (alias template) 133 | template 134 | using as_type_list = decltype(get_type_list< T >(std::make_integer_sequence< int, fields_number(0) >{})); 135 | 136 | } 137 | 138 | 139 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Pavel I. Kryukov https://github.com/pavelkryukov 3 | * License: Public-domain software 4 | */ 5 | 6 | #include "luple.h" 7 | #include "struct-reader.h" 8 | #include "type-loophole.h" 9 | 10 | #include 11 | 12 | struct EmptyStruct {}; 13 | 14 | struct SimpleStructure 15 | { 16 | int val; 17 | char key; 18 | short dum; 19 | }; 20 | 21 | namespace struct_reader 22 | { 23 | static_assert(std::is_same, type_list<>>::value); 24 | static_assert(std::is_same, type_list>::value); 25 | } 26 | 27 | namespace loophole_ns 28 | { 29 | // static_assert(std::is_same, luple_ns::type_list<>>::value); Crashes VS 30 | static_assert(std::is_same, luple_ns::type_list>::value); 31 | } 32 | 33 | struct StructureWithVector 34 | { 35 | int val; 36 | std::vector storage; 37 | }; 38 | 39 | namespace loophole_ns 40 | { 41 | static_assert(std::is_same, luple_ns::type_list>>::value); 42 | } 43 | 44 | int main() 45 | { 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /type-loophole.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The Great Type Loophole (C++14) 4 | 5 | Author: Alexandr Poltavsky, http://alexpolt.github.io 6 | 7 | License: Public-domain software 8 | 9 | Description: 10 | 11 | The Great Type Loophole is a technique that allows to exchange type information with template 12 | instantiations. Basically you can assign and read type information during compile time. 13 | Here it is used to detect data members of a data type. I described it for the first time in 14 | this blog post http://alexpolt.github.io/type-loophole.html . 15 | 16 | Before this, I've created a Struct Reader to get data member types from a struct,but it can only 17 | work with literal types from a fixed list. This can detect any types in a struct (except for 18 | references and cv-qualified types) with nothing but C++ (C++14). It doesn't require a pre-built 19 | type list. It uses list initialization for this and because of that it is limited to aggregate 20 | types. You can try it on something with a constructor but it will most likely fail (especially 21 | if there are constructors with an std::initializer_list). 22 | 23 | Dependencies: 24 | 25 | luple.h (a lightweight tuple): luple_t, luple_ns::type_list (bare template with a parameter pack) 26 | utility: std::integer_sequence 27 | 28 | Usage: 29 | 30 | #include "type-loophole.h" 31 | 32 | struct test { 33 | test() {} 34 | }; 35 | 36 | struct data { 37 | test t0; 38 | std::string t1; 39 | std::vector t2; 40 | }; 41 | 42 | using data_tlist = loophole_ns::as_type_list< data >; // type_list< test, std::string, std::vector > 43 | 44 | //luple_t<...> - takes luple_ns::type_list< list of types >, luple<...> takes a list of types directly 45 | //for more information refer to luple.h 46 | 47 | using data_luple = luple_t< data_tlist >; //check luple.h for API 48 | 49 | data d{ {}, "Hello World!", {1, 2 ,3} }; 50 | 51 | auto& l = reinterpret_cast< data_luple& > 52 | 53 | auto sz = size( data_luple ); 54 | 55 | printf( "%s\n", get< std::string >( l ).data() ); 56 | 57 | for( auto i : get< 2 >( l ) ) printf( "%d, ",i ); 58 | 59 | You can find links to online working examples in the blog post 60 | http://alexpolt.github.io/type-loophole.html 61 | 62 | */ 63 | 64 | 65 | #include "luple.h" 66 | 67 | 68 | namespace loophole_ns { 69 | 70 | /* 71 | tag generates friend declarations and helps with overload resolution. 72 | There are two types: one with the auto return type, which is the way we read types later. 73 | The second one is used in the detection of instantiations without which we'd get multiple 74 | definitions. 75 | */ 76 | 77 | template 78 | struct tag { 79 | friend auto loophole(tag); 80 | constexpr friend int cloophole(tag); 81 | }; 82 | 83 | 84 | /* 85 | The definitions of friend functions. 86 | */ 87 | template 88 | struct fn_def { 89 | friend auto loophole(tag) { return U{}; } 90 | constexpr friend int cloophole(tag) { return 0; } 91 | }; 92 | 93 | /* 94 | This specialization is to avoid multiple definition errors. 95 | */ 96 | template 97 | struct fn_def {}; 98 | 99 | /* 100 | This has a templated conversion operator which in turn triggers instantiations. 101 | Important point, using sizeof seems to be more reliable. Also default template 102 | arguments are "cached" (I think). To fix that I provide a U template parameter to 103 | the ins functions which do the detection using constexpr friend functions and SFINAE. 104 | */ 105 | template 106 | struct c_op { 107 | template static auto ins(...) -> int; 108 | template{}) > static auto ins(int) -> char; 109 | 110 | template(0)) == sizeof(char)>)> 111 | operator U(); 112 | }; 113 | 114 | /* 115 | Here we detect the data type field number. The byproduct is instantiations. 116 | Uses list initialization. Won't work for types with user provided constructors. 117 | In C++17 there is std::is_aggregate which can be added later. 118 | */ 119 | 120 | template 121 | constexpr int fields_number(...) { return sizeof...(NN)-1; } 122 | 123 | template 124 | constexpr auto fields_number(int) -> decltype(T{ c_op{}... }, 0) { 125 | return fields_number(0); 126 | } 127 | 128 | /* 129 | This is a helper to turn a data structure into a type list. 130 | Usage is: loophole_ns::as_type_list< data_t > 131 | I keep dependency on luple (a lightweight tuple of my design) because it's handy 132 | to turn a structure into luple (tuple). luple has the advantage of more stable layout 133 | across compilers and we can reinterpret_cast between the data structure and luple. 134 | More details are in the luple.h header. 135 | */ 136 | 137 | template 138 | struct loophole_type_list; 139 | 140 | template 141 | struct loophole_type_list< T, std::integer_sequence > { 142 | using type = luple_ns::type_list< decltype(loophole(tag{}))... >; 143 | }; 144 | 145 | template 146 | using as_type_list = 147 | typename loophole_type_list(0)>>::type; 148 | 149 | } 150 | 151 | 152 | --------------------------------------------------------------------------------