2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-22 17:32:35 +00:00

Compare commits

...

67 Commits

Author SHA1 Message Date
Antony Polukhin
26fffe6ff0 Added 'How it works' section into the docs 2016-10-14 08:59:22 +03:00
Antony Polukhin
e353fd63f2 Fixed typo 2016-10-12 22:05:27 +03:00
Antony Polukhin
a277cdac19 Minor cleanups and made fields_count() correctly work with structures that have bitfields 2016-10-08 18:04:27 +03:00
Antony Polukhin
48b86158f0 Added static_assert for fields_count() function. Now noncopy constructable classes are not allowed 2016-10-08 18:02:44 +03:00
Antony Polukhin
8e0f27baee Add more tests and try to relax POD requirement as was proposed by Anton Bikineev 2016-10-08 18:00:47 +03:00
Antony Polukhin
9c88c56cb3 Remove unused classes, drop member reference checks as was proposed by Anton Bikineev 2016-10-08 17:58:51 +03:00
Antony Polukhin
c110bdf0e6 Minor refactoring and one more test 2016-09-30 20:28:15 +03:00
Antony Polukhin
5ae2e2fd5f Optimized compilation times 2016-09-30 20:26:18 +03:00
Antony Polukhin
7c252b4ca5 Updated the motivating example 2016-09-29 22:35:22 +03:00
Antony Polukhin
a144d044df Disable clang, as it is too old in repo 2016-09-29 22:08:03 +03:00
Antony Polukhin
558734e7f8 Fixing clang compile options 2016-09-29 21:45:33 +03:00
Antony Polukhin
940cb1507f Fix testing issue with missing boost/type_index.hpp header and enable clang 2016-09-29 21:42:23 +03:00
Antony Polukhin
378acb1fe9 More tests and significant compilation time improvement 2016-09-29 21:33:24 +03:00
Antony Polukhin
c9bc21ed9c Fix all the known clang related issues 2016-09-29 21:31:42 +03:00
Antony Polukhin
d6687e5d18 Cleanups, clang related workarounds 2016-09-29 21:30:47 +03:00
Antony Polukhin
b0347fb617 Improving compilation times 2016-09-29 21:29:27 +03:00
Antony Polukhin
ac034a6ef7 Removing copypasted code and improving compilation times 2016-09-29 21:28:26 +03:00
Antony Polukhin
138a7aff2e Dropping std::tuple and fixing my own tuple implementation. std::tuple on GCC-6 was exceeding template instantiation depth limit extreamely fast 2016-09-29 21:25:59 +03:00
Antony Polukhin
12940c6ed9 Attempt to fix ineffective std::tuple_cat by implementing it from scratch 2016-09-29 21:24:17 +03:00
Antony Polukhin
282094999e Attempt to use std::tuple to avoid reinventing all the tuple functionality from scratch 2016-09-29 21:23:01 +03:00
Antony Polukhin
72c831cb1a More fixes and debugging for nested structures 2016-09-29 21:21:59 +03:00
Antony Polukhin
3dad4f6271 More tests fixed and enabled, dropped hand-written comparators in favour of std::tuple default ones 2016-09-28 22:45:01 +03:00
Antony Polukhin
46ca2b70b2 More tests enabled and more fixes 2016-09-28 22:43:47 +03:00
Antony Polukhin
558a49daee Use std::tuple in most of the places, instead adding and reinventing new functionality in our own tuple 2016-09-28 22:42:19 +03:00
Antony Polukhin
fd381c5dd4 Continuing work on correct representation of structures with tricky alignments 2016-09-28 22:40:35 +03:00
Antony Polukhin
313d667664 Fixing the compilation; still fails to compile 2016-09-26 20:45:03 +03:00
Antony Polukhin
4c2f7fbce4 Representing nested structures as nested tuples; fails to compile 2016-09-26 20:43:49 +03:00
Antony Polukhin
f4126ca464 Added test case from Lisa Lippincott and started fixing the issue 2016-09-26 20:41:45 +03:00
Antony Polukhin
217b4f9d50 Fixed typo 2016-09-07 01:37:03 +03:00
Antony Polukhin
b5dea7e0c4 Improved docs, removed unused classes 2016-09-05 21:41:53 +03:00
Antony Polukhin
79a79332ae Do not include c++17 headers by default (they are not tested yet) 2016-07-28 23:46:47 +03:00
Antony Polukhin
16e28d3c3c tuned cpp17 generator: the output is even more compact now 2016-07-28 23:45:57 +03:00
Antony Polukhin
452e01ff0b Generator updated, more compact c++17 code produced 2016-07-28 23:24:18 +03:00
Antony Polukhin
9fbb780839 Move more files to better reflect their content and usability (pod_ -> flat_) 2016-07-18 23:54:07 +03:00
Antony Polukhin
494a9cf6ca Rename more internal functions to better match their usability (removed flat prefix and moved to sequence_tuple namespace) 2016-07-18 23:51:22 +03:00
Antony Polukhin
67b1b5c06a Rename internal function to better reflect their usability in C++17 2016-07-18 23:49:21 +03:00
Antony Polukhin
212efb1be0 Added initial and untested version of C++17 functionality 2016-07-18 23:48:14 +03:00
Antony Polukhin
135faf692d Move some files and change header names 2016-07-18 23:46:45 +03:00
Antony Polukhin
9ecedfe6ca Better formatting for generated files 2016-07-18 23:42:07 +03:00
Antony Polukhin
27cd2d78e3 Initial commit of reflections based on 'structured bindings' from C++17 2016-07-14 22:38:34 +03:00
Antony Polukhin
7d9dec3b44 Fair implementation of comparison operators (now actually uses the underlying operators of POD types). More constexpr methods and adde some include guards 2016-07-14 22:35:17 +03:00
Antony Polukhin
2751725148 Changed comparisons _impl to work with sequence_tuple::tuple and made those _impl functions to constexpr 2016-07-14 22:31:16 +03:00
Antony Polukhin
4211404f20 Added tuple_size method, updated the docs and dropped some useless specializations 2016-07-14 22:29:05 +03:00
Antony Polukhin
6ef486f0b7 detail::as_tuple -> detail::as_flat_tuple 2016-07-12 22:20:55 +03:00
Antony Polukhin
2ca4142e93 Made an as_tuple function and improved some of the functions to reinterpret_cast to tuple only once 2016-07-12 21:34:05 +03:00
Antony Polukhin
c9bb66911a rename get to flat_get. Remove some usings 2016-07-12 21:32:38 +03:00
Antony Polukhin
1b0ae91a52 New library name in README 2016-06-18 17:04:35 +03:00
Antony Polukhin
74a1bb1eac More travisCI related fixes (9) 2016-06-18 14:46:54 +03:00
Antony Polukhin
b9987c70ab More travisCI related fixes (8) 2016-06-18 14:37:35 +03:00
Antony Polukhin
54f82e9482 More travisCI related fixes (7) 2016-06-18 13:35:58 +03:00
Antony Polukhin
3ee92b6a14 More travisCI related fixes (6) 2016-06-18 13:27:35 +03:00
Antony Polukhin
a83f1043f3 More travisCI related fixes (5) 2016-06-18 13:16:30 +03:00
Antony Polukhin
1405b1ab64 More travisCI related fixes (4) 2016-06-18 13:06:03 +03:00
Antony Polukhin
2854894a74 More travisCI related fixes (3) 2016-06-18 12:40:38 +03:00
Antony Polukhin
d78d300380 More travisCI related fixes (2) 2016-06-18 12:38:02 +03:00
Antony Polukhin
2284d6f703 More travisCI related fixes 2016-06-18 12:35:40 +03:00
Antony Polukhin
dc585f67fd Another travis related fix no3 2016-06-16 00:18:07 +03:00
Antony Polukhin
ebd3750681 Another travis related fix no2 2016-06-16 00:14:31 +03:00
Antony Polukhin
9fb1071862 Another travis related fix 2016-06-16 00:04:12 +03:00
Antony Polukhin
f058b9b771 Minor fix of README formatting 2016-06-15 23:58:32 +03:00
Antony Polukhin
96b0c9b7d7 Another TravisCI fix 2016-06-15 23:51:48 +03:00
Antony Polukhin
620f9f4f80 Fix travisCI tests 2016-06-15 23:41:30 +03:00
Antony Polukhin
36d22fe4de Updated docs, examples and polished headers 2016-06-15 23:30:56 +03:00
Antony Polukhin
efe421e982 Link to the docs provided 2016-06-09 23:10:35 +03:00
Antony Polukhin
db967a38a8 More experiments with pod_ops, more tests and docs 2016-06-09 23:05:07 +03:00
Antony Polukhin
11aa87e21f More docs, more Boost related modifications, more examples and tests. Work on rel_ops is going on 2016-06-08 21:26:55 +03:00
Antony Polukhin
99e3c623f4 Modifications to make library suitable for Boost 2016-06-07 22:43:44 +03:00
37 changed files with 5560 additions and 1742 deletions

View File

@@ -7,14 +7,13 @@
#
# See https://svn.boost.org/trac/boost/wiki/TravisCoverals for description of this file
# and how it can be used with Boost libraries.
#
# File revision #6 modified
env:
matrix:
- CXX_STANDARD=c++14 TOOLSET=g++-5
- CXX_STANDARD=c++1z TOOLSET=g++-5
- CXX_STANDARD=c++14 TOOLSET=clang++-3.7
#- CXX_STANDARD=c++1y TOOLSET=clang++
#- CXX_STANDARD=c++14 TOOLSET=clang++-3.7
###############################################################################################################
@@ -33,13 +32,36 @@ addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
#- llvm-toolchain-precise-3.7
packages:
- valgrind
- gcc-5
- g++-5
- clang-3.7
#- clang
#- clang-3.7
script:
- git clone https://github.com/boostorg/core.git ../boost_core
- git clone https://github.com/boostorg/assert.git ../boost_assert
- git clone https://github.com/boostorg/config.git ../boost_config
- git clone https://github.com/boostorg/functional.git ../boost_functional
- git clone https://github.com/boostorg/integer.git ../boost_integer
- git clone https://github.com/boostorg/type_traits.git ../boost_tt
- git clone https://github.com/boostorg/mpl.git ../boost_mpl
- git clone https://github.com/boostorg/detail.git ../boost_detail
- git clone https://github.com/boostorg/static_assert.git ../boost_sa
- git clone https://github.com/boostorg/preprocessor.git ../boost_preprocessor
- INCLUDES="-I../boost_core/include/ -I../boost_config/include/ -I../boost_assert/include/ -I../boost_preprocessor/include/ -I../boost_sa/include/ -I../boost_functional/include/ -I../boost_detail/include/ -I../boost_integer/include/ -I../boost_tt/include/ -I../boost_mpl/include/ -I./include/"
# `--coverage` flags required to generate coverage info for Coveralls
- $TOOLSET -std=$CXX_STANDARD --coverage main.cpp && valgrind ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/core.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/count_fields_on_chars.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/count_fields_on_ints.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/count_fields_on_long_longs.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/count_fields_on_shorts.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/count_fields_on_void_ptrs.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/flat_functions_for.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/global_flat_ops.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/minimal.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/flat_ops.cpp && valgrind ./a.out && rm ./a.out
- $TOOLSET -std=$CXX_STANDARD --coverage $INCLUDES test/std_interactions.cpp && valgrind ./a.out && rm ./a.out

156
README.md
View File

@@ -1,170 +1,50 @@
#Magic Get [![Build Status](https://travis-ci.org/apolukhin/magic_get.svg?branch=master)](https://travis-ci.org/apolukhin/magic_get)
#POD Flat Reflection (Magic Get) [![Build Status](https://travis-ci.org/apolukhin/magic_get.svg?branch=master)](https://travis-ci.org/apolukhin/magic_get) [![Build Status](https://travis-ci.org/apolukhin/magic_get.svg?branch=develop)](https://travis-ci.org/apolukhin/magic_get)
This C++14 library is meant for accessing structure elements by index and providing other std::tuple like methods for user defined POD types.
[Latest documentation](http://apolukhin.github.com/magic_get/index.html)
[Pre Boost version](https://github.com/apolukhin/magic_get/tree/pre_boost)
### Motivating example
```c++
#include <iostream>
#include "magic_get.hpp"
#include "boost/pfr.hpp"
struct my_struct {
struct my_struct { // no ostream operator defined!
int i;
char c;
double d;
};
int main() {
my_struct s{100, 'H', 3.141593 };
std::cout << "my_struct has " << flat_tuple_size<my_struct>::value << " fields: "
<< "{ " << flat_get<0>(s) << ", " << flat_get<1>(s)<< ", " << flat_get<2>(s) << " }\n";
using namespace boost::pfr::flat_ops; // for ostream operator out-of-the-box for all PODs!
my_struct s{100, 'H', 3.141593};
std::cout << "my_struct has " << boost::pfr::flat_tuple_size<my_struct>::value
<< " fields: " << s << "\n";
}
```
Outputs:
```
my_struct has 3 fields: { 100, H, 3.14159 }
my_struct has 3 fields: {100, H, 3.14159}
```
### Flattening
All the methods with prefix `flat_` represent a template parameter type as flat structure without static members:
```c++
// Helper structure.
struct my_struct_nested { short a1; int a2; };
// This structure:
struct my_struct {
int a0;
static const cvalue = 1000;
my_struct_nested nested;
short a3_a4[2];
};
// will be flattened and represented as:
struct my_struct_flat {
int a0;
short a1;
int a2
short a3;
short a4
};
```
So that
* `flat_get<2>(my_struct{})` will return `my_struct::my_struct_nested::a2` field
* `flat_get<3>(my_struct{})` will return `my_struct::a3_a4[0]` field
Same story with arrays:
```c++
int i[2][2] = {{10, 11}, {12, 13} };
assert(flat_get<1>(i) == 11);
```
### API
```c++
/// Returns const reference to a field with index `I` in flattened `T`.
/// Example usage: flat_get<0>(my_structure());
template <std::size_t I, class T>
decltype(auto) flat_get(const T& val) noexcept;
/// Returns reference to a field with index `I` in flattened `T`.
/// Requires: `T` must not have const fields.
/// Example usage: flat_get<0>(my_structure());
template <std::size_t I, class T>
decltype(auto) flat_get(T& val, typename std::enable_if< std::is_trivially_assignable<T, T>::value>::type* = 0);
/// `flat_tuple_element` has a `typedef type-of-a-field-with-index-I-in-flattened-T type;`
/// Example usage: std::vector< flat_tuple_element<0, my_structure>::type > v;
template <std::size_t I, class T>
using flat_tuple_element;
/// Type of a field with index `I` in flattened `T`
/// Example usage: std::vector< flat_tuple_element_t<0, my_structure> > v;
template <std::size_t I, class T>
using flat_tuple_element_t = typename flat_tuple_element<I, T>::type;
/// `flat_tuple_size` has a member `value` that constins fields count in a flattened `T`.
/// Example usage: std::array<int, flat_tuple_size<my_structure>::value > a;
template <class T>
using flat_tuple_size;
/// `flat_tuple_size_v` is a template variable that constins fields count in a flattened `T`.
/// Example usage: std::array<int, flat_tuple_size_v<my_structure> > a;
template <class T>
constexpr std::size_t flat_tuple_size_v = flat_tuple_size<T>::value;
/// Creates an `std::tuple` from a flattened T.
/// Example usage:
/// struct my_struct { int i, short s; };
/// my_struct s {10, 11};
/// std::tuple<int, short> t = flat_to_tuple(s);
/// assert(get<0>(t) == 10);
template <class T>
auto flat_make_tuple(const T& val) noexcept;
/// Creates an `std::tuple` with lvalue references to fields of a flattened T.
/// Example usage:
/// struct my_struct { int i, short s; };
/// my_struct s;
/// flat_tie(s) = std::tuple<int, short>{10, 11};
/// assert(s.s == 11);
template <class T>
auto flat_tie(T& val, typename std::enable_if< std::is_trivially_assignable<T, T>::value>::type* = 0 ) noexcept;
/// Writes to `out` POD `value`
/// Example usage:
/// struct my_struct { int i, short s; };
/// my_struct s{12, 13};
/// flat_write(std::cout, s); // outputs '{12, 13}'
template <class Char, class Traits, class T>
void flat_write(std::basic_ostream<Char, Traits>& out, const T& value);
/// Reads POD `value` from stream `in`
/// Example usage:
/// struct my_struct { int i, short s; };
/// my_struct s;
/// std::stringstream ss;
/// ss << "{12, 13}";
/// ss >> s;
/// assert(s.i == 12);
/// assert(s.i == 13);
template <class Char, class Traits, class T>
void flat_read(std::basic_istream<Char, Traits>& in, T& value);
/// Contains comparison operators and stream operators for any POD types that does not have it's own operators.
/// If POD is comparable or streamable using it's own operator or it's conversion operator, then the original operator is be used.
///
/// Example usage:
/// struct comparable_struct { // No operators defined for that structure
/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
/// };
/// using namespace pod_ops;
///
/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
/// assert(s1 < s2);
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
namespace pod_ops;
```
### Requirements and Limitations
* C++14 compatible compiler (GCC-5.0+, Clang, ...)
* Static variables are ignored
C++14 limitations (C++17 fixes those):
* T must be POD and must not contain references nor bitfields
* T must not contain pointers to user defined types
* Enums will be returned as their underlying type
* Static variables are ignored
### License

41
doc/Jamfile.v2 Normal file
View File

@@ -0,0 +1,41 @@
# Copyright Antony Polukhin 2016.
# Use, modification, and distribution are
# subject to the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
using quickbook ;
using boostbook ;
using doxygen ;
doxygen autodoc
:
[ glob ../../../boost/pfr.hpp ]
[ glob ../../../boost/pfr/*.hpp ]
:
<doxygen:param>EXTRACT_ALL=NO
<doxygen:param>HIDE_UNDOC_MEMBERS=YES
<doxygen:param>EXTRACT_PRIVATE=NO
<doxygen:param>ENABLE_PREPROCESSING=YES
<doxygen:param>EXPAND_ONLY_PREDEF=YES
<doxygen:param>MACRO_EXPANSION=YES
<doxygen:param>INLINE_SIMPLE_STRUCTS=YES
<doxygen:param>SORT_MEMBER_DOCS=NO
<doxygen:param>"ALIASES= \\
\"flattening{1}=\\xmlonly<link linkend='boost_pod_flat_reflection.quick_start.flattening'>\\1</link>\\endxmlonly\" \\
\"podops=\\b See \\b Also: \\xmlonly<link linkend='boost_pod_flat_reflection.quick_start.three_ways_of_getting_operators'>Three ways of getting operators</link>\\endxmlonly\" \\
"
<doxygen:param>"PREDEFINED=\"BOOST_PFR_DOXYGEN_INVOKED\" \\
\"detail::stl_type_info=std::type_info\""
<xsl:param>"boost.doxygen.reftitle=Boost.PFR Header Reference"
;
xml pfr : pfr.qbk : <dependency>autodoc ;
boostbook standalone
:
pfr
:
<xsl:param>boost.root=http://www.boost.org/doc/libs/1_61_0
# <xsl:param>boost.root=../../../..
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/doc/html
;

185
doc/pfr.qbk Normal file
View File

@@ -0,0 +1,185 @@
[library Boost.POD Flat Reflection
[quickbook 1.6]
[version 1.0]
[copyright 2016 Antony Polukhin]
[category Language Features Emulation]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
[@http://www.boost.org/LICENSE_1_0.txt])
]
]
[section Motivation]
In C++ we have:
* tuples - types that provide access to members by index. Those are useful for generic programming.
* PODs - types with named fields that do not provide access to members by index. Those are just easy to use.
This library provides tuple like methods for POD types, making PODs usable in contexts were only tuples were useful.
[note All you have to do is to add `#include <boost/pfr.hpp>`.
No macro or other type/member registrations required.]
Boost.POD Flat Reflection (Boost.PFR) adds following out-of-the-box functionality to PODs:
* comparison operators
* heterogeneous comparators
* hash
* stream operators
* access to members by index
* member reflections
* methods for cooperation with `std::tuple`
[warning This is not an official Boost library! It wasn't reviewed and can't be downloaded from www.boost.org. This library is available to the community to know real interest and get comments for refinement. The intention is to submit library to formal review, if community think that it is interesting!]
[caution This is a C++14 library! Pre C++14 compilers (C++11, C++03...) are not supported]
[endsect]
[section Quick Start]
[import ../example/examples.cpp]
[section Accessing POD member by index] [pfr_example_get] [endsect]
[section Flattening] [pfr_example_flattening] [pfr_example_flattening_2] [endsect]
[/ [section Counting fields] [pfr_example_flat_tuple_size] [endsect] ]
[section Three ways of getting operators ]
There are three ways to start using Boost.PFR hashing, comparison and streaming operators for type `T` in your code. Each method has it's own drawbacks and suits own cases.
[table:flat_ops_comp Different approaches for operators
[[ Approach ] [ Defines operators in global namespace ]
[ Defined operators could be found by ADL ]
[ Works for local types ]
[ Usable localy, without affecting code from other scopes ]
[ Ignores implicit conversion operators ]
[ Respects user defined operators ] ]
[[ [headerref boost/pfr/flat_ops.hpp] ] [ no ] [ no ] [ yes ] [ yes ] [ no ] [ yes ] ]
[[ [headerref boost/pfr/flat_functions_for.hpp] ] [ yes if T is in it ] [ yes ] [ no ] [ no, but could be limited to translation unit ] [ yes for T ] [ no (compile time error) ] ]
[[ [headerref boost/pfr/global_flat_ops.hpp] ] [ yes ] [ yes ] [ yes ] [ no, but could be limited to translation unit ] [ yes all ] [ yes ] ]
]
More detailed description:
[*1) [headerref boost/pfr/flat_ops.hpp] approach]
This method is good if you're writing generic algorithms and need to use operators from Boost.PFR only if there's no operators defined for the type:
```
#include <boost/pfr/flat_ops.hpp>
template <class T>
struct uniform_comparator_less {
bool operator()(const T& lhs, const T& rhs) const noexcept {
using namespace flat_ops; // Enables Boost.PFR operators usage in this scope.
return lhs < rhs; // If T has operator< or conversion operator then will use it, otherwise will use boost::pfr::flat_less<T>.
}
};
```
This method's effects are local to the function. It works even for local types, like structures defined in functions.
However *Argument Dependant Lookup* does not work with it:
```
#include <boost/pfr/flat_ops.hpp>
template <class T>
struct uniform_comparator_less {
bool operator()(const T& lhs, const T& rhs) const noexcept {
using namespace flat_ops;
return std::less{}(lhs, rhs); // Compile time error if T has neither operator< nor conversion operator to comparable type.
}
};
```
[*2) [headerref boost/pfr/flat_functions_for.hpp] approach]
This method is good if you're writing POD structure and wish to define operators for that structure.
```
#include <boost/pfr/flat_functions_for.hpp>
struct pair_like {
int first;
short second;
};
BOOST_PFR_FLAT_FUNCTIONS_FOR(pair_like) // Defines operators
// ...
assert(pair_like{1, 2} < pair_like{1, 3});
```
Argument Dependant Lookup works well, `std::less` will find the operators for `struct pair_like`. [macroref BOOST_PFR_FLAT_FUNCTIONS_FOR BOOST_PFR_FLAT_FUNCTIONS_FOR(T)]
can not be used for local types, it must be called only once in namespace of `T`. It does not respect conversion operators of `T`, so for example the following code
will output different values:
```
#include <boost/pfr/flat_functions_for.hpp>
struct empty {
operator std::string() { return "empty{}"; }
};
// Uncomment to get different output:
// BOOST_PFR_FLAT_FUNCTIONS_FOR(empty)
// ...
std::cout << empty{}; // Outputs `empty{}` if BOOST_PFR_FLAT_FUNCTIONS_FOR(empty) is commented out, '{}' otherwise.
```
[*3) [headerref boost/pfr/global_flat_ops.hpp] approach]
This approach is for those, who wish to have comparisong/streaming/hashing for all their types.
```
#include <boost/pfr/global_flat_ops.hpp>
struct pair_like {
int first;
short second;
};
// ...
assert(pair_like{1, 2} < pair_like{1, 3});
```
Argument Dependant Lookup works well, `std::less` will find the operators for `struct pair_like`. Operators for local types will be also defined.
*All conversion operators of all POD types won't be used during comparisons/streaming/hashing.*
[endsect]
[endsect]
[section Requirements and Limitations]
[note Boost.PFR does not depend on any Boost library. You may use it's headers even without Boost. ]
* Boost.PFR *requires C++14 compatible compiler* (GCC-5.0+, Clang, ...)
* Static variables are ignored
* T must be aggregate initializable
C++14 limitations (C++17 fixes those):
* T must be POD and must not contain references nor bitfields
* T must not contain pointers to user defined types
* Enums will be returned as their underlying type
* All the methods that provide access to filds have a `reinterpret_cast` to an unrelated type. All the possible efforts and compiler scpecific tricks were used to avoid issues. But strictly speaking *this is an Undefined Behavior.
[endsect]
[section How it works]
Short description:
* at compile-time: uses aggregate initialization to detect fields count in user-provided structure
* at compile-time: makes a structure that is convertible to anything and remeber types it has been converted to during aggregate initialization of user-provided structure
* at compile-time: creates a tuple with exactly the same layout as in user-provided structure
* at run-time: `reinterpret_cast`s pointer to user-provided structure to the tuple pointer => all the tuple methods are available for the structure
Long description: [@https://www.youtube.com/watch?v=abdeAew3gmQ Antony Polukhin: C++14 Reflections Without Macros, Markup nor External Tooling. ].
[endsect]
[xinclude autodoc.xml]

102
example/examples.cpp Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2016 Antony Polukhin
// Distributed under the Boost Software License, Version 1.0.
// (See the accompanying file LICENSE_1_0.txt
// or a copy at <http://www.boost.org/LICENSE_1_0.txt>.)
#include <cassert>
//[pfr_example_get
/*`
The following example shows how to access structure fields by index using [funcref boost::pfr::flat_get].
Let's define some structure:
*/
#include <boost/pfr/core.hpp>
struct foo { // defining structure
int some_integer;
char c;
};
/*`
We can access fields of that structure by index:
*/
foo f {777, '!'};
auto& r1 = boost::pfr::flat_get<0>(f); // accessing field with index 0, returns reference to `foo::some_integer`
auto& r2 = boost::pfr::flat_get<1>(f); // accessing field with index 1, returns reference to `foo::c`
//] [/pfr_example_get]
//[pfr_example_flat_tuple_size
/*`
The following example shows how to count fields using [classref boost::pfr::flat_tuple_size].
*/
#include <boost/pfr/core.hpp>
struct foo2 { // defining structure
int some_integer;
char c;
short some_other_field;
};
static_assert(
boost::pfr::flat_tuple_size<foo2>::value // returns total count of fields in `foo2`
== 3, ""
);
static_assert(
boost::pfr::flat_tuple_size<int[100]>::value // works with arrays too!
== 100, ""
);
//] [/pfr_example_flat_tuple_size]
//[pfr_example_flattening
/*`
[warning All the functions and metafunctions in Boost.PFR that start with `flat_` represent template parameter type as flat structure without static members! ]
Take a look at the `struct my_struct`:
*/
struct my_struct_nested { short a1; int a2; };
struct my_struct {
int a0;
static const int cvalue = 1000;
my_struct_nested nested;
short a3_a4[2];
};
/*` It will be flattened and represented as: */
struct my_struct_flat {
int a0;
short a1;
int a2;
short a3;
short a4;
};
//] [/pfr_example_flattening]
void example_get() {
//[pfr_example_flattening_2
/*`
It means, that:
*/
boost::pfr::flat_get<2>(my_struct{}); // ... will return `my_struct::my_struct_nested::a2` field.
boost::pfr::flat_get<3>(my_struct{}); // ... will return `my_struct::a3_a4[0]` field.
/*` Exactly the same story with arrays: */
int i[2][2] = {{10, 11}, {12, 13} };
const int& r = boost::pfr::flat_get<1>(i);
assert(r == 11);
//] [/pfr_example_flattening_2]
(void)r;
}
int main() {
example_get();
}

16
include/boost/pfr.hpp Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#pragma once
/// \file boost/pfr.hpp
/// Includes all the Boost.PFR headers, except \xmlonly<link linkend='header.boost.pfr.global_flat_ops_hpp'>boost/pfr/global_flat_ops.hpp</link>\endxmlonly
#include <boost/pfr/core.hpp>
//#include <boost/pfr/core17.hpp>
#include <boost/pfr/functors.hpp>
#include <boost/pfr/flat_ops.hpp>
#include <boost/pfr/flat_functions_for.hpp>

993
include/boost/pfr/core.hpp Normal file
View File

@@ -0,0 +1,993 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_CORE_HPP
#define BOOST_PFR_CORE_HPP
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <type_traits>
#include <utility> // metaprogramming stuff
#include <tuple>
#include <iosfwd> // stream operators
#include <boost/pfr/fields_count.hpp>
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wmissing-braces"
# pragma clang diagnostic ignored "-Wundefined-inline"
# pragma clang diagnostic ignored "-Wundefined-internal"
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
namespace boost { namespace pfr {
namespace detail {
///////////////////// General utility stuff
template <class T> struct identity{
typedef T type;
};
template <class T>
constexpr T construct_helper() noexcept { // adding const here allows to deal with copyable only types
return {};
}
///////////////////// Tuple that holds it's values in the supplied order
namespace sequence_tuple {
template <std::size_t N, class T>
struct base_from_member {
T value;
};
template <class I, class ...Tail>
struct tuple_base;
template <std::size_t... I, class ...Tail>
struct tuple_base< std::index_sequence<I...>, Tail... >
: base_from_member<I , Tail>...
{
static constexpr std::size_t size_v = sizeof...(I);
constexpr tuple_base() noexcept = default;
constexpr tuple_base(tuple_base&&) noexcept = default;
constexpr tuple_base(const tuple_base&) noexcept = default;
constexpr tuple_base(Tail... v) noexcept
: base_from_member<I, Tail>{ v }...
{}
};
template <>
struct tuple_base<std::index_sequence<> > {
static constexpr std::size_t size_v = 0;
};
template <std::size_t N, class T>
constexpr T& get_impl(base_from_member<N, T>& t) noexcept {
return t.value;
}
template <std::size_t N, class T>
constexpr const T& get_impl(const base_from_member<N, T>& t) noexcept {
return t.value;
}
template <std::size_t N, class T>
constexpr volatile T& get_impl(volatile base_from_member<N, T>& t) noexcept {
return t.value;
}
template <std::size_t N, class T>
constexpr const volatile T& get_impl(const volatile base_from_member<N, T>& t) noexcept {
return t.value;
}
template <std::size_t N, class T>
constexpr T&& get_impl(base_from_member<N, T>&& t) noexcept {
return std::forward<T>(t.value);
}
template <class ...Values>
struct tuple: tuple_base<
std::make_index_sequence<sizeof...(Values)>,
Values...>
{
using tuple_base<
std::make_index_sequence<sizeof...(Values)>,
Values...
>::tuple_base;
};
template <std::size_t N, class ...T>
constexpr decltype(auto) get(tuple<T...>& t) noexcept {
static_assert(N < tuple<T...>::size_v, "Tuple index out of bounds");
return get_impl<N>(t);
}
template <std::size_t N, class ...T>
constexpr decltype(auto) get(const tuple<T...>& t) noexcept {
static_assert(N < tuple<T...>::size_v, "Tuple index out of bounds");
return get_impl<N>(t);
}
template <std::size_t N, class ...T>
constexpr decltype(auto) get(const volatile tuple<T...>& t) noexcept {
static_assert(N < tuple<T...>::size_v, "Tuple index out of bounds");
return get_impl<N>(t);
}
template <std::size_t N, class ...T>
constexpr decltype(auto) get(volatile tuple<T...>& t) noexcept {
static_assert(N < tuple<T...>::size_v, "Tuple index out of bounds");
return get_impl<N>(t);
}
template <std::size_t N, class ...T>
constexpr decltype(auto) get(tuple<T...>&& t) noexcept {
static_assert(N < tuple<T...>::size_v, "Tuple index out of bounds");
return get_impl<N>(std::move(t));
}
template <size_t I, class T>
using tuple_element = std::remove_reference< decltype(
::boost::pfr::detail::sequence_tuple::get<I>( std::declval<T>() )
) >;
} // namespace sequence_tuple
template <std::size_t I, std::size_t N>
struct print_impl {
template <class Stream, class T>
static void print (Stream& out, const T& value) {
if (!!I) out << ", ";
out << boost::pfr::detail::sequence_tuple::get<I>(value);
print_impl<I + 1, N>::print(out, value);
}
};
template <std::size_t I>
struct print_impl<I, I> {
template <class Stream, class T> static void print (Stream&, const T&) noexcept {}
};
template <std::size_t I, std::size_t N>
struct read_impl {
template <class Stream, class T>
static void read (Stream& in, const T& value) {
char ignore = {};
if (!!I) {
in >> ignore;
if (ignore != ',') in.setstate(Stream::failbit);
in >> ignore;
if (ignore != ' ') in.setstate(Stream::failbit);
}
in >> boost::pfr::detail::sequence_tuple::get<I>(value);
read_impl<I + 1, N>::read(in, value);
}
};
template <std::size_t I>
struct read_impl<I, I> {
template <class Stream, class T> static void read (Stream&, const T&) {}
};
///////////////////// Array that has the constexpr
template <std::size_t N>
struct size_array { // libc++ misses constexpr on operator[]
typedef std::size_t type;
std::size_t data[N];
static constexpr std::size_t size() noexcept { return N; }
constexpr std::size_t count_nonzeros() const noexcept {
std::size_t count = 0;
for (std::size_t i = 0; i < size(); ++i) {
if (data[i]) {
++ count;
}
}
return count;
}
constexpr std::size_t count_from_opening_till_matching_parenthis_seq(std::size_t from, std::size_t opening_parenthis, std::size_t closing_parenthis) const noexcept {
if (data[from] != opening_parenthis) {
return 0;
}
std::size_t unclosed_parnthesis = 0;
std::size_t count = 0;
for (; ; ++from) {
if (data[from] == opening_parenthis) {
++ unclosed_parnthesis;
} else if (data[from] == closing_parenthis) {
-- unclosed_parnthesis;
}
++ count;
if (unclosed_parnthesis == 0) {
return count;
}
}
return count;
}
};
template <>
struct size_array<0> { // libc++ misses constexpr on operator[]
typedef std::size_t type;
std::size_t data[1];
static constexpr std::size_t size() noexcept { return 0; }
constexpr std::size_t count_nonzeros() const noexcept {
return 0;
}
};
template <std::size_t I, std::size_t N>
constexpr std::size_t get(const size_array<N>& a) noexcept {
static_assert(I < N, "Array index out of bounds");
return a.data[I];
}
template <class T> constexpr size_array<sizeof(T) * 3> fields_count_and_type_ids_with_zeros() noexcept;
template <class T> constexpr auto flat_array_of_type_ids() noexcept;
///////////////////// All the stuff for representing Type as integer and converting integer back to type
namespace typeid_conversions {
///////////////////// Helper constants and typedefs
constexpr std::size_t native_types_mask = 31;
constexpr std::size_t bits_per_extension = 3;
constexpr std::size_t extension_maks = (
static_cast<std::size_t>((1 << bits_per_extension) - 1)
<< static_cast<std::size_t>(sizeof(std::size_t) * 8 - bits_per_extension)
);
constexpr std::size_t native_ptr_type = (
static_cast<std::size_t>(1)
<< static_cast<std::size_t>(sizeof(std::size_t) * 8 - bits_per_extension)
);
constexpr std::size_t native_const_ptr_type = (
static_cast<std::size_t>(2)
<< static_cast<std::size_t>(sizeof(std::size_t) * 8 - bits_per_extension)
);
constexpr std::size_t native_const_volatile_ptr_type = (
static_cast<std::size_t>(3)
<< static_cast<std::size_t>(sizeof(std::size_t) * 8 - bits_per_extension)
);
constexpr std::size_t native_volatile_ptr_type = (
static_cast<std::size_t>(4)
<< static_cast<std::size_t>(sizeof(std::size_t) * 8 - bits_per_extension)
);
constexpr std::size_t native_ref_type = (
static_cast<std::size_t>(5)
<< static_cast<std::size_t>(sizeof(std::size_t) * 8 - bits_per_extension)
);
template <std::size_t Index, std::size_t Extension>
using if_extension = std::enable_if_t< (Index & extension_maks) == Extension >*;
///////////////////// Helper functions
template <std::size_t Unptr>
constexpr std::size_t type_to_id_extension_apply(std::size_t ext) noexcept {
constexpr std::size_t native_id = (Unptr & native_types_mask);
constexpr std::size_t extensions = (Unptr & ~native_types_mask);
static_assert(
!((extensions >> bits_per_extension) & native_types_mask),
"Too many extensions for a single field (something close to `int************************** p;` is in the POD type)."
);
return (extensions >> bits_per_extension) | native_id | ext;
}
template <std::size_t Index>
using remove_1_ext = size_t_<
((Index & ~native_types_mask) << bits_per_extension) | (Index & native_types_mask)
>;
///////////////////// Forward declarations
template <class Type> constexpr std::size_t type_to_id(identity<Type*>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<const Type*>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<const volatile Type*>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<volatile Type*>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<Type&>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<Type>, std::enable_if_t<std::is_enum<Type>::value>* = 0) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<Type>, std::enable_if_t<std::is_empty<Type>::value>* = 0) noexcept;
template <class Type> constexpr size_array<sizeof(Type) * 3> type_to_id(identity<Type>, std::enable_if_t<!std::is_enum<Type>::value && !std::is_empty<Type>::value>* = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_const_ptr_type> = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_ptr_type> = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_const_volatile_ptr_type> = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_volatile_ptr_type> = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_ref_type> = 0) noexcept;
///////////////////// Definitions of type_to_id and id_to_type for fundamental types
/// @cond
#define BOOST_MAGIC_GET_REGISTER_TYPE(Type, Index) \
constexpr std::size_t type_to_id(identity<Type>) noexcept { \
return Index; \
} \
constexpr Type id_to_type( size_t_<Index > ) noexcept { \
return construct_helper<Type>(); \
} \
/**/
/// @endcond
// Register all base types here
BOOST_MAGIC_GET_REGISTER_TYPE(unsigned char , 1)
BOOST_MAGIC_GET_REGISTER_TYPE(unsigned short , 2)
BOOST_MAGIC_GET_REGISTER_TYPE(unsigned int , 3)
BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long , 4)
BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long long , 5)
BOOST_MAGIC_GET_REGISTER_TYPE(signed char , 6)
BOOST_MAGIC_GET_REGISTER_TYPE(short , 7)
BOOST_MAGIC_GET_REGISTER_TYPE(int , 8)
BOOST_MAGIC_GET_REGISTER_TYPE(long , 9)
BOOST_MAGIC_GET_REGISTER_TYPE(long long , 10)
BOOST_MAGIC_GET_REGISTER_TYPE(char , 11)
BOOST_MAGIC_GET_REGISTER_TYPE(wchar_t , 12)
BOOST_MAGIC_GET_REGISTER_TYPE(char16_t , 13)
BOOST_MAGIC_GET_REGISTER_TYPE(char32_t , 14)
BOOST_MAGIC_GET_REGISTER_TYPE(float , 15)
BOOST_MAGIC_GET_REGISTER_TYPE(double , 16)
BOOST_MAGIC_GET_REGISTER_TYPE(long double , 17)
BOOST_MAGIC_GET_REGISTER_TYPE(bool , 18)
BOOST_MAGIC_GET_REGISTER_TYPE(void* , 19)
BOOST_MAGIC_GET_REGISTER_TYPE(const void* , 20)
BOOST_MAGIC_GET_REGISTER_TYPE(volatile void* , 21)
BOOST_MAGIC_GET_REGISTER_TYPE(const volatile void* , 22)
BOOST_MAGIC_GET_REGISTER_TYPE(std::nullptr_t , 23)
constexpr std::size_t tuple_begin_tag = 24;
constexpr std::size_t tuple_end_tag = 25;
#undef BOOST_MAGIC_GET_REGISTER_TYPE
///////////////////// Definitions of type_to_id and id_to_type for types with extensions and nested types
template <class Type>
constexpr std::size_t type_to_id(identity<Type*>) noexcept {
constexpr auto unptr = type_to_id(identity<Type>{});
static_assert(
std::is_same<const std::size_t, decltype(unptr)>::value,
"Pointers to user defined types are not supported."
);
return type_to_id_extension_apply<unptr>(native_ptr_type);
}
template <class Type>
constexpr std::size_t type_to_id(identity<const Type*>) noexcept {
constexpr auto unptr = type_to_id(identity<Type>{});
static_assert(
std::is_same<const std::size_t, decltype(unptr)>::value,
"Const pointers to user defined types are not supported."
);
return type_to_id_extension_apply<unptr>(native_const_ptr_type);
}
template <class Type>
constexpr std::size_t type_to_id(identity<const volatile Type*>) noexcept {
constexpr auto unptr = type_to_id(identity<Type>{});
static_assert(
std::is_same<const std::size_t, decltype(unptr)>::value,
"Const volatile pointers to user defined types are not supported."
);
return type_to_id_extension_apply<unptr>(native_const_volatile_ptr_type);
}
template <class Type>
constexpr std::size_t type_to_id(identity<volatile Type*>) noexcept {
constexpr auto unptr = type_to_id(identity<Type>{});
static_assert(
std::is_same<const std::size_t, decltype(unptr)>::value,
"Volatile pointers to user defined types are not supported."
);
return type_to_id_extension_apply<unptr>(native_volatile_ptr_type);
}
template <class Type>
constexpr std::size_t type_to_id(identity<Type&>) noexcept {
constexpr auto unptr = type_to_id(identity<Type>{});
static_assert(
std::is_same<const std::size_t, decltype(unptr)>::value,
"References to user defined types are not supported."
);
return type_to_id_extension_apply<unptr>(native_ref_type);
}
template <class Type>
constexpr std::size_t type_to_id(identity<Type>, std::enable_if_t<std::is_enum<Type>::value>*) noexcept {
return type_to_id(identity<typename std::underlying_type<Type>::type >{});
}
template <class Type>
constexpr std::size_t type_to_id(identity<Type>, std::enable_if_t<std::is_empty<Type>::value>*) noexcept {
static_assert(!std::is_empty<Type>::value, "Empty classes/structures as members are not supported.");
return 0;
}
template <class Type>
constexpr size_array<sizeof(Type) * 3> type_to_id(identity<Type>, std::enable_if_t<!std::is_enum<Type>::value && !std::is_empty<Type>::value>*) noexcept {
constexpr auto t = flat_array_of_type_ids<Type>();
size_array<sizeof(Type) * 3> result {{tuple_begin_tag}};
constexpr bool requires_tuplening = (
(t.count_nonzeros() != 1) || (t.count_nonzeros() == t.count_from_opening_till_matching_parenthis_seq(0, tuple_begin_tag, tuple_end_tag))
);
if (requires_tuplening) {
for (std::size_t i = 0; i < t.size(); ++i)
result.data[i + 1] = t.data[i];
result.data[result.size() - 1] = tuple_end_tag;
} else {
for (std::size_t i = 0; i < t.size(); ++i)
result.data[i] = t.data[i];
}
return result;
}
template <std::size_t Index>
constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_ptr_type>) noexcept {
typedef decltype( id_to_type(remove_1_ext<Index>()) )* res_t;
return construct_helper<res_t>();
}
template <std::size_t Index>
constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_const_ptr_type>) noexcept {
typedef const decltype( id_to_type(remove_1_ext<Index>()) )* res_t;
return construct_helper<res_t>();
}
template <std::size_t Index>
constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_const_volatile_ptr_type>) noexcept {
typedef const volatile decltype( id_to_type(remove_1_ext<Index>()) )* res_t;
return construct_helper<res_t>();
}
template <std::size_t Index>
constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_volatile_ptr_type>) noexcept {
typedef volatile decltype( id_to_type(remove_1_ext<Index>()) )* res_t;
return construct_helper<res_t>();
}
template <std::size_t Index>
constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_ref_type>) noexcept {
static_assert(!Index, "References are not supported");
return nullptr;
}
} // namespace typeid_conversions
///////////////////// Structure that remembers types as integers on a `constexpr operator Type()` call
struct ubiq_val {
std::size_t* ref_;
template <class T>
constexpr void assign(const T& typeids) const noexcept {
for (std::size_t i = 0; i < T::size(); ++i)
ref_[i] = typeids.data[i];
}
constexpr void assign(std::size_t val) const noexcept {
ref_[0] = val;
}
template <class Type>
constexpr operator Type() const noexcept {
constexpr auto typeids = typeid_conversions::type_to_id(identity<Type>{});
assign(typeids);
return construct_helper<Type>();
}
};
///////////////////// Structure that remembers size of the type on a `constexpr operator Type()` call
struct ubiq_sizes {
std::size_t& ref_;
template <class Type>
constexpr operator Type() const noexcept {
ref_ = sizeof(Type);
return construct_helper<Type>();
}
};
///////////////////// Returns array of (offsets without accounting alignments). Required for keeping places for nested type ids
template <class T, std::size_t N, std::size_t... I>
constexpr size_array<N> get_type_offsets() noexcept {
typedef size_array<N> array_t;
array_t sizes{};
T tmp{ ubiq_sizes{sizes.data[I]}... };
(void)tmp;
array_t offsets{{0}};
for (std::size_t i = 1; i < N; ++i)
offsets.data[i] = offsets.data[i - 1] + sizes.data[i - 1];
return offsets;
}
///////////////////// Returns array of typeids and zeros if construtor of a type accepts sizeof...(I) parameters, substitution failure otherwise
template <class T, std::size_t N, std::size_t... I>
constexpr auto flat_type_to_array_of_type_ids(std::size_t* types, std::index_sequence<I...>) noexcept
-> typename std::add_pointer<decltype(T{ ubiq_constructor{I}... })>::type
{
static_assert(
N <= sizeof(T),
"Bit fields are not supported."
);
constexpr auto offsets = get_type_offsets<T, N, I...>();
T tmp{ ubiq_val{types + get<I>(offsets) * 3}... };
(void)types;
(void)tmp;
(void)offsets; // If type is empty offsets are not used
return nullptr;
}
///////////////////// Returns array of typeids and zeros
template <class T>
constexpr size_array<sizeof(T) * 3> fields_count_and_type_ids_with_zeros() noexcept {
size_array<sizeof(T) * 3> types{};
constexpr std::size_t N = fields_count<T>();
flat_type_to_array_of_type_ids<T, N>(types.data, std::make_index_sequence<N>());
return types;
}
///////////////////// Returns array of typeids without zeros
template <class T>
constexpr auto flat_array_of_type_ids() noexcept {
constexpr auto types = fields_count_and_type_ids_with_zeros<T>();
constexpr std::size_t count = types.count_nonzeros();
size_array<count> res{};
std::size_t j = 0;
for (std::size_t i = 0; i < decltype(types)::size(); ++i) {
if (types.data[i]) {
res.data[j] = types.data[i];
++ j;
}
}
return res;
}
///////////////////// Convert array of typeids into sequence_tuple::tuple
template <class T, std::size_t First, std::size_t... I>
constexpr auto as_flat_tuple_impl(std::index_sequence<First, I...>) noexcept;
template <class T>
constexpr sequence_tuple::tuple<> as_flat_tuple_impl(std::index_sequence<>) noexcept {
return sequence_tuple::tuple<>{};
}
template <std::size_t Increment, std::size_t... I>
constexpr auto increment_index_sequence(std::index_sequence<I...>) noexcept {
return std::index_sequence<I + Increment...>{};
}
template <class T, std::size_t V, std::size_t I, std::size_t SubtupleLength>
constexpr auto prepare_subtuples(size_t_<V>, size_t_<I>, size_t_<SubtupleLength>) noexcept {
static_assert(SubtupleLength == 0, "Internal error while representing nested field as tuple");
return typeid_conversions::id_to_type(size_t_<V>{});
}
template <class T, std::size_t I, std::size_t SubtupleLength>
constexpr auto prepare_subtuples(size_t_<typeid_conversions::tuple_end_tag>, size_t_<I>, size_t_<SubtupleLength>) noexcept {
static_assert(sizeof(T) == 0, "Internal error while representing nested field as tuple");
return int{};
}
template <class T, std::size_t I, std::size_t SubtupleLength>
constexpr auto prepare_subtuples(size_t_<typeid_conversions::tuple_begin_tag>, size_t_<I>, size_t_<SubtupleLength>) noexcept {
static_assert(SubtupleLength > 2, "Internal error while representing nested field as tuple");
constexpr auto seq = std::make_index_sequence<SubtupleLength - 2>{};
return as_flat_tuple_impl<T>( increment_index_sequence<I + 1>(seq) );
}
template <class Array>
constexpr Array remove_subtuples(Array indexes_plus_1, const Array& subtuple_lengths) noexcept {
for (std::size_t i = 0; i < subtuple_lengths.size(); ++i) {
if (subtuple_lengths.data[i]) {
const std::size_t skips_count = subtuple_lengths.data[i];
for (std::size_t j = i + 1; j < skips_count + i; ++j) {
indexes_plus_1.data[j] = 0;
}
i += skips_count - 1;
}
}
return indexes_plus_1;
}
template <std::size_t N, class Array>
constexpr size_array<N> resize_dropping_zeros_and_decrementing(size_t_<N>, const Array& a) noexcept {
size_array<N> result{};
std::size_t result_indx = 0;
for (std::size_t i = 0; i < a.size(); ++i) {
if (a.data[i]) {
result.data[result_indx] = static_cast<std::size_t>(a.data[i] - 1);
++ result_indx;
}
}
return result;
}
template <class T, std::size_t First, std::size_t... I, std::size_t... INew>
constexpr auto as_flat_tuple_impl_drop_helpers(std::index_sequence<First, I...>, std::index_sequence<INew...>) noexcept {
constexpr auto a = flat_array_of_type_ids<T>();
constexpr size_array<sizeof...(I) + 1> subtuples_length {{
a.count_from_opening_till_matching_parenthis_seq(First, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag),
a.count_from_opening_till_matching_parenthis_seq(I, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag)...
}};
constexpr size_array<sizeof...(I) + 1> type_indexes_with_subtuple_internals {{ 1, 1 + I - First...}};
constexpr auto type_indexes_plus_1_and_zeros_as_skips = remove_subtuples(type_indexes_with_subtuple_internals, subtuples_length);
constexpr auto new_size = size_t_<type_indexes_plus_1_and_zeros_as_skips.count_nonzeros()>{};
constexpr auto type_indexes = resize_dropping_zeros_and_decrementing(new_size, type_indexes_plus_1_and_zeros_as_skips);
typedef sequence_tuple::tuple<
decltype(prepare_subtuples<T>(
size_t_< a.data[ First + type_indexes.data[INew] ] >{}, // id of type
size_t_< First + type_indexes.data[INew] >{}, // index of current id in `a`
size_t_< subtuples_length.data[ type_indexes.data[INew] ] >{} // if id of type is tuple, then length of that tuple
))...
> subtuples_uncleanuped_t;
return subtuples_uncleanuped_t{};
}
template <class Array>
constexpr std::size_t count_skips_in_array(std::size_t begin_index, std::size_t end_index, const Array& a) noexcept {
std::size_t skips = 0;
for (std::size_t i = begin_index; i < end_index; ++i) {
if (a.data[i] == typeid_conversions::tuple_begin_tag) {
const std::size_t this_tuple_size = a.count_from_opening_till_matching_parenthis_seq(i, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag) - 1;
skips += this_tuple_size;
i += this_tuple_size - 1;
}
}
return skips;
}
template <class T, std::size_t First, std::size_t... I>
constexpr auto as_flat_tuple_impl(std::index_sequence<First, I...>) noexcept {
constexpr auto a = flat_array_of_type_ids<T>();
constexpr std::size_t count_of_I = sizeof...(I);
return as_flat_tuple_impl_drop_helpers<T>(
std::index_sequence<First, I...>{},
std::make_index_sequence< 1 + count_of_I - count_skips_in_array(First, First + count_of_I, a) >{}
);
}
template <class T>
constexpr auto internal_tuple_with_same_alignment() noexcept {
typedef typename std::remove_cv<T>::type type;
#ifdef BOOST_PFR_RELAX_POD_REQUIREMENT
static_assert(std::is_standard_layout<type>::value, "Not applyable");
static_assert(std::is_move_constructible<type>::value || std::is_array<type>::value, "Not applyable");
#else
static_assert(std::is_pod<type>::value, "Not applyable");
#endif
static_assert(!std::is_reference<type>::value, "Not applyable");
constexpr auto res = as_flat_tuple_impl<type>(
std::make_index_sequence< decltype(flat_array_of_type_ids<type>())::size() >()
);
static_assert(
std::alignment_of<decltype(res)>::value == std::alignment_of<type>::value,
"Alignment check failed, probably your structure has user-defined alignment for the whole structure or for some of the fields."
);
static_assert(sizeof(res) == sizeof(type), "Size check failed, probably your structure has bitfields or user-defined alignment.");
return res;
}
template <class T>
using internal_tuple_with_same_alignment_t = decltype( internal_tuple_with_same_alignment<T>() );
///////////////////// Flattening
template <class Tuple, std::size_t Begin, std::size_t Size>
constexpr auto make_flat_tuple_of_references(Tuple&& t, size_t_<Begin>, size_t_<Size>) noexcept;
template <class Tuple, std::size_t Begin>
constexpr detail::sequence_tuple::tuple<> make_flat_tuple_of_references(Tuple&& t, size_t_<Begin>, size_t_<0>) noexcept;
template <class Tuple, std::size_t Begin>
constexpr auto make_flat_tuple_of_references(Tuple&& t, size_t_<Begin>, size_t_<1>) noexcept;
template <class... T>
constexpr auto as_tuple_with_references(T&&... args) noexcept {
return detail::sequence_tuple::tuple<T&...>{ std::forward<T>(args)... };
}
template <class... T>
constexpr decltype(auto) as_tuple_with_references(detail::sequence_tuple::tuple<T...>& t) noexcept {
return make_flat_tuple_of_references(t, size_t_<0>{}, size_t_<detail::sequence_tuple::tuple<T...>::size_v>{});
}
template <class... T>
constexpr decltype(auto) as_tuple_with_references(const detail::sequence_tuple::tuple<T...>& t) noexcept {
return make_flat_tuple_of_references(t, size_t_<0>{}, size_t_<detail::sequence_tuple::tuple<T...>::size_v>{});
}
template <class Tuple1, std::size_t... I1, class Tuple2, std::size_t... I2>
constexpr auto my_tuple_cat_impl(const Tuple1& t1, std::index_sequence<I1...>, const Tuple2& t2, std::index_sequence<I2...>) noexcept {
return as_tuple_with_references(
detail::sequence_tuple::get<I1>(t1)...,
detail::sequence_tuple::get<I2>(t2)...
);
}
template <class Tuple1, class Tuple2>
constexpr auto my_tuple_cat(const Tuple1& t1, const Tuple2& t2) noexcept {
return my_tuple_cat_impl(
t1, std::make_index_sequence< Tuple1::size_v >{},
t2, std::make_index_sequence< Tuple2::size_v >{}
);
}
template <class Tuple, std::size_t Begin, std::size_t Size>
constexpr auto make_flat_tuple_of_references(Tuple&& t, size_t_<Begin>, size_t_<Size>) noexcept {
constexpr std::size_t next_size = Size / 2;
return my_tuple_cat(
make_flat_tuple_of_references(std::forward<Tuple>(t), size_t_<Begin>{}, size_t_<next_size>{}),
make_flat_tuple_of_references(std::forward<Tuple>(t), size_t_<Begin + Size / 2>{}, size_t_<Size - next_size>{})
);
}
template <class Tuple, std::size_t Begin>
constexpr detail::sequence_tuple::tuple<> make_flat_tuple_of_references(Tuple&& /*t*/, size_t_<Begin>, size_t_<0>) noexcept {
return detail::sequence_tuple::tuple<>{};
}
template <class Tuple, std::size_t Begin>
constexpr auto make_flat_tuple_of_references(Tuple&& t, size_t_<Begin>, size_t_<1>) noexcept {
return as_tuple_with_references(
detail::sequence_tuple::get<Begin>(std::forward<Tuple>(t))
);
}
/// @cond
#ifdef __GNUC__
#define MAY_ALIAS __attribute__((__may_alias__))
#else
#define MAY_ALIAS
#endif
/// @endcond
template <class T>
decltype(auto) tie_as_flat_tuple(const T& val) noexcept {
MAY_ALIAS const auto* const t = reinterpret_cast<const detail::internal_tuple_with_same_alignment_t<T>*>( std::addressof(val) );
return make_flat_tuple_of_references(*t, size_t_<0>{}, size_t_<detail::internal_tuple_with_same_alignment_t<T>::size_v>{});
}
template <class T>
decltype(auto) tie_as_flat_tuple(const volatile T& val) noexcept {
MAY_ALIAS const volatile auto* const t = reinterpret_cast<const volatile detail::internal_tuple_with_same_alignment_t<T>*>( std::addressof(val) );
return make_flat_tuple_of_references(*t, size_t_<0>{}, size_t_<detail::internal_tuple_with_same_alignment_t<T>::size_v>{});
}
template <class T>
decltype(auto) tie_as_flat_tuple(volatile T& val) noexcept {
MAY_ALIAS volatile auto* const t = reinterpret_cast<volatile detail::internal_tuple_with_same_alignment_t<T>*>( std::addressof(val) );
return make_flat_tuple_of_references(*t, size_t_<0>{}, size_t_<detail::internal_tuple_with_same_alignment_t<T>::size_v>{});
}
template <class T>
decltype(auto) tie_as_flat_tuple(T& val) noexcept {
MAY_ALIAS auto* const t = reinterpret_cast<detail::internal_tuple_with_same_alignment_t<T>*>( std::addressof(val) );
return make_flat_tuple_of_references(*t, size_t_<0>{}, size_t_<detail::internal_tuple_with_same_alignment_t<T>::size_v>{});
}
#undef MAY_ALIAS
template <class T, std::size_t... I>
constexpr auto make_stdtuple_from_tietuple(const T& t, std::index_sequence<I...>) noexcept {
return std::make_tuple(
boost::pfr::detail::sequence_tuple::get<I>(t)...
);
}
template <class T, std::size_t... I>
constexpr auto make_stdtiedtuple_from_tietuple(const T& t, std::index_sequence<I...>) noexcept {
return std::tie(
boost::pfr::detail::sequence_tuple::get<I>(t)...
);
}
} // namespace detail
#ifdef __clang__
# pragma clang diagnostic pop
#endif
/// \brief Returns reference or const reference to a field with index `I` in \flattening{flattened} T.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s {10, 11};
/// assert(boost::pfr::flat_get<0>(s) == 10);
/// boost::pfr::flat_get<1>(s) = 0;
/// \endcode
template <std::size_t I, class T>
decltype(auto) flat_get(const T& val) noexcept {
return boost::pfr::detail::sequence_tuple::get<I>( boost::pfr::detail::tie_as_flat_tuple(val) );
}
/// \overload flat_get
template <std::size_t I, class T>
decltype(auto) flat_get(T& val /* @cond */, std::enable_if_t< std::is_trivially_assignable<T, T>::value>* = 0/* @endcond */ ) noexcept {
return boost::pfr::detail::sequence_tuple::get<I>( boost::pfr::detail::tie_as_flat_tuple(val) );
}
/// \brief `flat_tuple_element` has a `typedef type-of-the-field-with-index-I-in-\flattening{flattened}-T type;`
///
/// \b Example:
/// \code
/// std::vector< boost::pfr::flat_tuple_element<0, my_structure>::type > v;
/// \endcode
template <std::size_t I, class T>
using flat_tuple_element = std::remove_reference<
typename boost::pfr::detail::sequence_tuple::tuple_element<I, decltype(boost::pfr::detail::tie_as_flat_tuple(std::declval<T&>())) >::type
>;
/// \brief Type of a field with index `I` in \flattening{flattened} `T`
///
/// \b Example:
/// \code
/// std::vector< boost::pfr::flat_tuple_element_t<0, my_structure> > v;
/// \endcode
template <std::size_t I, class T>
using flat_tuple_element_t = typename flat_tuple_element<I, T>::type;
/// \brief Has a static const member variable `value` that contains fields count in a \flattening{flattened} T.
///
/// \b Example:
/// \code
/// std::array<int, boost::pfr::flat_tuple_size<my_structure>::value > a;
/// \endcode
template <class T>
using flat_tuple_size = boost::pfr::detail::size_t_<decltype(boost::pfr::detail::tie_as_flat_tuple(std::declval<T&>()))::size_v>;
/// \brief `flat_tuple_size_v` is a template variable that contains fields count in a \flattening{flattened} T.
///
/// \b Example:
/// \code
/// std::array<int, boost::pfr::flat_tuple_size_v<my_structure> > a;
/// \endcode
template <class T>
constexpr std::size_t flat_tuple_size_v = flat_tuple_size<T>::value;
/// \brief Creates an `std::tuple` from a \flattening{flattened} T.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s {10, 11};
/// std::tuple<int, short> t = flat_make_tuple(s);
/// assert(flat_get<0>(t) == 10);
/// \endcode
template <class T>
auto flat_structure_to_tuple(const T& val) noexcept {
return detail::make_stdtuple_from_tietuple(
detail::tie_as_flat_tuple(val),
std::make_index_sequence< flat_tuple_size_v<T> >()
);
}
/// \brief Creates an `std::tuple` with lvalue references to fields of a \flattening{flattened} T.
///
/// \b Requires: `T` must not have const fields.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s;
/// flat_structure_tie(s) = std::tuple<int, short>{10, 11};
/// assert(s.s == 11);
/// \endcode
template <class T>
auto flat_structure_tie(T& val /* @cond */, std::enable_if_t< std::is_trivially_assignable<T, T>::value>* = 0 /* @endcond */) noexcept {
return detail::make_stdtiedtuple_from_tietuple(
detail::tie_as_flat_tuple(val),
std::make_index_sequence< flat_tuple_size_v<T> >()
);
}
/// \brief Writes \flattening{flattened} POD `value` to `out`
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s{12, 13};
/// flat_write(std::cout, s); // outputs '{12, 13}'
/// \endcode
template <class Char, class Traits, class T>
void flat_write(std::basic_ostream<Char, Traits>& out, const T& value) {
out << '{';
detail::print_impl<0, flat_tuple_size_v<T> >::print(out, detail::tie_as_flat_tuple(value));
out << '}';
}
/// Reads \flattening{flattened} POD `value` from stream `in`
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s;
/// std::stringstream ss;
/// ss << "{ 12, 13 }";
/// ss >> s;
/// assert(s.i == 12);
/// assert(s.i == 13);
/// \endcode
template <class Char, class Traits, class T>
void flat_read(std::basic_istream<Char, Traits>& in, T& value) {
const auto prev_exceptions = in.exceptions();
in.exceptions( typename std::basic_istream<Char, Traits>::iostate(0) );
const auto prev_flags = in.flags( typename std::basic_istream<Char, Traits>::fmtflags(0) );
char parenthis = {};
in >> parenthis;
if (parenthis != '{') in.setstate(std::basic_istream<Char, Traits>::failbit);
detail::read_impl<0, flat_tuple_size_v<T> >::read(in, detail::tie_as_flat_tuple(value));
in >> parenthis;
if (parenthis != '}') in.setstate(std::basic_istream<Char, Traits>::failbit);
in.flags(prev_flags);
in.exceptions(prev_exceptions);
}
}} // namespace boost::pfr
#endif // BOOST_PFR_CORE_HPP

View File

@@ -0,0 +1,175 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_CORE17_HPP
#define BOOST_PFR_CORE17_HPP
#pragma once
#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */
# include <boost/pfr/detail/core17_generated.hpp>
#else
namespace boost { namespace pfr { namespace detail {
template <class T>
constexpr auto as_tuple(T&& val) noexcept {
static_assert(!sizeof(val), "C++17 required for this function");
return nullptr;
}
template <class T>
using as_tuple_t = decltype( ::boost::pfr::detail::as_tuple(std::declval<T&>()) );
}}} // namespace boost::pfr::detail
#endif
namespace boost { namespace pfr {
/// \brief Returns reference or const reference to a field with index `I` in aggregate T.
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s {10, 11};
/// assert(boost::pfr::get<0>(s) == 10);
/// boost::pfr::get<1>(s) = 0;
/// \endcode
template <std::size_t I, class T>
constexpr decltype(auto) get(const T& val) noexcept {
return detail::sequence_tuple::get<I>( detail::as_tuple(val) );
}
/// \overload get
template <std::size_t I, class T>
constexpr decltype(auto) get(T& val) noexcept {
return detail::sequence_tuple::get<I>( detail::as_tuple(val) );
}
/// \brief `tuple_element` has a `typedef type-of-a-field-with-index-I-in-aggregate-T type;`
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// std::vector< boost::pfr::tuple_element<0, my_structure>::type > v;
/// \endcode
template <std::size_t I, class T>
using tuple_element = detail::teleport_extents<
T,
typename detail::sequence_tuple::tuple_element<I, detail::as_tuple_t<T> >::type
>;
/// \brief Type of a field with index `I` in aggregate `T`.
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// std::vector< boost::pfr::tuple_element_t<0, my_structure> > v;
/// \endcode
template <std::size_t I, class T>
using tuple_element_t = typename tuple_element<I, T>::type;
/// \brief Creates an `std::tuple` from an aggregate T.
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s {10, 11};
/// std::tuple<int, short> t = make_tuple(s);
/// assert(get<0>(t) == 10);
/// \endcode
template <class T>
constexpr auto structure_to_tuple(const T& val) noexcept {
typedef detail::as_tuple_t<T> internal_tuple_t;
return detail::make_stdtuple_from_seqtuple(
detail::as_tuple(val),
std::make_index_sequence< internal_tuple_t::size_v >()
);
}
/// \brief Creates an `std::tuple` with lvalue references to fields of an aggregate T.
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s;
/// tie(s) = std::tuple<int, short>{10, 11};
/// assert(s.s == 11);
/// \endcode
template <class T>
constexpr auto structure_tie(T& val) noexcept {
typedef detail::as_tuple_t<T> internal_tuple_t;
return detail::tie_sequence_tuple_impl(
detail::as_tuple(val),
std::make_index_sequence< internal_tuple_t::size_v >()
);
}
/// \brief Writes aggregate `value` to `out`
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s{12, 13};
/// write(std::cout, s); // outputs '{12, 13}'
/// \endcode
template <class Char, class Traits, class T>
void write(std::basic_ostream<Char, Traits>& out, const T& value) {
out << '{';
detail::sequence_tuple::print_impl<0, tuple_size_v<T> >::print(out, detail::as_tuple(value));
out << '}';
}
/// Reads aggregate `value` from stream `in`
///
/// \b Requires: C++17.
///
/// \b Example:
/// \code
/// struct my_struct { int i, short s; };
/// my_struct s;
/// std::stringstream ss;
/// ss << "{ 12, 13 }";
/// ss >> s;
/// assert(s.i == 12);
/// assert(s.i == 13);
/// \endcode
template <class Char, class Traits, class T>
void read(std::basic_istream<Char, Traits>& in, T& value) {
const auto prev_exceptions = in.exceptions();
in.exceptions( typename std::basic_istream<Char, Traits>::iostate(0) );
const auto prev_flags = in.flags( typename std::basic_istream<Char, Traits>::fmtflags(0) );
char parenthis = {};
in >> parenthis;
if (parenthis != '{') in.setstate(std::basic_istream<Char, Traits>::failbit);
detail::sequence_tuple::read_impl<0, tuple_size_v<T> >::read(in, detail::as_tuple(value));
in >> parenthis;
if (parenthis != '}') in.setstate(std::basic_istream<Char, Traits>::failbit);
in.flags(prev_flags);
in.exceptions(prev_exceptions);
}
}} // namespace boost::pfr
#endif // BOOST_PFR_CORE17_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_FIELDS_COUNT_HPP
#define BOOST_PFR_FIELDS_COUNT_HPP
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <type_traits>
#include <utility> // metaprogramming stuff
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wmissing-braces"
# pragma clang diagnostic ignored "-Wundefined-inline"
# pragma clang diagnostic ignored "-Wundefined-internal"
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
namespace boost { namespace pfr {
namespace detail {
///////////////////// General utility stuff
template <std::size_t Index>
using size_t_ = std::integral_constant<std::size_t, Index >;
///////////////////// Structure that can be converted to reference to anything
struct ubiq_constructor {
std::size_t ignore;
template <class Type> constexpr operator Type&() const noexcept; // Undefined, allows initialization of reference fields (T& and const T&)
//template <class Type> constexpr operator Type&&() const noexcept; // Undefined, allows initialization of rvalue reference fields and move-only types
};
///////////////////// Structure that can be converted to reference to anything except reference to T
template <class T>
struct ubiq_constructor_except {
template <class Type> constexpr operator std::enable_if_t<!std::is_same<T, Type>::value, Type&> () const noexcept; // Undefined
};
///////////////////// Hand-made is_aggregate_initializable_n<T> trait:
// `std::is_constructible<T, ubiq_constructor_except<T>>` consumes a lot of time, so we made a separate lazy trait for it.
template <std::size_t N, class T> struct is_single_field_and_aggregate_initializable: std::false_type {};
template <class T> struct is_single_field_and_aggregate_initializable<1, T>: std::integral_constant<
bool, !std::is_constructible<T, ubiq_constructor_except<T>>::value
> {};
template <class T, std::size_t N>
struct is_aggregate_initializable_n {
template <std::size_t ...I>
static constexpr bool is_not_constructible_n(std::index_sequence<I...>) noexcept {
return !std::is_constructible<T, decltype(ubiq_constructor{I})...>::value
|| is_single_field_and_aggregate_initializable<N, T>::value
;
}
static constexpr bool value =
std::is_empty<T>::value
|| std::is_fundamental<T>::value
|| is_not_constructible_n(std::make_index_sequence<N>{})
;
};
///////////////////// Methods for detecting max parameters for construction of T
template <class T, std::size_t... I>
constexpr auto enable_if_constructible_helper(std::index_sequence<I...>) noexcept
-> typename std::add_pointer<decltype(T{ ubiq_constructor{I}... })>::type;
template <class T, std::size_t N>
constexpr void detect_fields_count(std::size_t& count, size_t_<N>, size_t_<N>, long) noexcept {
// Hand-made is_aggregate<T> trait:
// Aggregates could be constructed from `decltype(ubiq_constructor{I})...` but report that there's no constructor from `decltype(ubiq_constructor{I})...`
// Special case for N == 1: `std::is_constructible<T, ubiq_constructor>` returns true if N == 1 and T is copy/move constructible.
static_assert(
is_aggregate_initializable_n<T, N>::value,
"Types with user specified constructors (non-aggregate types) are not supported."
);
count = N;
}
template <class T, std::size_t Begin, std::size_t Middle>
constexpr void detect_fields_count(std::size_t& count, size_t_<Begin>, size_t_<Middle>, int) noexcept;
template <class T, std::size_t Begin, std::size_t Middle>
constexpr auto detect_fields_count(std::size_t& count, size_t_<Begin>, size_t_<Middle>, long) noexcept
-> decltype( enable_if_constructible_helper<T>(std::make_index_sequence<Middle>()) )
{
constexpr std::size_t next = Middle + (Middle - Begin + 1) / 2;
detect_fields_count<T>(count, size_t_<Middle>{}, size_t_<next>{}, 1L);
return nullptr;
}
template <class T, std::size_t Begin, std::size_t Middle>
constexpr void detect_fields_count(std::size_t& count, size_t_<Begin>, size_t_<Middle>, int) noexcept {
constexpr std::size_t next = (Begin + Middle) / 2;
detect_fields_count<T>(count, size_t_<Begin>{}, size_t_<next>{}, 1L);
}
///////////////////// Returns non-flattened fields count
template <class T>
constexpr std::size_t fields_count() noexcept {
static_assert(std::is_copy_constructible<std::remove_all_extents_t<T>>::value, "Structure and each field in structure must be copy constructible");
std::size_t res = 0u;
constexpr std::size_t next = (sizeof(T) * 8) / 2 + 1; // We multiply by 8 because we may have bitfields in T
detect_fields_count<T>(res, size_t_<0>{}, size_t_<next>{}, 1L);
return res;
}
} // namespace detail
#ifdef __clang__
# pragma clang diagnostic pop
#endif
/// \brief Has a static const member variable `value` that constins fields count in a T.
/// Works for any T that supports aggregate initialization even if T is not POD.
/// \flattening{Flattens} only multidimensional arrays.
///
/// \b Example:
/// \code
/// std::array<int, boost::pfr::tuple_size<my_structure>::value > a;
/// \endcode
template <class T>
using tuple_size = detail::size_t_< boost::pfr::detail::fields_count<T>() >;
/// \brief `tuple_size_v` is a template variable that contains fields count in a T and
/// works for any T that supports aggregate initialization even if T is not POD.
/// \flattening{Flattens} only multidimensional arrays.
///
/// \b Example:
/// \code
/// std::array<int, boost::pfr::tuple_size_v<my_structure> > a;
/// \endcode
template <class T>
constexpr std::size_t tuple_size_v = tuple_size<T>::value;
}} // namespace boost::pfr
#endif // BOOST_PFR_FIELDS_COUNT_HPP

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/functors.hpp>
/// \def BOOST_PFR_FLAT_FUNCTIONS_FOR(T)
/// Defines comparison operators and stream operators for T.
/// If POD is comparable or streamable using it's own operator (but not it's conversion operator), then the original operator is used.
///
/// \b Example:
/// \code
/// #include <boost/pfr/flat_functions_for.hpp>
/// struct comparable_struct { // No operators defined for that structure
/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
/// };
/// BOOST_PFR_FLAT_FUNCTIONS_FOR(comparable_struct)
/// // ...
///
/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
/// assert(s1 < s2);
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
/// \endcode
///
/// \podops for other ways to define operators and more details.
///
/// \b Defines \b following \b for \b T:
/// \code
/// bool operator==(const T& lhs, const T& rhs) noexcept;
/// bool operator!=(const T& lhs, const T& rhs) noexcept;
/// bool operator< (const T& lhs, const T& rhs) noexcept;
/// bool operator> (const T& lhs, const T& rhs) noexcept;
/// bool operator<=(const T& lhs, const T& rhs) noexcept;
/// bool operator>=(const T& lhs, const T& rhs) noexcept;
///
/// template <class Char, class Traits>
/// std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, const T& value);
///
/// template <class Char, class Traits>
/// std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, T& value);
///
/// // helper function for Boost unordered containers and boost::hash<>.
/// std::size_t hash_value(const T& value) noexcept;
/// \endcode
#define BOOST_PFR_FLAT_FUNCTIONS_FOR(T) \
static inline bool operator==(const T& lhs, const T& rhs) noexcept { return ::boost::pfr::flat_equal_to<T>{}(lhs, rhs); } \
static inline bool operator!=(const T& lhs, const T& rhs) noexcept { return ::boost::pfr::flat_not_equal<T>{}(lhs, rhs); } \
static inline bool operator< (const T& lhs, const T& rhs) noexcept { return ::boost::pfr::flat_less<T>{}(lhs, rhs); } \
static inline bool operator> (const T& lhs, const T& rhs) noexcept { return ::boost::pfr::flat_greater<T>{}(lhs, rhs); } \
static inline bool operator<=(const T& lhs, const T& rhs) noexcept { return ::boost::pfr::flat_less_equal<T>{}(lhs, rhs); } \
static inline bool operator>=(const T& lhs, const T& rhs) noexcept { return ::boost::pfr::flat_greater_equal<T>{}(lhs, rhs); } \
template <class Char, class Traits> \
static ::std::basic_ostream<Char, Traits>& operator<<(::std::basic_ostream<Char, Traits>& out, const T& value) { \
::boost::pfr::flat_write(out, value); \
return out; \
} \
template <class Char, class Traits> \
static ::std::basic_istream<Char, Traits>& operator>>(::std::basic_istream<Char, Traits>& in, T& value) { \
::boost::pfr::flat_read(in, value); \
return in; \
} \
static inline std::size_t hash_value(const T& v) noexcept { \
return ::boost::pfr::flat_hash<T>{}(v); \
} \
/**/

View File

@@ -0,0 +1,162 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/functors.hpp>
/// \file boost/pfr/flat_ops.hpp
/// Contains comparison operators and stream operators for any POD types that do not have their own operators.
/// If POD is comparable or streamable using it's own operator or it's conversion operator, then the original operator is used.
///
/// Just write \b using \b namespace \b flat_ops; and operators will be available in scope.
///
/// \b Example:
/// \code
/// #include <boost/pfr/flat_ops.hpp>
/// struct comparable_struct { // No operators defined for that structure
/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
/// };
/// // ...
///
/// using namespace flat_ops;
///
/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
/// assert(s1 < s2);
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
/// \endcode
///
/// \podops for other ways to define operators and more details.
///
/// \b This \b header \b contains:
namespace boost { namespace pfr {
namespace detail {
///////////////////// `value` is true if Detector<Tleft, Tright> does not compile (SFINAE)
template <template <class, class> class Detector, class Tleft, class Tright>
struct not_appliable {
struct success{};
template <class Tl, class Tr> static Detector<Tl, Tr> detector_impl(long) noexcept;
template <class Tl, class Tr> static success detector_impl(int) noexcept;
static constexpr bool value = std::is_same<
decltype(detector_impl<Tleft, Tright>(1L)),
success
>::value;
};
///////////////////// Detectors for different operators
template <class T1, class T2> using comp_eq_detector = decltype(std::declval<T1>() == std::declval<T2>());
template <class T1, class T2> using comp_ne_detector = decltype(std::declval<T1>() != std::declval<T2>());
template <class T1, class T2> using comp_lt_detector = decltype(std::declval<T1>() < std::declval<T2>());
template <class T1, class T2> using comp_le_detector = decltype(std::declval<T1>() <= std::declval<T2>());
template <class T1, class T2> using comp_gt_detector = decltype(std::declval<T1>() > std::declval<T2>());
template <class T1, class T2> using comp_ge_detector = decltype(std::declval<T1>() >= std::declval<T2>());
template <class S, class T> using ostreamable_detector = decltype(std::declval<S>() << std::declval<T>());
template <class S, class T> using istreamable_detector = decltype(std::declval<S>() >> std::declval<T>());
///////////////////// Helper typedef that it used by all the enable_not_*_comp_t
template <template <class, class> class Detector, class T>
using enable_not_comp_base_t = typename std::enable_if<
not_appliable<Detector, T const&, T const&>::value && std::is_pod<T>::value,
bool
>::type;
///////////////////// std::enable_if_t like functions that enable only if types do not support operation and are PODs
template <class T> using enable_not_eq_comp_t = enable_not_comp_base_t<comp_eq_detector, T>;
template <class T> using enable_not_ne_comp_t = enable_not_comp_base_t<comp_ne_detector, T>;
template <class T> using enable_not_lt_comp_t = enable_not_comp_base_t<comp_lt_detector, T>;
template <class T> using enable_not_le_comp_t = enable_not_comp_base_t<comp_le_detector, T>;
template <class T> using enable_not_gt_comp_t = enable_not_comp_base_t<comp_gt_detector, T>;
template <class T> using enable_not_ge_comp_t = enable_not_comp_base_t<comp_ge_detector, T>;
template <class Stream, class Type>
using enable_not_ostreamable_t = typename std::enable_if<
not_appliable<ostreamable_detector, Stream&, Type const&>::value && std::is_pod<Type>::value,
Stream&
>::type;
template <class Stream, class Type>
using enable_not_istreamable_t = typename std::enable_if<
not_appliable<istreamable_detector, Stream&, Type&>::value && std::is_pod<Type>::value,
Stream&
>::type;
} // namespace detail
namespace flat_ops {
#ifdef BOOST_PFR_DOXYGEN_INVOKED
template <class T> bool operator==(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator!=(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator< (const T& lhs, const T& rhs) noexcept;
template <class T> bool operator> (const T& lhs, const T& rhs) noexcept;
template <class T> bool operator<=(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator>=(const T& lhs, const T& rhs) noexcept;
template <class Char, class Traits, class T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, const T& value);
template <class Char, class Traits, class T>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, T& value);
/// \brief helper function for Boost
template <class T> std::size_t hash_value(const T& value) noexcept;
#else
template <class T>
static detail::enable_not_eq_comp_t<T> operator==(const T& lhs, const T& rhs) noexcept {
return flat_equal_to<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_ne_comp_t<T> operator!=(const T& lhs, const T& rhs) noexcept {
return flat_not_equal<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_lt_comp_t<T> operator<(const T& lhs, const T& rhs) noexcept {
return flat_less<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_gt_comp_t<T> operator>(const T& lhs, const T& rhs) noexcept {
return flat_greater<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_le_comp_t<T> operator<=(const T& lhs, const T& rhs) noexcept {
return flat_less_equal<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_ge_comp_t<T> operator>=(const T& lhs, const T& rhs) noexcept {
return flat_greater_equal<T>{}(lhs, rhs);
}
template <class Char, class Traits, class T>
static detail::enable_not_ostreamable_t<std::basic_ostream<Char, Traits>, T> operator<<(std::basic_ostream<Char, Traits>& out, const T& value) {
flat_write(out, value);
return out;
}
template <class Char, class Traits, class T>
static detail::enable_not_istreamable_t<std::basic_istream<Char, Traits>, T> operator>>(std::basic_istream<Char, Traits>& in, T& value) {
flat_read(in, value);
return in;
}
template <class T>
static std::enable_if_t<std::is_pod<T>::value, std::size_t> hash_value(const T& value) noexcept {
return flat_hash<T>{}(value);
}
#endif
} // namespace flat_ops
}} // namespace boost::pfr

View File

@@ -0,0 +1,357 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_FUNCTORS_HPP
#define BOOST_PFR_FUNCTORS_HPP
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/core.hpp>
/// \file boost/pfr/functors.hpp
/// Contains functors that can work with PODs and are close to the Standard Library ones.
/// Each functor \flattening{flattens} the POD type and iterates over its fields.
namespace boost { namespace pfr {
namespace detail {
template <std::size_t I, std::size_t N>
struct equal_impl {
template <class T, class U>
constexpr static bool cmp(const T& v1, const U& v2) noexcept {
return ::boost::pfr::detail::sequence_tuple::get<I>(v1) == ::boost::pfr::detail::sequence_tuple::get<I>(v2)
&& equal_impl<I + 1, N>::cmp(v1, v2);
}
};
template <std::size_t N>
struct equal_impl<N, N> {
template <class T, class U>
constexpr static bool cmp(const T&, const U&) noexcept {
return T::size_v == U::size_v;
}
};
template <std::size_t I, std::size_t N>
struct not_equal_impl {
template <class T, class U>
constexpr static bool cmp(const T& v1, const U& v2) noexcept {
return ::boost::pfr::detail::sequence_tuple::get<I>(v1) != ::boost::pfr::detail::sequence_tuple::get<I>(v2)
|| not_equal_impl<I + 1, N>::cmp(v1, v2);
}
};
template <std::size_t N>
struct not_equal_impl<N, N> {
template <class T, class U>
constexpr static bool cmp(const T&, const U&) noexcept {
return T::size_v != U::size_v;
}
};
template <std::size_t I, std::size_t N>
struct less_impl {
template <class T, class U>
constexpr static bool cmp(const T& v1, const U& v2) noexcept {
using ::boost::pfr::detail::sequence_tuple::get;
return get<I>(v1) < get<I>(v2)
|| (get<I>(v1) == get<I>(v2) && less_impl<I + 1, N>::cmp(v1, v2));
}
};
template <std::size_t N>
struct less_impl<N, N> {
template <class T, class U>
constexpr static bool cmp(const T&, const U&) noexcept {
return T::size_v < U::size_v;
}
};
template <std::size_t I, std::size_t N>
struct less_equal_impl {
template <class T, class U>
constexpr static bool cmp(const T& v1, const U& v2) noexcept {
using ::boost::pfr::detail::sequence_tuple::get;
return get<I>(v1) < get<I>(v2)
|| (get<I>(v1) == get<I>(v2) && less_equal_impl<I + 1, N>::cmp(v1, v2));
}
};
template <std::size_t N>
struct less_equal_impl<N, N> {
template <class T, class U>
constexpr static bool cmp(const T&, const U&) noexcept {
return T::size_v <= U::size_v;
}
};
template <std::size_t I, std::size_t N>
struct greater_impl {
template <class T, class U>
constexpr static bool cmp(const T& v1, const U& v2) noexcept {
using ::boost::pfr::detail::sequence_tuple::get;
return get<I>(v1) > get<I>(v2)
|| (get<I>(v1) == get<I>(v2) && greater_impl<I + 1, N>::cmp(v1, v2));
}
};
template <std::size_t N>
struct greater_impl<N, N> {
template <class T, class U>
constexpr static bool cmp(const T&, const U&) noexcept {
return T::size_v > U::size_v;
}
};
template <std::size_t I, std::size_t N>
struct greater_equal_impl {
template <class T, class U>
constexpr static bool cmp(const T& v1, const U& v2) noexcept {
using ::boost::pfr::detail::sequence_tuple::get;
return get<I>(v1) > get<I>(v2)
|| (get<I>(v1) == get<I>(v2) && greater_equal_impl<I + 1, N>::cmp(v1, v2));
}
};
template <std::size_t N>
struct greater_equal_impl<N, N> {
template <class T, class U>
constexpr static bool cmp(const T&, const U&) noexcept {
return T::size_v >= U::size_v;
}
};
template <typename SizeT>
constexpr void hash_combine(SizeT& seed, SizeT value) noexcept {
seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
template <std::size_t I, std::size_t N>
struct hash_impl {
template <class T>
constexpr static std::size_t compute(const T& val) noexcept {
typedef std::decay_t<typename detail::sequence_tuple::tuple_element<I, T>::type> elem_t;
std::size_t h = std::hash<elem_t>()( ::boost::pfr::detail::sequence_tuple::get<I>(val) );
hash_combine(h, hash_impl<I + 1, N>::compute(val) );
return h;
}
};
template <std::size_t N>
struct hash_impl<N, N> {
template <class T>
constexpr static std::size_t compute(const T&) noexcept {
return 0;
}
};
///////////////////// Define min_element and to avoid inclusion of <algorithm>
constexpr std::size_t min_size(std::size_t x, std::size_t y) noexcept {
return x < y ? x : y;
}
} // namespace detail
///////////////////// Comparisons
/// \brief std::equal_to like flattening comparator
template <class T = void> struct flat_equal_to {
/// \return \b true if each field of \b x equals the field with same index of \b y
bool operator()(const T& x, const T& y) const noexcept {
return detail::equal_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const noexcept;
#endif
};
/// @cond
template <> struct flat_equal_to<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const noexcept {
return detail::equal_impl<
0,
detail::min_size(flat_tuple_size_v<T>, flat_tuple_size_v<U>)
>::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
};
/// @endcond
/// \brief std::not_equal like flattening comparator
template <class T = void> struct flat_not_equal {
/// \return \b true if at least one field \b x not equals the field with same index of \b y
bool operator()(const T& x, const T& y) const noexcept {
return detail::not_equal_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const noexcept;
#endif
};
/// @cond
template <> struct flat_not_equal<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const noexcept {
return detail::not_equal_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::greater like flattening comparator
template <class T = void> struct flat_greater {
/// \return \b true if field of \b x greater than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y
bool operator()(const T& x, const T& y) const noexcept {
return detail::greater_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const noexcept;
#endif
};
/// @cond
template <> struct flat_greater<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const noexcept {
return detail::greater_impl<
0,
detail::min_size(flat_tuple_size_v<T>, flat_tuple_size_v<U>)
>::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::less like flattening comparator
template <class T = void> struct flat_less {
/// \return \b true if field of \b x less than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y
bool operator()(const T& x, const T& y) const noexcept {
return detail::less_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const noexcept;
#endif
};
/// @cond
template <> struct flat_less<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const noexcept {
return detail::less_impl<
0,
detail::min_size(flat_tuple_size_v<T>, flat_tuple_size_v<U>)
>::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::greater_equal like flattening comparator
template <class T = void> struct flat_greater_equal {
/// \return \b true if field of \b x greater than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y;
/// or if each field of \b x equals the field with same index of \b y .
bool operator()(const T& x, const T& y) const noexcept {
return detail::greater_equal_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const noexcept;
#endif
};
/// @cond
template <> struct flat_greater_equal<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const noexcept {
return detail::greater_equal_impl<
0,
detail::min_size(flat_tuple_size_v<T>, flat_tuple_size_v<U>)
>::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::less_equal like flattening comparator
template <class T = void> struct flat_less_equal {
/// \return \b true if field of \b x less than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y;
/// or if each field of \b x equals the field with same index of \b y .
bool operator()(const T& x, const T& y) const noexcept {
return detail::less_equal_impl<0, flat_tuple_size_v<T> >::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const noexcept;
#endif
};
/// @cond
template <> struct flat_less_equal<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const noexcept {
return detail::less_equal_impl<
0,
detail::min_size(flat_tuple_size_v<T>, flat_tuple_size_v<U>)
>::cmp(detail::tie_as_flat_tuple(x), detail::tie_as_flat_tuple(y));
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::hash like flattening functor
template <class T> struct flat_hash {
/// \return hash value of \b x
std::size_t operator()(const T& x) const noexcept {
return detail::hash_impl<0, flat_tuple_size_v<T> >::compute(detail::tie_as_flat_tuple(x));
}
};
}} // namespace boost::pfr
#endif // BOOST_PFR_FUNCTORS_HPP

View File

@@ -0,0 +1,110 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/functors.hpp>
/// \file boost/pfr/global_flat_ops.hpp
/// Contains comparison operators and stream operators for any POD types that do not have their own operators.
/// If POD is comparable or streamable using it's own operator (but not it's conversion operator), then the original operator is used.
///
/// \b Example:
/// \code
/// #include <boost/pfr/global_flat_ops.hpp>
/// struct comparable_struct { // No operators defined for that structure
/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
/// };
/// // ...
///
/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
/// assert(s1 < s2);
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
/// \endcode
///
/// \podops for other ways to define operators and more details.
///
/// \b This \b header \b defines:
/// @cond
namespace boost { namespace pfr { namespace detail {
template <class T, class U>
using enable_comparisons = std::enable_if_t<
std::is_same<T, U>::value && std::is_pod<T>::value,
bool
>;
}}} // namespace boost::pfr::detail
/// @endcond
#ifdef BOOST_PFR_DOXYGEN_INVOKED
template <class T> bool operator==(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator!=(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator< (const T& lhs, const T& rhs) noexcept;
template <class T> bool operator> (const T& lhs, const T& rhs) noexcept;
template <class T> bool operator<=(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator>=(const T& lhs, const T& rhs) noexcept;
template <class Char, class Traits, class T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, const T& value);
template <class Char, class Traits, class T>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, T& value);
/// \brief helper function for Boost unordered containers and boost::hash<>.
template <class T> std::size_t hash_value(const T& value) noexcept;
#else
template <class T, class U>
static boost::pfr::detail::enable_comparisons<T, U> operator==(const T& lhs, const U& rhs) noexcept {
return ::boost::pfr::flat_equal_to<T>{}(lhs, rhs);
}
template <class T, class U>
static boost::pfr::detail::enable_comparisons<T, U> operator!=(const T& lhs, const U& rhs) noexcept {
return ::boost::pfr::flat_not_equal<T>{}(lhs, rhs);
}
template <class T, class U>
static boost::pfr::detail::enable_comparisons<T, U> operator<(const T& lhs, const U& rhs) noexcept {
return ::boost::pfr::flat_less<T>{}(lhs, rhs);
}
template <class T, class U>
static boost::pfr::detail::enable_comparisons<T, U> operator>(const T& lhs, const U& rhs) noexcept {
return ::boost::pfr::flat_greater<T>{}(lhs, rhs);
}
template <class T, class U>
static boost::pfr::detail::enable_comparisons<T, U> operator<=(const T& lhs, const U& rhs) noexcept {
return ::boost::pfr::flat_less_equal<T>{}(lhs, rhs);
}
template <class T, class U>
static boost::pfr::detail::enable_comparisons<T, U> operator>=(const T& lhs, const U& rhs) noexcept {
return ::boost::pfr::flat_greater_equal<T>{}(lhs, rhs);
}
template <class Char, class Traits, class T>
static std::enable_if_t<std::is_pod<T>::value, std::basic_ostream<Char, Traits>&> operator<<(std::basic_ostream<Char, Traits>& out, const T& value) {
::boost::pfr::flat_write(out, value);
return out;
}
template <class Char, class Traits, class T>
static std::enable_if_t<std::is_pod<T>::value, std::basic_istream<Char, Traits>&> operator>>(std::basic_istream<Char, Traits>& in, T& value) {
::boost::pfr::flat_read(in, value);
return in;
}
template <class T>
static std::enable_if_t<std::is_pod<T>::value, std::size_t> hash_value(const T& value) noexcept {
return ::boost::pfr::flat_hash<T>{}(value);
}
#endif

File diff suppressed because it is too large Load Diff

471
main.cpp
View File

@@ -1,471 +0,0 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "magic_get.hpp"
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <sstream>
#include <set>
#include <vector>
#include <cassert>
#include <algorithm>
#include <unordered_set>
template <std::size_t I, class T>
void print(T& f) {
std::cout << flat_get<I>(f) << "\t\t"
<< typeid(flat_tuple_element_t<I, T>).name()
<< std::endl;
}
struct make_my_life_harder { int a0; short a1; };
struct make_my_life_even_more_harder { unsigned int b0; unsigned short b1; make_my_life_harder cr;};
struct foo {
unsigned char v0;
unsigned int v1;
unsigned short v2;
unsigned long long v3;
unsigned char v4and5[2];
int v6;
std::size_t v7;
int* v8;
const void* v9;
int const**const volatile**volatile** v10;
const double v11;
make_my_life_harder v12and13;
make_my_life_even_more_harder v14and15andv16and17;
};
void test_print() {
foo f {
'A', 11, 12, 13, {'B', 'C'}, 16, 17, 0, 0, 0, 30.1
, {18, 19}
, {20, 21, {22, 23}}
};
print<0>(f); print<1>(f); print<2>(f);
print<3>(f); print<4>(f); print<5>(f);
print<6>(f); print<7>(f); print<8>(f);
print<9>(f); print<10>(f); print<11>(f);
print<12>(f); print<13>(f); print<14>(f);
print<15>(f); print<16>(f); print<17>(f);
static_assert(flat_tuple_size_v<foo> == 18, "failed tuple size check");
int a[] = {0, 1, 2, 3};
std::cout << '\n' << flat_get<1>(a) << std::endl;
int b[2][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}};
std::cout << flat_get<4>(b) << std::endl;
int i = 777;
std::cout << flat_get<0>(i) << std::endl;
}
void test_runtime(const foo& f) {
assert( flat_get<0>(f) == f.v0);
assert( flat_get<1>(f) == f.v1);
assert( flat_get<2>(f) == f.v2);
assert( flat_get<3>(f) == f.v3);
assert( flat_get<4>(f) == f.v4and5[0]);
assert( flat_get<5>(f) == f.v4and5[1]);
assert( flat_get<6>(f) == f.v6);
assert( flat_get<7>(f) == f.v7);
assert( flat_get<8>(f) == f.v8);
assert( flat_get<9>(f) == f.v9);
assert( flat_get<10>(f) == f.v10);
assert( flat_get<11>(f) < f.v11 + 0.001); assert( flat_get<11>(f) > f.v11 - 0.001);
assert( flat_get<12>(f) == f.v12and13.a0);
assert( flat_get<13>(f) == f.v12and13.a1);
assert( flat_get<14>(f) == f.v14and15andv16and17.b0);
assert( flat_get<15>(f) == f.v14and15andv16and17.b1);
assert( flat_get<16>(f) == f.v14and15andv16and17.cr.a0);
assert( flat_get<17>(f) == f.v14and15andv16and17.cr.a1);
}
template <class T>
void test_compiletime() {
constexpr T f{};
static_assert(flat_tuple_size_v<foo> == 18, "failed tuple size check");
static_assert( std::is_same< decltype(flat_get<0>(f)), decltype((f.v0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<1>(f)), decltype((f.v1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<2>(f)), decltype((f.v2))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<3>(f)), decltype((f.v3))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<4>(f)), decltype((f.v4and5[0]))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<5>(f)), decltype((f.v4and5[1]))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<6>(f)), decltype((f.v6))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<7>(f)), decltype((f.v7))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<8>(f)), decltype((f.v8))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<9>(f)), decltype((f.v9))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<10>(f)), decltype((f.v10))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<11>(f)), decltype((f.v11))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<12>(f)), decltype((f.v12and13.a0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<13>(f)), decltype((f.v12and13.a1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<14>(f)), decltype((f.v14and15andv16and17.b0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<15>(f)), decltype((f.v14and15andv16and17.b1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<16>(f)), decltype((f.v14and15andv16and17.cr.a0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<17>(f)), decltype((f.v14and15andv16and17.cr.a1))>::value, "types missmatch");
}
template <class T>
constexpr void test_compiletime_array() {
{
constexpr T f[20] = {0};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
{
constexpr T f[2][10] = {0};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
{
constexpr T f[2][5][2] = {0};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
}
//#define TEST_REF
#ifdef TEST_REF
struct with_ref {
int i;
int& ref;
};
void test() {
int ref_me = 1234567890;
with_ref f { 987654321, ref_me };
print<0>(f); print<1>(f);
}
#endif
void test_with_enums() {
enum class my_enum: unsigned {
VALUE1 = 17, VALUE2, VALUE3
};
struct my_struct { my_enum e; int i; short s; };
my_struct s {my_enum::VALUE1, 10, 11};
std::tuple<unsigned, int, short> t = flat_make_tuple(s);
assert(std::get<0>(t) == 17);
assert(std::get<1>(t) == 10);
assert(std::get<2>(t) == 11);
flat_get<1>(s) = 101;
assert(flat_get<1>(s) == 101);
flat_get<2>(s) = 111;
assert(flat_get<2>(s) == 111);
assert(flat_tie(s) == flat_tie(s));
assert(flat_tie(s) == flat_make_tuple(s));
assert(flat_tie(s) != t);
flat_tie(s) = t;
assert(flat_get<0>(s) == 17);
assert(flat_get<1>(s) == 10);
assert(flat_get<2>(s) == 11);
static_assert(std::is_same<
int, flat_tuple_element_t<1, my_struct>
>::value, "");
static_assert(std::is_same<
short, flat_tuple_element_t<2, my_struct>
>::value, "");
static_assert(std::is_same<
const int, flat_tuple_element_t<1, const my_struct>
>::value, "");
static_assert(std::is_same<
volatile short, flat_tuple_element_t<2, volatile my_struct>
>::value, "");
static_assert(std::is_same<
const volatile short, flat_tuple_element_t<2, const volatile my_struct>
>::value, "");
static_assert(
3 == flat_tuple_size_v<const volatile my_struct>,
""
);
}
void test_comparable_struct() {
struct comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
using namespace pod_ops;
comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
comparable_struct s2 = s1;
comparable_struct s3 {0, 1, "Hello", false, 6,7,8,9,10,11111};
assert(s1 == s2);
assert(s1 <= s2);
assert(s1 >= s2);
assert(!(s1 != s2));
assert(!(s1 == s3));
assert(s1 != s3);
assert(s1 < s3);
assert(s3 > s2);
assert(s1 <= s3);
assert(s3 >= s2);
std::cout << s1 << std::endl;
comparable_struct s4;
std::stringstream ss;
ss.exceptions ( std::ios::failbit);
ss << s1;
ss >> s4;
std::cout << s4 << std::endl;
assert(s1 == s4);
int i = 1, j = 2;
assert(i != j);
}
void test_empty_struct() {
struct empty {};
using namespace pod_ops;
std::cout << empty{} << std::endl;
assert(empty{} == empty{});
}
void test_pods_with_int_operators() {
using namespace pod_ops;
std::stringstream ss;
ss << std::is_pod<int>{};
int i = 0;
ss >> i;
assert(i == 1);
std::cout << i << std::endl;
}
void test_struct_with_single_field() {
struct f1 { int i; };
using namespace pod_ops;
std::stringstream ss;
ss << f1{ 777 };
f1 var{};
ss >> var;
assert(var.i == 777);
assert(var == f1{ 777 });
assert(var != f1{ 778 });
assert(var <= f1{ 777 });
assert(var <= f1{ 778 });
assert(var < f1{ 778 });
assert(var >= f1{ 777 });
assert(var >= f1{ 776 });
assert(var > f1{ 776 });
}
template <class Comparator>
void test_with_contatiners() {
struct testing { bool b1, b2; int i; };
struct testing2 { bool b1, b2; int i; };
std::set<testing, Comparator > t{
{true, true, 100},
{false, true, 100},
{true, false, 100},
{true, true, 101}
};
assert(t.find({true, true, 100}) != t.end());
assert(t.count({true, true, 100}) == 1);
assert(t.find(testing2{true, true, 100}) != t.end());
std::set<testing2, Comparator > t2{
{true, true, 101},
{true, true, 100},
{true, false, 100},
{false, true, 100}
};
assert(std::equal(t.begin(), t.end(), t2.begin(), t2.end(), flat_equal_to<>{}));
assert(!std::equal(t.begin(), t.end(), t2.begin(), t2.end(), flat_not_equal<>{}));
std::vector<testing> res;
std::set_intersection(t.begin(), t.end(), t2.begin(), t2.end(),
std::back_inserter(res), Comparator{});
assert(res.size() == 4);
}
void test_with_user_defined_constructor() {
struct pr {
int i;
short s;
pr() = default;
pr(const pr&) = default;
pr(pr&&) = default;
pr(int ii, short is) noexcept
: i(ii), s(is)
{}
};
pr p{1, 2};
//assert(flat_get<1>(p) == 2); // Compilation error
}
template <class T1, std::size_t CountInT, std::size_t CountHelpers>
void test_counts_on_multiple_chars_impl_1() {
struct t1_c { T1 v1; char c[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_c> == CountInT + CountHelpers, "");
struct t1_s { T1 v1; short s[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_s> == CountInT + CountHelpers, "");
struct t1_i { T1 v1; int i[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_i> == CountInT + CountHelpers, "");
struct t1_p { T1 v1; void* p[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_p> == CountInT + CountHelpers, "");
struct t1_ll { T1 v1; long long ll[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_ll> == CountInT + CountHelpers, "");
struct rt1_c { char c[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_c> == CountInT + CountHelpers, "");
struct rt1_s { short s[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_s> == CountInT + CountHelpers, "");
struct rt1_i { int i[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_i> == CountInT + CountHelpers, "");
struct rt1_p { void* p[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_p> == CountInT + CountHelpers, "");
struct rt1_ll { long long ll[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_ll> == CountInT + CountHelpers, "");
}
template <class T1, std::size_t CountInT>
void test_counts_on_multiple_chars_impl() {
struct t1_0 { T1 v1; };
static_assert(flat_tuple_size_v<t1_0> == CountInT, "");
static_assert(flat_tuple_size_v<T1> == CountInT, "");
static_assert(flat_tuple_size_v<std::conditional_t<std::is_fundamental<T1>::value, T1*, void*> > == 1, "");
static_assert(flat_tuple_size_v<T1[5]> == CountInT*5, "");
test_counts_on_multiple_chars_impl_1<T1, CountInT, 1>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 2>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 3>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 4>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 5>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 6>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 7>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 8>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 9>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 10>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 11>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 12>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 13>();/*
test_counts_on_multiple_chars_impl_1<T1, CountInT, 14>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 15>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 16>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 17>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 18>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 19>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 20>();*/
}
template <class T>
void test_counts_on_multiple_chars() {
test_counts_on_multiple_chars_impl<T, 1>();
struct t2 { T v1; T v2; };
test_counts_on_multiple_chars_impl<t2, 2>();
test_counts_on_multiple_chars_impl<T[2], 2>();
test_counts_on_multiple_chars_impl<T[3], 3>();
test_counts_on_multiple_chars_impl<T[4], 4>();
struct t8 { T v1; T v2; T v3; T v4; T v5; T v6; T v7; T v8; };
test_counts_on_multiple_chars_impl<t8, 8>();
}
void test_hash() {
struct almost_pair { int i; short s; };
std::unordered_set<almost_pair, flat_hash<almost_pair>, flat_equal_to<> > s;
s.insert({0, 1});
s.insert({1, 0});
s.insert({1, 1});
assert(s.size() == 3);
flat_hash<almost_pair> hs;
assert(hs({0, 1}) != hs({1, 0}));
assert(hs({0, 1}) == hs({0, 1}));
assert(hs({1, 1}) == hs({1, 1}));
assert(hs({0, 0}) != hs({1, 1}));
struct single_field { int i; };
assert(flat_hash<single_field>()({1}) != std::hash<int>()(1));
assert(flat_hash<single_field>()({199}) != std::hash<int>()(199));
}
int main() {
test_compiletime<foo>();
test_compiletime_array<int>();
test_compiletime_array<void*>();
test_compiletime_array<const void*>();
test_compiletime_array<char>();
test_compiletime_array<char const volatile*>();
{
foo f {
'A', 11, 12, 13, {'B', 'C'}, 16, 17, 0, 0, 0, 30.1
, {18, 19}
, {20, 21, {22, 23}}
};
test_runtime(f);
}
{
foo f {
'\0', 12437, 1212, 13, {'1', '7'}, 163, 1723, 0, 0, 0, 3000.1
, {-18, -19}
, {656565, 65535, {-22, -23}}
};
test_runtime(f);
}
test_with_enums();
test_comparable_struct();
test_empty_struct();
test_pods_with_int_operators();
test_struct_with_single_field();
test_with_contatiners<flat_less<>>();
test_with_contatiners<flat_greater<>>();
test_print();
test_with_user_defined_constructor();
test_counts_on_multiple_chars<char>();
test_counts_on_multiple_chars<short>();
test_counts_on_multiple_chars<int>();
test_counts_on_multiple_chars<void*>();
test_counts_on_multiple_chars<long long>();
test_hash();
}

109
misc/generate_cpp17.py Normal file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/python
# Copyright (c) 2016 Antony Polukhin
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
############################################################################################################################
import sys
import string
# Skipping some letters that mey produce keywords or are hard to read
ascii_letters = string.ascii_letters.replace("o", "").replace("O", "").replace("i", "").replace("I", "")
PROLOGUE = """// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////// This is an auto generated header. Modify pfr/misc/generate_cpp17.py instead. ////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_PFR_CORE17_GENERATED_HPP
#define BOOST_PFR_CORE17_GENERATED_HPP
#pragma once
#if __cplusplus < 201606L /* Oulu meeting, not the exact value */
# error C++17 is required for this header.
#endif
#include <boost/pfr/core.hpp>
namespace boost { namespace pfr { namespace detail {
template <class... Args>
constexpr auto make_tuple_of_references(Args&&... args) noexcept {
return sequence_tuple::tuple<Args&...>{ std::forward<Args>(args)... };
}
template <class T>
constexpr auto as_tuple_impl(T&& /*val*/, size_t_<0>) noexcept {
return sequence_tuple::tuple<>{};
}
"""
############################################################################################################################
EPILOGUE = """
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class T>
constexpr auto as_tuple(const T& val) noexcept {
typedef size_t_<fields_count<T>()> fields_count_tag;
return detail::as_tuple_impl(val, fields_count_tag{});
}
template <class T>
constexpr auto as_tuple(T& val) noexcept {
typedef size_t_<fields_count<T>()> fields_count_tag;
return detail::as_tuple_impl(val, fields_count_tag{});
}
template <class T>
using as_tuple_t = decltype( ::boost::pfr::detail::as_tuple(std::declval<T&>()) );
}}} // namespace boost::pfr::detail
#endif // BOOST_PFR_CORE17_GENERATED_HPP
"""
############################################################################################################################
indexes = ""
print PROLOGUE
funcs_count = 100 if len(sys.argv) == 1 else int(sys.argv[1])
max_args_on_a_line = len(ascii_letters)
for i in xrange(funcs_count):
if i == 0:
indexes = " "
elif i % max_args_on_a_line == 0:
indexes += ",\n "
else:
indexes += ","
if i >= max_args_on_a_line:
indexes += ascii_letters[i / max_args_on_a_line - 1]
indexes += ascii_letters[i % max_args_on_a_line]
print "template <class T>"
print "constexpr auto as_tuple_impl(T&& val, size_t_<" + str(i + 1) + ">) noexcept {"
if i < max_args_on_a_line:
print " auto& [" + indexes.strip() + "] = std::forward<T>(val);"
print " return ::boost::pfr::detail::make_tuple_of_references(" + indexes.strip() + ");"
else:
print " auto& ["
print indexes
print " ] = std::forward<T>(val);"
print ""
print " return ::boost::pfr::detail::make_tuple_of_references("
print indexes
print " );"
print "}\n"
print EPILOGUE

4
misc/generate_single.sh Normal file
View File

@@ -0,0 +1,4 @@
cat ../include/boost/pfr/core.hpp
sed -e '1,/boost\/pfr/d' ../include/boost/pfr/functors.hpp
sed -e '1,/boost\/pfr/d' ../include/boost/pfr/flat_functions_for.hpp
sed -e '1,/boost\/pfr/d' ../include/boost/pfr/flat_ops.hpp

29
test/Jamfile.v2 Normal file
View File

@@ -0,0 +1,29 @@
# Copyright (C) 2016, Antony Polukhin.
#
# Use, modification and distribution is subject to the Boost Software License,
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
#
test-suite pfr
:
[ run core.cpp ]
[ run minimal.cpp ]
[ run count_fields_on_chars.cpp ]
# [ run count_fields_on_ints.cpp ]
# [ run count_fields_on_long_longs.cpp ]
# [ run count_fields_on_shorts.cpp ]
[ run count_fields_on_void_ptrs.cpp ]
[ run std_interactions.cpp ]
[ run flat_functions_for.cpp ]
[ run flat_ops.cpp ]
[ run global_flat_ops.cpp ]
[ compile-fail non_aggregate1.cpp ]
[ compile-fail non_aggregate2.cpp ]
[ compile-fail bitfields.cpp ]
[ run motivating_example.cpp ]
[ run ../example/examples.cpp ]
[ run only_fields_count.cpp ]
;

23
test/bitfields.cpp Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/core.hpp>
#include <boost/core/lightweight_test.hpp>
struct bf {
unsigned int i1: 1;
unsigned int i2: 1;
unsigned int i3: 1;
unsigned int i4: 1;
unsigned int i5: 1;
unsigned int i6: 1;
};
int main() {
(void)boost::pfr::flat_tuple_size<bf>::value; // Must be a compile time error
return boost::report_errors();
}

709
test/core.cpp Normal file
View File

@@ -0,0 +1,709 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//#define BOOST_PFR_RELAX_POD_REQUIREMENT
#include <boost/pfr.hpp>
#include <boost/core/lightweight_test.hpp>
//#include <boost/type_index.hpp> // for debugging
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <sstream>
#include <set>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <cstring>
using namespace boost::pfr;
template <std::size_t I, class T>
void print(T& f) {
std::cout << flat_get<I>(f) << "\t\t"
<< typeid(flat_tuple_element_t<I, T>).name()
<< std::endl;
}
struct make_my_life_harder { int a0; short a1; };
struct make_my_life_even_more_harder { unsigned int b0; unsigned short b1; make_my_life_harder cr;};
struct foo {
unsigned char v0;
unsigned int v1;
unsigned short v2;
unsigned long long v3;
unsigned char v4and5[2];
int v6;
std::size_t v7;
int* v8;
const void* v9;
int const**const volatile**volatile** v10;
const double v11;
make_my_life_harder v12and13;
make_my_life_even_more_harder v14and15andv16and17;
};
void test_print() {
foo f {
'A', 11, 12, 13, {'B', 'C'}, 16, 17, 0, 0, 0, 30.1
, {18, 19}
, {20, 21, {22, 23}}
};
print<0>(f); print<1>(f); print<2>(f);
print<3>(f); print<4>(f); print<5>(f);
print<6>(f); print<7>(f); print<8>(f);
print<9>(f); print<10>(f); print<11>(f);
print<12>(f); print<13>(f); print<14>(f);
print<15>(f); print<16>(f); print<17>(f);
static_assert(flat_tuple_size_v<foo> == 18, "failed tuple size check");
int a[] = {0, 1, 2, 3};
std::cout << '\n' << flat_get<1>(a) << std::endl;
int b[2][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}};
std::cout << flat_get<4>(b) << std::endl;
int i = 777;
std::cout << flat_get<0>(i) << std::endl;
}
void test_runtime(const foo& f) {
BOOST_TEST_EQ( flat_get<0>(f), f.v0);
BOOST_TEST_EQ( flat_get<1>(f), f.v1);
BOOST_TEST_EQ( flat_get<2>(f), f.v2);
BOOST_TEST_EQ( flat_get<3>(f), f.v3);
BOOST_TEST_EQ( flat_get<4>(f), f.v4and5[0]);
BOOST_TEST_EQ( flat_get<5>(f), f.v4and5[1]);
BOOST_TEST_EQ( flat_get<6>(f), f.v6);
BOOST_TEST_EQ( flat_get<7>(f), f.v7);
BOOST_TEST_EQ( flat_get<8>(f), f.v8);
BOOST_TEST_EQ( flat_get<9>(f), f.v9);
BOOST_TEST_EQ( flat_get<10>(f), f.v10);
BOOST_TEST( flat_get<11>(f) < f.v11 + 0.001); BOOST_TEST( flat_get<11>(f) > f.v11 - 0.001);
BOOST_TEST_EQ( flat_get<12>(f), f.v12and13.a0);
BOOST_TEST_EQ( flat_get<13>(f), f.v12and13.a1);
BOOST_TEST_EQ( flat_get<14>(f), f.v14and15andv16and17.b0);
BOOST_TEST_EQ( flat_get<15>(f), f.v14and15andv16and17.b1);
BOOST_TEST_EQ( flat_get<16>(f), f.v14and15andv16and17.cr.a0);
BOOST_TEST_EQ( flat_get<17>(f), f.v14and15andv16and17.cr.a1);
}
template <class T>
void test_compiletime() {
constexpr T f{};
static_assert(flat_tuple_size_v<foo> == 18, "failed tuple size check");
static_assert( std::is_same< decltype(flat_get<0>(f)), decltype((f.v0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<1>(f)), decltype((f.v1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<2>(f)), decltype((f.v2))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<3>(f)), decltype((f.v3))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<4>(f)), decltype((f.v4and5[0]))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<5>(f)), decltype((f.v4and5[1]))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<6>(f)), decltype((f.v6))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<7>(f)), decltype((f.v7))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<8>(f)), decltype((f.v8))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<9>(f)), decltype((f.v9))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<10>(f)), decltype((f.v10))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<11>(f)), decltype((f.v11))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<12>(f)), decltype((f.v12and13.a0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<13>(f)), decltype((f.v12and13.a1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<14>(f)), decltype((f.v14and15andv16and17.b0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<15>(f)), decltype((f.v14and15andv16and17.b1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<16>(f)), decltype((f.v14and15andv16and17.cr.a0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<17>(f)), decltype((f.v14and15andv16and17.cr.a1))>::value, "types missmatch");
}
template <class T>
constexpr void test_compiletime_array() {
{
constexpr T f[20] = {0};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
{
constexpr T f[2][10] = {{0}};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
{
constexpr T f[2][5][2] = {{{0}}};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
}
void test_with_enums() {
enum class my_enum: unsigned {
VALUE1 = 17, VALUE2, VALUE3
};
struct my_struct { my_enum e; int i; short s; };
my_struct s {my_enum::VALUE1, 10, 11};
std::tuple<unsigned, int, short> t = flat_structure_to_tuple(s);
BOOST_TEST_EQ(std::get<0>(t), 17u);
BOOST_TEST_EQ(std::get<1>(t), 10);
BOOST_TEST_EQ(std::get<2>(t), 11);
flat_get<1>(s) = 101;
BOOST_TEST_EQ(flat_get<1>(s), 101);
flat_get<2>(s) = 111;
BOOST_TEST_EQ(flat_get<2>(s), 111);
BOOST_TEST(flat_structure_tie(s) == flat_structure_tie(s));
BOOST_TEST(flat_structure_tie(s) == flat_structure_to_tuple(s));
BOOST_TEST(flat_structure_tie(s) != t);
flat_structure_tie(s) = t;
BOOST_TEST_EQ(flat_get<0>(s), 17u);
BOOST_TEST_EQ(flat_get<1>(s), 10);
BOOST_TEST_EQ(flat_get<2>(s), 11);
static_assert(std::is_same<
int, flat_tuple_element_t<1, my_struct>
>::value, "");
static_assert(std::is_same<
short, flat_tuple_element_t<2, my_struct>
>::value, "");
static_assert(std::is_same<
const int, flat_tuple_element_t<1, const my_struct>
>::value, "");
static_assert(std::is_same<
volatile short, flat_tuple_element_t<2, volatile my_struct>
>::value, "");
static_assert(std::is_same<
const volatile short, flat_tuple_element_t<2, const volatile my_struct>
>::value, "");
static_assert(
3 == flat_tuple_size_v<const volatile my_struct>,
""
);
}
void test_comparable_struct() {
struct comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
using namespace flat_ops;
comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
comparable_struct s2 = s1;
comparable_struct s3 {0, 1, "Hello", false, 6,7,8,9,10,11111};
BOOST_TEST(s1 == s2);
BOOST_TEST(s1 <= s2);
BOOST_TEST(s1 >= s2);
BOOST_TEST(!(s1 != s2));
BOOST_TEST(!(s1 == s3));
BOOST_TEST(s1 != s3);
BOOST_TEST(s1 < s3);
BOOST_TEST(s3 > s2);
BOOST_TEST(s1 <= s3);
BOOST_TEST(s3 >= s2);
std::cout << s1 << std::endl;
comparable_struct s4;
std::stringstream ss;
ss.exceptions ( std::ios::failbit);
ss << s1;
ss >> s4;
std::cout << s4 << std::endl;
BOOST_TEST(s1 == s4);
int i = 1, j = 2;
BOOST_TEST_NE(i, j);
}
void test_empty_struct() {
struct empty {};
using namespace flat_ops;
std::cout << empty{} << std::endl;
BOOST_TEST(empty{} == empty{});
}
void test_pods_with_int_operators() {
using namespace flat_ops;
std::stringstream ss;
ss << std::is_pod<int>{};
int i = 0;
ss >> i;
BOOST_TEST_EQ(i, 1);
std::cout << i << std::endl;
}
void test_struct_with_single_field() {
struct f1 { int i; };
using namespace flat_ops;
std::stringstream ss;
ss << f1{ 777 };
f1 var{};
ss >> var;
BOOST_TEST_EQ(var.i, 777);
BOOST_TEST(var == f1{ 777 });
BOOST_TEST(var != f1{ 778 });
BOOST_TEST(var <= f1{ 777 });
BOOST_TEST(var <= f1{ 778 });
BOOST_TEST(var < f1{ 778 });
BOOST_TEST(var >= f1{ 777 });
BOOST_TEST(var >= f1{ 776 });
BOOST_TEST(var > f1{ 776 });
}
template <class Comparator>
void test_with_contatiners() {
struct testing { bool b1, b2; int i; };
struct testing2 { bool b1, b2; int i; };
std::set<testing, Comparator > t{
{true, true, 100},
{false, true, 100},
{true, false, 100},
{true, true, 101}
};
BOOST_TEST(t.find({true, true, 100}) != t.end());
BOOST_TEST_EQ(t.count({true, true, 100}), 1u);
BOOST_TEST(t.find(testing2{true, true, 100}) != t.end());
std::set<testing2, Comparator > t2{
{true, true, 101},
{true, true, 100},
{true, false, 100},
{false, true, 100}
};
BOOST_TEST(std::equal(t.begin(), t.end(), t2.begin(), t2.end(), flat_equal_to<>{}));
BOOST_TEST(!std::equal(t.begin(), t.end(), t2.begin(), t2.end(), flat_not_equal<>{}));
std::vector<testing> res;
std::set_intersection(t.begin(), t.end(), t2.begin(), t2.end(),
std::back_inserter(res), Comparator{});
BOOST_TEST_EQ(res.size(), 4u);
}
void test_with_user_defined_constructor() {
struct pr {
int i;
short s;
pr() = default;
pr(const pr&) = default;
pr(pr&&) = default;
pr(int ii, short is) noexcept
: i(ii), s(is)
{}
};
pr p{1, 2};
//assert(flat_get<1>(p) == 2); // Compilation error
}
void test_hash() {
struct almost_pair { int i; short s; };
std::unordered_set<almost_pair, flat_hash<almost_pair>, flat_equal_to<> > s;
s.insert({0, 1});
s.insert({1, 0});
s.insert({1, 1});
BOOST_TEST_EQ(s.size(), 3u);
flat_hash<almost_pair> hs;
BOOST_TEST_NE(hs({0, 1}), hs({1, 0}));
BOOST_TEST_EQ(hs({0, 1}), hs({0, 1}));
BOOST_TEST_EQ(hs({1, 1}), hs({1, 1}));
BOOST_TEST_NE(hs({0, 0}), hs({1, 1}));
struct single_field { int i; };
BOOST_TEST_NE(flat_hash<single_field>()({1}), std::hash<int>()(1));
BOOST_TEST_NE(flat_hash<single_field>()({199}), std::hash<int>()(199));
}
// Test case by Lisa Lippincott
void test_alignment_with_nested_structure() {
struct A0 {
short s;
char c;
};
struct B0 {
A0 a;
char c1;
char c2;
};
B0 test_struct;
std::memset(&test_struct, 0, sizeof(test_struct));
test_struct.a.s = 0;
test_struct.a.c = '1';
test_struct.c1 = '2';
test_struct.c2 = '3';
BOOST_TEST_EQ(flat_get<0>(test_struct), 0);
BOOST_TEST_EQ(flat_get<1>(test_struct), '1');
BOOST_TEST_EQ(flat_get<2>(test_struct), '2');
BOOST_TEST_EQ(flat_get<3>(test_struct), '3');
}
template <std::size_t... I>
void test_and_debug_internals(std::index_sequence<I...>) {
struct A0 {
short s;
char c;
};
A0 a0 { 3, '4' };
BOOST_TEST_EQ(boost::pfr::flat_get<0>(a0), 3);
BOOST_TEST_EQ(boost::pfr::flat_get<1>(a0), '4');
struct A1 {
int i;
};
struct B1 {
A1 a;
int j;
};
B1 b1 { {5}, 6 };
BOOST_TEST_EQ(boost::pfr::flat_get<0>(b1), 5);
BOOST_TEST_EQ(boost::pfr::flat_get<1>(b1), 6);
/*
struct B0 {
A0 a;
char c1;
char c2;
};
typedef B0 type;
typedef B0 T;
using namespace boost::pfr::detail;
constexpr auto a = flat_array_of_type_ids<T>();
(void)a; // `a` is unused if T is an empty type
constexpr size_array<a.size()> skips_in_subtuples {{
a.count_from_opening_till_matching_parenthis_seq(I, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag)...
}};
constexpr size_array<sizeof...(I) + 1> indexes_in_subtuples_uncleanuped {{ 1, 1 + I...}};
constexpr auto indexes_plus_1_and_zeros_as_skips = remove_skips(indexes_in_subtuples_uncleanuped, skips_in_subtuples);
constexpr auto new_size = size_t_<indexes_plus_1_and_zeros_as_skips.count_nonzeros()>{};
constexpr auto indexes = resize_dropping_zeros_and_decrementing(new_size, indexes_plus_1_and_zeros_as_skips);
static_assert(indexes.data[0] == 0, "");
static_assert(indexes.data[1] == 4, "");
static_assert(indexes.data[2] == 5, "");
static_assert(
std::is_same<
decltype(boost::pfr::detail::as_flat_tuple_impl<type>(
std::make_index_sequence< decltype(boost::pfr::detail::flat_array_of_type_ids<type>())::size() >()
)),
boost::pfr::detail::sequence_tuple::tuple<boost::pfr::detail::sequence_tuple::tuple<short, char>, char, char>
>::value,
""
);
constexpr auto res = as_flat_tuple_impl<foo>(
std::make_index_sequence< decltype(flat_array_of_type_ids<foo>())::size() >()
);
auto afoo = flat_array_of_type_ids<foo>();
std::cerr << "\n\n";
for (std::size_t i = 0; i < afoo.size(); ++i) {
std::cerr << afoo.data[i] << ' ';
}
std::cerr << boost::typeindex::type_id<decltype(res)>() << "\n\n";*/
}
// test by Alexey Moiseytsev
void another_test_with_unusual_alignment() {
struct nested {
char c0;
char c1;
int i0;
short s0;
char c2;
};
struct pair {
nested n0;
nested n1;
};
// layout:
// offset struct tuple
// 0 n0.c0 n0.c0
// 1 n0.c1 n0.c1
// 2 padding padding
// 3 padding padding
// 4 n0.i0 n0.i0
// 5 n0.i0 n0.i0
// 6 n0.i0 n0.i0
// 7 n0.i0 n0.i0
// 8 n0.s0 n0.s0
// 9 n0.s0 n0.s0
// 10 n0.c2 n0.c2
// 11 padding n1.c0 !!!
// 12 n1.c0 n1.c1 !!!
// 13 n1.c1 padding !!!
// 14 padding padding
// 15 padding padding
// 16 n1.i0 n1.i0
// 17 n1.i0 n1.i0
// 18 n1.i0 n1.i0
// 19 n1.i0 n1.i0
// 20 n1.s0 n1.s0
// 21 n1.s0 n1.s0
// 22 n1.c2 n1.c2
// 23 padding padding
pair s{{'q', 'w', 12, 32, 'e'}, {'a', 's', 24, 64, 'd'}};
BOOST_TEST_EQ(flat_get<0>(s), 'q');
BOOST_TEST_EQ(flat_get<1>(s), 'w');
BOOST_TEST_EQ(flat_get<2>(s), 12);
BOOST_TEST_EQ(flat_get<3>(s), 32);
BOOST_TEST_EQ(flat_get<4>(s), 'e');
BOOST_TEST_EQ(flat_get<5>(s), 'a');
BOOST_TEST_EQ(flat_get<6>(s), 's');
BOOST_TEST_EQ(flat_get<7>(s), 24);
BOOST_TEST_EQ(flat_get<8>(s), 64);
BOOST_TEST_EQ(flat_get<9>(s), 'd');
}
#ifdef BOOST_PFR_RELAX_POD_REQUIREMENT
// Test inspired by Anton Bikineev
void test_structure_with_default_values() {
struct test_me {
int i = 2;
short s = 14;
};
test_me s;
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
}
// Test inspired by Anton Bikineev
void test_st_layout_structure_with_non_constexpr_type() {
/* TODO: fixme
struct non_literal_structure {
int i1 = 3;
short s1 = 15;
non_literal_structure() = delete;
non_literal_structure(const non_literal_structure&) = delete;
non_literal_structure(non_literal_structure&&) = default;
non_literal_structure& operator=(const non_literal_structure&) = delete;
non_literal_structure& operator=(non_literal_structure&&) = delete;
};
struct test_me {
int i = 2;
short s = 14;
non_literal_structure nonlit{};
};
BOOST_TEST_EQ(tuple_size_v<test_me>, 3);
test_me s{};
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
BOOST_TEST_EQ(flat_get<2>(s), 3);
BOOST_TEST_EQ(flat_get<3>(s), 15);*/
}
// Test inspired by Anton Bikineev
void test_structure_with_user_provided_default_constructor() {
struct test_me {
short s = 2;
constexpr test_me(short){}
};
test_me s{0};
(void)s;
BOOST_TEST_EQ(flat_get<0>(s), 2); // TODO: fix compile time error message
}
#endif
/*
void test_copy_only_pod() {
struct copy_only_pod {
int i1;
short s1;
copy_only_pod() = delete;
copy_only_pod(copy_only_pod&&) = delete;
copy_only_pod(const copy_only_pod&) = default;
copy_only_pod& operator=(const copy_only_pod&) = delete;
copy_only_pod& operator=(copy_only_pod&&) = delete;
};
copy_only_pod s{2, 14};
BOOST_TEST_EQ(tuple_size_v<copy_only_pod>, 2u);
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
struct with_reference {
int& i;
int* p;
};
BOOST_TEST_EQ(tuple_size_v<with_reference>, 2u);
struct with_nested_copy_only_pod {
int i;
copy_only_pod p;
};
BOOST_TEST_EQ(tuple_size_v<with_nested_copy_only_pod>, 2u);
with_nested_copy_only_pod np{2, {3, 4}};
BOOST_TEST_EQ(flat_get<0>(np), 2);
BOOST_TEST_EQ(flat_get<1>(np), 3);
BOOST_TEST_EQ(flat_get<2>(np), 4);
} // */
/* // TODO: think of something with it!
void test_move_only_pod() {
struct move_only_pod {
int i1;
short s1;
move_only_pod() = delete;
//move_only_pod(move_only_pod&&) = delete;
move_only_pod(const move_only_pod&) = delete;
move_only_pod(move_only_pod&&) = default;
move_only_pod& operator=(const move_only_pod&) = delete;
move_only_pod& operator=(move_only_pod&&) = delete;
};
move_only_pod s{2, 14};
BOOST_TEST_EQ(tuple_size_v<move_only_pod>, 2u);
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
struct with_reference {
int& i;
int* p;
};
BOOST_TEST_EQ(tuple_size_v<with_reference>, 2u);
struct with_nested_move_only_pod {
int i;
short s;
move_only_pod p;
char c;
};
BOOST_TEST_EQ(tuple_size_v<with_nested_move_only_pod>, 4u);
// with_nested_move_only_pod np{2, {3, 4}};
// BOOST_TEST_EQ(flat_get<0>(np), 2);
// BOOST_TEST_EQ(flat_get<1>(np), 3);
// BOOST_TEST_EQ(flat_get<2>(np), 4);
} // */
int main() {
test_compiletime<foo>();
test_compiletime_array<int>();
test_compiletime_array<void*>();
test_compiletime_array<const void*>();
test_compiletime_array<char>();
test_compiletime_array<char const volatile*>();
{
foo f {
'A', 11, 12, 13, {'B', 'C'}, 16, 17, 0, 0, 0, 30.1
, {18, 19}
, {20, 21, {22, 23}}
};
test_runtime(f);
}
{
foo f {
'\0', 12437, 1212, 13, {'1', '7'}, 163, 1723, 0, 0, 0, 3000.1
, {-18, -19}
, {656565, 65535, {-22, -23}}
};
test_runtime(f);
}
test_with_enums();
test_comparable_struct();
test_empty_struct();
test_pods_with_int_operators();
test_struct_with_single_field();
test_with_contatiners<flat_less<>>();
test_with_contatiners<flat_greater<>>();
test_print();
test_with_user_defined_constructor();
test_hash();
struct non_pod1 {
std::string s;
std::vector<int> v;
int i;
struct foo {
std::string s2;
} f;
};
static_assert(tuple_size<non_pod1>::value == 4, "Must not be a compile error");
struct non_pod2 {
unsigned ui1: 1;
unsigned ui2: 2;
std::string s;
std::vector<int> v;
int i;
struct foo {
std::string s2;
} f;
};
static_assert(tuple_size<non_pod2>::value == 6, "Must not be a compile error even with bitfields");
int i_2dimens[2][2] = {{10, 11}, {12, 13} };
static_assert(tuple_size<decltype(i_2dimens)>::value == 4, "");
static_assert(flat_tuple_size<decltype(i_2dimens)>::value == 4, "");
test_and_debug_internals(std::make_index_sequence<6>{});
test_alignment_with_nested_structure();
another_test_with_unusual_alignment();
#ifdef BOOST_PFR_RELAX_POD_REQUIREMENT
test_structure_with_default_values();
test_st_layout_structure_with_non_constexpr_type();
test_structure_with_user_provided_default_constructor();
#endif
//test_copy_only_pod();
//test_move_only_pod();
return boost::report_errors();
}

659
test/core.cpp.autosave Normal file
View File

@@ -0,0 +1,659 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//#define BOOST_PFR_RELAX_POD_REQUIREMENT
#include <boost/pfr.hpp>
#include <boost/core/lightweight_test.hpp>
//#include <boost/type_index.hpp> // for debugging
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <sstream>
#include <set>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <cstring>
using namespace boost::pfr;
template <std::size_t I, class T>
void print(T& f) {
std::cout << flat_get<I>(f) << "\t\t"
<< typeid(flat_tuple_element_t<I, T>).name()
<< std::endl;
}
struct make_my_life_harder { int a0; short a1; };
struct make_my_life_even_more_harder { unsigned int b0; unsigned short b1; make_my_life_harder cr;};
struct foo {
unsigned char v0;
unsigned int v1;
unsigned short v2;
unsigned long long v3;
unsigned char v4and5[2];
int v6;
std::size_t v7;
int* v8;
const void* v9;
int const**const volatile**volatile** v10;
const double v11;
make_my_life_harder v12and13;
make_my_life_even_more_harder v14and15andv16and17;
};
void test_print() {
foo f {
'A', 11, 12, 13, {'B', 'C'}, 16, 17, 0, 0, 0, 30.1
, {18, 19}
, {20, 21, {22, 23}}
};
print<0>(f); print<1>(f); print<2>(f);
print<3>(f); print<4>(f); print<5>(f);
print<6>(f); print<7>(f); print<8>(f);
print<9>(f); print<10>(f); print<11>(f);
print<12>(f); print<13>(f); print<14>(f);
print<15>(f); print<16>(f); print<17>(f);
static_assert(flat_tuple_size_v<foo> == 18, "failed tuple size check");
int a[] = {0, 1, 2, 3};
std::cout << '\n' << flat_get<1>(a) << std::endl;
int b[2][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}};
std::cout << flat_get<4>(b) << std::endl;
int i = 777;
std::cout << flat_get<0>(i) << std::endl;
}
void test_runtime(const foo& f) {
BOOST_TEST_EQ( flat_get<0>(f), f.v0);
BOOST_TEST_EQ( flat_get<1>(f), f.v1);
BOOST_TEST_EQ( flat_get<2>(f), f.v2);
BOOST_TEST_EQ( flat_get<3>(f), f.v3);
BOOST_TEST_EQ( flat_get<4>(f), f.v4and5[0]);
BOOST_TEST_EQ( flat_get<5>(f), f.v4and5[1]);
BOOST_TEST_EQ( flat_get<6>(f), f.v6);
BOOST_TEST_EQ( flat_get<7>(f), f.v7);
BOOST_TEST_EQ( flat_get<8>(f), f.v8);
BOOST_TEST_EQ( flat_get<9>(f), f.v9);
BOOST_TEST_EQ( flat_get<10>(f), f.v10);
BOOST_TEST( flat_get<11>(f) < f.v11 + 0.001); BOOST_TEST( flat_get<11>(f) > f.v11 - 0.001);
BOOST_TEST_EQ( flat_get<12>(f), f.v12and13.a0);
BOOST_TEST_EQ( flat_get<13>(f), f.v12and13.a1);
BOOST_TEST_EQ( flat_get<14>(f), f.v14and15andv16and17.b0);
BOOST_TEST_EQ( flat_get<15>(f), f.v14and15andv16and17.b1);
BOOST_TEST_EQ( flat_get<16>(f), f.v14and15andv16and17.cr.a0);
BOOST_TEST_EQ( flat_get<17>(f), f.v14and15andv16and17.cr.a1);
}
template <class T>
void test_compiletime() {
constexpr T f{};
static_assert(flat_tuple_size_v<foo> == 18, "failed tuple size check");
static_assert( std::is_same< decltype(flat_get<0>(f)), decltype((f.v0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<1>(f)), decltype((f.v1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<2>(f)), decltype((f.v2))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<3>(f)), decltype((f.v3))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<4>(f)), decltype((f.v4and5[0]))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<5>(f)), decltype((f.v4and5[1]))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<6>(f)), decltype((f.v6))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<7>(f)), decltype((f.v7))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<8>(f)), decltype((f.v8))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<9>(f)), decltype((f.v9))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<10>(f)), decltype((f.v10))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<11>(f)), decltype((f.v11))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<12>(f)), decltype((f.v12and13.a0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<13>(f)), decltype((f.v12and13.a1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<14>(f)), decltype((f.v14and15andv16and17.b0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<15>(f)), decltype((f.v14and15andv16and17.b1))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<16>(f)), decltype((f.v14and15andv16and17.cr.a0))>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<17>(f)), decltype((f.v14and15andv16and17.cr.a1))>::value, "types missmatch");
}
template <class T>
constexpr void test_compiletime_array() {
{
constexpr T f[20] = {0};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
{
constexpr T f[2][10] = {{0}};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
{
constexpr T f[2][5][2] = {{{0}}};
static_assert(flat_tuple_size_v<decltype(f)> == 20, "failed tuple size check for array");
static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch");
static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch");
}
}
void test_with_enums() {
enum class my_enum: unsigned {
VALUE1 = 17, VALUE2, VALUE3
};
struct my_struct { my_enum e; int i; short s; };
my_struct s {my_enum::VALUE1, 10, 11};
std::tuple<unsigned, int, short> t = flat_structure_to_tuple(s);
BOOST_TEST_EQ(std::get<0>(t), 17u);
BOOST_TEST_EQ(std::get<1>(t), 10);
BOOST_TEST_EQ(std::get<2>(t), 11);
flat_get<1>(s) = 101;
BOOST_TEST_EQ(flat_get<1>(s), 101);
flat_get<2>(s) = 111;
BOOST_TEST_EQ(flat_get<2>(s), 111);
BOOST_TEST(flat_structure_tie(s) == flat_structure_tie(s));
BOOST_TEST(flat_structure_tie(s) == flat_structure_to_tuple(s));
BOOST_TEST(flat_structure_tie(s) != t);
flat_structure_tie(s) = t;
BOOST_TEST_EQ(flat_get<0>(s), 17u);
BOOST_TEST_EQ(flat_get<1>(s), 10);
BOOST_TEST_EQ(flat_get<2>(s), 11);
static_assert(std::is_same<
int, flat_tuple_element_t<1, my_struct>
>::value, "");
static_assert(std::is_same<
short, flat_tuple_element_t<2, my_struct>
>::value, "");
static_assert(std::is_same<
const int, flat_tuple_element_t<1, const my_struct>
>::value, "");
static_assert(std::is_same<
volatile short, flat_tuple_element_t<2, volatile my_struct>
>::value, "");
static_assert(std::is_same<
const volatile short, flat_tuple_element_t<2, const volatile my_struct>
>::value, "");
static_assert(
3 == flat_tuple_size_v<const volatile my_struct>,
""
);
}
void test_comparable_struct() {
struct comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
using namespace flat_ops;
comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
comparable_struct s2 = s1;
comparable_struct s3 {0, 1, "Hello", false, 6,7,8,9,10,11111};
BOOST_TEST(s1 == s2);
BOOST_TEST(s1 <= s2);
BOOST_TEST(s1 >= s2);
BOOST_TEST(!(s1 != s2));
BOOST_TEST(!(s1 == s3));
BOOST_TEST(s1 != s3);
BOOST_TEST(s1 < s3);
BOOST_TEST(s3 > s2);
BOOST_TEST(s1 <= s3);
BOOST_TEST(s3 >= s2);
std::cout << s1 << std::endl;
comparable_struct s4;
std::stringstream ss;
ss.exceptions ( std::ios::failbit);
ss << s1;
ss >> s4;
std::cout << s4 << std::endl;
BOOST_TEST(s1 == s4);
int i = 1, j = 2;
BOOST_TEST_NE(i, j);
}
void test_empty_struct() {
struct empty {};
using namespace flat_ops;
std::cout << empty{} << std::endl;
BOOST_TEST(empty{} == empty{});
}
void test_pods_with_int_operators() {
using namespace flat_ops;
std::stringstream ss;
ss << std::is_pod<int>{};
int i = 0;
ss >> i;
BOOST_TEST_EQ(i, 1);
std::cout << i << std::endl;
}
void test_struct_with_single_field() {
struct f1 { int i; };
using namespace flat_ops;
std::stringstream ss;
ss << f1{ 777 };
f1 var{};
ss >> var;
BOOST_TEST_EQ(var.i, 777);
BOOST_TEST(var == f1{ 777 });
BOOST_TEST(var != f1{ 778 });
BOOST_TEST(var <= f1{ 777 });
BOOST_TEST(var <= f1{ 778 });
BOOST_TEST(var < f1{ 778 });
BOOST_TEST(var >= f1{ 777 });
BOOST_TEST(var >= f1{ 776 });
BOOST_TEST(var > f1{ 776 });
}
template <class Comparator>
void test_with_contatiners() {
struct testing { bool b1, b2; int i; };
struct testing2 { bool b1, b2; int i; };
std::set<testing, Comparator > t{
{true, true, 100},
{false, true, 100},
{true, false, 100},
{true, true, 101}
};
BOOST_TEST(t.find({true, true, 100}) != t.end());
BOOST_TEST_EQ(t.count({true, true, 100}), 1u);
BOOST_TEST(t.find(testing2{true, true, 100}) != t.end());
std::set<testing2, Comparator > t2{
{true, true, 101},
{true, true, 100},
{true, false, 100},
{false, true, 100}
};
BOOST_TEST(std::equal(t.begin(), t.end(), t2.begin(), t2.end(), flat_equal_to<>{}));
BOOST_TEST(!std::equal(t.begin(), t.end(), t2.begin(), t2.end(), flat_not_equal<>{}));
std::vector<testing> res;
std::set_intersection(t.begin(), t.end(), t2.begin(), t2.end(),
std::back_inserter(res), Comparator{});
BOOST_TEST_EQ(res.size(), 4u);
}
void test_with_user_defined_constructor() {
struct pr {
int i;
short s;
pr() = default;
pr(const pr&) = default;
pr(pr&&) = default;
pr(int ii, short is) noexcept
: i(ii), s(is)
{}
};
pr p{1, 2};
//assert(flat_get<1>(p) == 2); // Compilation error
}
void test_hash() {
struct almost_pair { int i; short s; };
std::unordered_set<almost_pair, flat_hash<almost_pair>, flat_equal_to<> > s;
s.insert({0, 1});
s.insert({1, 0});
s.insert({1, 1});
BOOST_TEST_EQ(s.size(), 3u);
flat_hash<almost_pair> hs;
BOOST_TEST_NE(hs({0, 1}), hs({1, 0}));
BOOST_TEST_EQ(hs({0, 1}), hs({0, 1}));
BOOST_TEST_EQ(hs({1, 1}), hs({1, 1}));
BOOST_TEST_NE(hs({0, 0}), hs({1, 1}));
struct single_field { int i; };
BOOST_TEST_NE(flat_hash<single_field>()({1}), std::hash<int>()(1));
BOOST_TEST_NE(flat_hash<single_field>()({199}), std::hash<int>()(199));
}
// Test case by Lisa Lippincott
void test_alignment_with_neted_structure() {
struct A0 {
short s;
char c;
};
struct B0 {
A0 a;
char c1;
char c2;
};
B0 test_struct;
std::memset(&test_struct, 0, sizeof(test_struct));
test_struct.a.s = 0;
test_struct.a.c = '1';
test_struct.c1 = '2';
test_struct.c2 = '3';
BOOST_TEST_EQ(flat_get<0>(test_struct), 0);
BOOST_TEST_EQ(flat_get<1>(test_struct), '1');
BOOST_TEST_EQ(flat_get<2>(test_struct), '2');
BOOST_TEST_EQ(flat_get<3>(test_struct), '3');
}
template <std::size_t... I>
void test_and_debug_internals(std::index_sequence<I...>) {
struct A0 {
short s;
char c;
};
A0 a0 { 3, '4' };
BOOST_TEST_EQ(boost::pfr::flat_get<0>(a0), 3);
BOOST_TEST_EQ(boost::pfr::flat_get<1>(a0), '4');
struct A1 {
int i;
};
struct B1 {
A1 a;
int j;
};
B1 b1 { {5}, 6 };
BOOST_TEST_EQ(boost::pfr::flat_get<0>(b1), 5);
BOOST_TEST_EQ(boost::pfr::flat_get<1>(b1), 6);
/*
struct B0 {
A0 a;
char c1;
char c2;
};
typedef B0 type;
typedef B0 T;
using namespace boost::pfr::detail;
constexpr auto a = flat_array_of_type_ids<T>();
(void)a; // `a` is unused if T is an empty type
constexpr size_array<a.size()> skips_in_subtuples {{
a.count_from_opening_till_matching_parenthis_seq(I, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag)...
}};
constexpr size_array<sizeof...(I) + 1> indexes_in_subtuples_uncleanuped {{ 1, 1 + I...}};
constexpr auto indexes_plus_1_and_zeros_as_skips = remove_skips(indexes_in_subtuples_uncleanuped, skips_in_subtuples);
constexpr auto new_size = size_t_<indexes_plus_1_and_zeros_as_skips.count_nonzeros()>{};
constexpr auto indexes = resize_dropping_zeros_and_decrementing(new_size, indexes_plus_1_and_zeros_as_skips);
static_assert(indexes.data[0] == 0, "");
static_assert(indexes.data[1] == 4, "");
static_assert(indexes.data[2] == 5, "");
static_assert(
std::is_same<
decltype(boost::pfr::detail::as_flat_tuple_impl<type>(
std::make_index_sequence< decltype(boost::pfr::detail::flat_array_of_type_ids<type>())::size() >()
)),
boost::pfr::detail::sequence_tuple::tuple<boost::pfr::detail::sequence_tuple::tuple<short, char>, char, char>
>::value,
""
);
constexpr auto res = as_flat_tuple_impl<foo>(
std::make_index_sequence< decltype(flat_array_of_type_ids<foo>())::size() >()
);
auto afoo = flat_array_of_type_ids<foo>();
std::cerr << "\n\n";
for (std::size_t i = 0; i < afoo.size(); ++i) {
std::cerr << afoo.data[i] << ' ';
}
std::cerr << boost::typeindex::type_id<decltype(res)>() << "\n\n";*/
}
// test by Alexey Moiseytsev
void another_test_with_unusual_alignment() {
struct nested {
char c0;
char c1;
int i0;
short s0;
char c2;
};
struct pair {
nested n0;
nested n1;
};
// layout:
// offset struct tuple
// 0 n0.c0 n0.c0
// 1 n0.c1 n0.c1
// 2 padding padding
// 3 padding padding
// 4 n0.i0 n0.i0
// 5 n0.i0 n0.i0
// 6 n0.i0 n0.i0
// 7 n0.i0 n0.i0
// 8 n0.s0 n0.s0
// 9 n0.s0 n0.s0
// 10 n0.c2 n0.c2
// 11 padding n1.c0 !!!
// 12 n1.c0 n1.c1 !!!
// 13 n1.c1 padding !!!
// 14 padding padding
// 15 padding padding
// 16 n1.i0 n1.i0
// 17 n1.i0 n1.i0
// 18 n1.i0 n1.i0
// 19 n1.i0 n1.i0
// 20 n1.s0 n1.s0
// 21 n1.s0 n1.s0
// 22 n1.c2 n1.c2
// 23 padding padding
pair s{{'q', 'w', 12, 32, 'e'}, {'a', 's', 24, 64, 'd'}};
BOOST_TEST_EQ(flat_get<0>(s), 'q');
BOOST_TEST_EQ(flat_get<1>(s), 'w');
BOOST_TEST_EQ(flat_get<2>(s), 12);
BOOST_TEST_EQ(flat_get<3>(s), 32);
BOOST_TEST_EQ(flat_get<4>(s), 'e');
BOOST_TEST_EQ(flat_get<5>(s), 'a');
BOOST_TEST_EQ(flat_get<6>(s), 's');
BOOST_TEST_EQ(flat_get<7>(s), 24);
BOOST_TEST_EQ(flat_get<8>(s), 64);
BOOST_TEST_EQ(flat_get<9>(s), 'd');
}
#ifdef BOOST_PFR_RELAX_POD_REQUIREMENT
// Test inspired by Anton Bikineev
void test_structure_with_default_values() {
struct test_me {
int i = 2;
short s = 14;
};
test_me s;
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
}
// Test inspired by Anton Bikineev
void test_st_layout_structure_with_non_constexpr_type() {
struct non_literal_structure {
int i1 = 3;
short s1 = 15;
non_literal_structure() = delete;
non_literal_structure(const non_literal_structure&) = delete;
non_literal_structure(non_literal_structure&&) = default;
non_literal_structure& operator=(const non_literal_structure&) = delete;
non_literal_structure& operator=(non_literal_structure&&) = delete;
};
struct test_me {
int i = 2;
short s = 14;
non_literal_structure nonlit{};
};
BOOST_TEST_EQ(tuple_size_v<test_me>, 3);
test_me s{};
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
BOOST_TEST_EQ(flat_get<2>(s), 3);
BOOST_TEST_EQ(flat_get<3>(s), 15);
}
// Test inspired by Anton Bikineev
void test_structure_with_user_provided_default_constructor() {
struct test_me {
short s = 2;
constexpr test_me(short){}
};
test_me s{0};
(void)s;
//BOOST_TEST_EQ(flat_get<0>(s), 2); // TODO: fix compile time error message2
}
#endif
void test_move_only_pod() {
struct move_only_pod {
int i1;
short s1;
move_only_pod() = delete;
//move_only_pod(move_only_pod&&) = delete;
move_only_pod(const move_only_pod&) = delete;
move_only_pod(move_only_pod&&) = default;
move_only_pod& operator=(const move_only_pod&) = delete;
move_only_pod& operator=(move_only_pod&&) = delete;
};
move_only_pod s{2, 14};
BOOST_TEST_EQ(tuple_size_v<move_only_pod>, 2u);
BOOST_TEST_EQ(flat_get<0>(s), 2);
BOOST_TEST_EQ(flat_get<1>(s), 14);
/* // TODO: fix me!
struct with_nested_move_only_pod {
int i;
move_only_pod p;
};
BOOST_TEST_EQ(tuple_size_v<with_nested_move_only_pod>, 2u); */
/* // TODO: fix me!
with_nested_move_only_pod np{2, {3, 4}};
BOOST_TEST_EQ(flat_get<0>(np), 2);
BOOST_TEST_EQ(flat_get<1>(np), 3);
BOOST_TEST_EQ(flat_get<2>(np), 4);*/
}
int main() {
test_compiletime<foo>();
test_compiletime_array<int>();
test_compiletime_array<void*>();
test_compiletime_array<const void*>();
test_compiletime_array<char>();
test_compiletime_array<char const volatile*>();
{
foo f {
'A', 11, 12, 13, {'B', 'C'}, 16, 17, 0, 0, 0, 30.1
, {18, 19}
, {20, 21, {22, 23}}
};
test_runtime(f);
}
{
foo f {
'\0', 12437, 1212, 13, {'1', '7'}, 163, 1723, 0, 0, 0, 3000.1
, {-18, -19}
, {656565, 65535, {-22, -23}}
};
test_runtime(f);
}
test_with_enums();
test_comparable_struct();
test_empty_struct();
test_pods_with_int_operators();
test_struct_with_single_field();
test_with_contatiners<flat_less<>>();
test_with_contatiners<flat_greater<>>();
test_print();
test_with_user_defined_constructor();
test_hash();
struct non_pod1 {
std::string s;
std::vector<int> v;
int i;
struct foo {
std::string s2;
} f;
};
static_assert(tuple_size<non_pod1>::value == 4, "Must not be a compile error");
struct non_pod2 {
unsigned ui1: 1;
unsigned ui2: 2;
std::string s;
std::vector<int> v;
int i;
struct foo {
std::string s2;
} f;
};
static_assert(tuple_size<non_pod2>::value == 6, "Must not be a compile error even with bitfields");
int i_2dimens[2][2] = {{10, 11}, {12, 13} };
static_assert(tuple_size<decltype(i_2dimens)>::value == 4, "");
static_assert(flat_tuple_size<decltype(i_2dimens)>::value == 4, "");
test_and_debug_internals(std::make_index_sequence<6>{});
test_alignment_with_neted_structure();
another_test_with_unusual_alignment();
#ifdef BOOST_PFR_RELAX_POD_REQUIREMENT
test_structure_with_default_values();
test_st_layout_structure_with_non_constexpr_type();
test_structure_with_user_provided_default_constructor();
#endif
test_move_only_pod();
return boost::report_errors();
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr.hpp>
#include "test_counts_on.hpp"
int main() {
test_counts_on_multiple_chars<char>();
return 0;
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr.hpp>
#include "test_counts_on.hpp"
int main() {
test_counts_on_multiple_chars<int>();
return 0;
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr.hpp>
#include "test_counts_on.hpp"
int main() {
test_counts_on_multiple_chars<long long>();
return 0;
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr.hpp>
#include "test_counts_on.hpp"
int main() {
test_counts_on_multiple_chars<short>();
return 0;
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr.hpp>
#include "test_counts_on.hpp"
int main() {
test_counts_on_multiple_chars<void*>();
return 0;
}

108
test/flat_functions_for.cpp Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/flat_functions_for.hpp>
#include <boost/core/lightweight_test.hpp>
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <sstream>
#include <set>
#include <string>
#include <boost/functional/hash.hpp>
#include <unordered_set>
struct adl_hash {
template <class T>
std::size_t operator()(const T& val) const {
using namespace boost;
return hash_value(val);
}
};
struct comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
BOOST_PFR_FLAT_FUNCTIONS_FOR(comparable_struct)
void test_comparable_struct() {
comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
comparable_struct s2 = s1;
comparable_struct s3 {0, 1, "Hello", false, 6,7,8,9,10,11111};
BOOST_TEST_EQ(s1, s2);
BOOST_TEST(s1 <= s2);
BOOST_TEST(s1 >= s2);
BOOST_TEST(!(s1 != s2));
BOOST_TEST(!(s1 == s3));
BOOST_TEST(s1 != s3);
BOOST_TEST(s1 < s3);
BOOST_TEST(s3 > s2);
BOOST_TEST(s1 <= s3);
BOOST_TEST(s3 >= s2);
std::cout << s1 << std::endl;
comparable_struct s4;
std::stringstream ss;
ss.exceptions ( std::ios::failbit);
ss << s1;
ss >> s4;
std::cout << s4 << std::endl;
BOOST_TEST_EQ(s1, s4);
int i = 1, j = 2;
BOOST_TEST_NE(i, j);
}
struct empty { operator std::string() { return "empty{}"; } };
BOOST_PFR_FLAT_FUNCTIONS_FOR(empty)
void test_empty_struct() {
BOOST_TEST_EQ(empty{}, empty{});
}
namespace foo {
struct testing { bool b1, b2; int i; };
BOOST_PFR_FLAT_FUNCTIONS_FOR(testing);
}
template <class Comparator>
void test_with_contatiners() {
std::set<foo::testing, Comparator > t{
{true, true, 100},
{false, true, 100},
{true, false, 100},
{true, true, 101}
};
BOOST_TEST(t.find({true, true, 100}) != t.end());
BOOST_TEST_EQ(t.count({true, true, 100}), 1u);
std::unordered_set<foo::testing, adl_hash> us(t.begin(), t.end());
BOOST_TEST_EQ(us.size(), t.size());
}
void test_implicit_conversions() {
std::stringstream ss;
ss << std::true_type{};
BOOST_TEST_EQ(ss.str(), "1"); // Does not breaks implicit conversion
ss.str("");
ss << empty{};
BOOST_TEST_EQ(ss.str(), "{}"); // Breaks implicit conversion for types marked with BOOST_PFR_FLAT_FUNCTIONS_FOR
}
int main() {
test_comparable_struct();
test_empty_struct();
test_with_contatiners<std::less<>>();
test_with_contatiners<std::greater<>>();
test_implicit_conversions();
return boost::report_errors();
}

82
test/flat_ops.cpp Normal file
View File

@@ -0,0 +1,82 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/flat_ops.hpp>
#include <boost/core/lightweight_test.hpp>
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <sstream>
#include <set>
#include <string>
template <class T>
void test_comparable_struct() {
using namespace boost::pfr::flat_ops;
T s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
T s2 = s1;
T s3 {0, 1, "Hello", false, 6,7,8,9,10,11111};
BOOST_TEST(s1 == s2);
BOOST_TEST(s1 <= s2);
BOOST_TEST(s1 >= s2);
BOOST_TEST(!(s1 != s2));
BOOST_TEST(!(s1 == s3));
BOOST_TEST(s1 != s3);
BOOST_TEST(s1 < s3);
BOOST_TEST(s3 > s2);
BOOST_TEST(s1 <= s3);
BOOST_TEST(s3 >= s2);
std::cout << s1 << std::endl;
T s4;
std::stringstream ss;
ss.exceptions ( std::ios::failbit);
ss << s1;
ss >> s4;
std::cout << s4 << std::endl;
BOOST_TEST(s1 == s4);
int i = 1, j = 2;
BOOST_TEST_NE(i, j);
}
void test_empty_struct() {
struct empty {};
using namespace boost::pfr::flat_ops;
std::cout << empty{};
BOOST_TEST(empty{} == empty{});
}
void test_implicit_conversions() {
std::stringstream ss;
ss << std::true_type{};
BOOST_TEST_EQ(ss.str(), "1"); // Does not breaks implicit conversion
}
namespace foo {
struct comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
}
int main() {
test_comparable_struct<foo::comparable_struct>();
struct local_comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
test_comparable_struct<local_comparable_struct>();
test_empty_struct();
test_implicit_conversions();
return boost::report_errors();
}

112
test/global_flat_ops.cpp Normal file
View File

@@ -0,0 +1,112 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/global_flat_ops.hpp>
#include <boost/core/lightweight_test.hpp>
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <sstream>
#include <set>
#include <string>
#include <boost/functional/hash.hpp>
#include <unordered_set>
template <class T>
void test_comparable_struct() {
T s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
T s2 = s1;
T s3 {0, 1, "Hello", false, 6,7,8,9,10,11111};
BOOST_TEST(s1 == s2);
BOOST_TEST(s1 <= s2);
BOOST_TEST(s1 >= s2);
BOOST_TEST(!(s1 != s2));
BOOST_TEST(!(s1 == s3));
BOOST_TEST(s1 != s3);
BOOST_TEST(s1 < s3);
BOOST_TEST(s3 > s2);
BOOST_TEST(s1 <= s3);
BOOST_TEST(s3 >= s2);
std::cout << s1 << std::endl;
T s4;
std::stringstream ss;
ss.exceptions ( std::ios::failbit);
ss << s1;
ss >> s4;
std::cout << s4 << std::endl;
BOOST_TEST(s1 == s4);
int i = 1, j = 2;
BOOST_TEST_NE(i, j);
}
void test_empty_struct() {
struct empty {};
std::cout << empty{};
BOOST_TEST(empty{} == empty{});
}
struct adl_hash {
template <class T>
std::size_t operator()(const T& val) const {
using namespace boost;
return hash_value(val);
}
};
template <class Comparator>
void test_with_contatiners() {
struct testing { bool b1, b2; int i; };
std::set<testing, Comparator > t{
{true, true, 100},
{false, true, 100},
{true, false, 100},
{true, true, 101}
};
BOOST_TEST(t.find({true, true, 100}) != t.end());
BOOST_TEST_EQ(t.count({true, true, 100}), 1u);
std::unordered_set<testing, adl_hash> us(t.begin(), t.end());
BOOST_TEST_EQ(us.size(), t.size());
}
namespace foo {
struct comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
}
void test_implicit_conversions() {
std::stringstream ss;
ss << std::true_type{};
BOOST_TEST_EQ(ss.str(), "{}"); // Breaks implicit conversion :(
}
int main() {
test_comparable_struct<foo::comparable_struct>();
struct local_comparable_struct {
int i; short s; char data[50]; bool bl; int a,b,c,d,e,f;
};
test_comparable_struct<local_comparable_struct>();
test_empty_struct();
test_with_contatiners<std::less<>>();
test_with_contatiners<std::greater<>>();
test_implicit_conversions();
return boost::report_errors();
}

12
test/minimal.cpp Normal file
View File

@@ -0,0 +1,12 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/core.hpp>
int main() {
struct foo { int i; char c;};
static_assert(boost::pfr::flat_tuple_size_v<foo> == 2, "");
}

View File

@@ -0,0 +1,16 @@
#include <iostream>
#include "boost/pfr.hpp"
struct my_struct { // no ostream operator defined!
int i;
char c;
double d;
};
int main() {
using namespace boost::pfr::flat_ops; // ostream operator out-of-the-box for all PODs!
my_struct s{100, 'H', 3.141593};
std::cout << "my_struct has " << boost::pfr::flat_tuple_size<my_struct>::value
<< " fields: " << s << "\n";
}

16
test/non_aggregate1.cpp Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/core.hpp>
#include <boost/core/lightweight_test.hpp>
#include <tuple>
int main() {
(void)boost::pfr::tuple_size<std::pair<int, short>>::value; // Must be a compile time error
return boost::report_errors();
}

16
test/non_aggregate2.cpp Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/core.hpp>
#include <boost/core/lightweight_test.hpp>
#include <tuple>
int main() {
(void)boost::pfr::flat_tuple_size<std::pair<int, short>>::value; // Must be a compile time error
return boost::report_errors();
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/fields_count.hpp>
int main() {
struct nested { int i; char data[20]; };
struct foo { int i; char c; nested n; };
static_assert(boost::pfr::tuple_size_v<foo> == 3, "");
struct with_reference { int& i; char data; };
static_assert(boost::pfr::tuple_size_v<with_reference> == 2, "");
}

35
test/std_interactions.cpp Normal file
View File

@@ -0,0 +1,35 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/pfr/core.hpp>
#include <boost/core/lightweight_test.hpp>
namespace helper {
template <std::size_t I, class T>
decltype(auto) get(T&& v) {
return boost::pfr::flat_get<I>(std::forward<T>(v));
}
}
int main() {
using namespace std;
using namespace helper;
struct foo { int i; short s;};
foo f{1, 2};
BOOST_TEST_EQ(get<0>(f), 1);
const foo cf{1, 2};
BOOST_TEST_EQ(get<1>(cf), 2);
std::tuple<int, short> t{10, 20};
BOOST_TEST_EQ(get<0>(t), 10);
const std::tuple<int, short> ct{10, 20};
BOOST_TEST_EQ(get<1>(ct), 20);
return boost::report_errors();
}

99
test/test_counts_on.hpp Normal file
View File

@@ -0,0 +1,99 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_TEST_TEST_COUNTS_ON_HPP
#define BOOST_PFR_TEST_TEST_COUNTS_ON_HPP
#include <boost/pfr/core.hpp>
template <class T1, std::size_t CountInT, std::size_t CountHelpers>
void test_counts_on_multiple_chars_impl_1() {
using namespace boost::pfr;
struct t1_c { T1 v1; char c[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_c> == CountInT + CountHelpers, "");
struct t1_s { T1 v1; short s[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_s> == CountInT + CountHelpers, "");
struct t1_i { T1 v1; int i[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_i> == CountInT + CountHelpers, "");
struct t1_p { T1 v1; void* p[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_p> == CountInT + CountHelpers, "");
struct t1_ll { T1 v1; long long ll[CountHelpers]; };
static_assert(flat_tuple_size_v<t1_ll> == CountInT + CountHelpers, "");
struct rt1_c { char c[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_c> == CountInT + CountHelpers, "");
struct rt1_s { short s[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_s> == CountInT + CountHelpers, "");
struct rt1_i { int i[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_i> == CountInT + CountHelpers, "");
struct rt1_p { void* p[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_p> == CountInT + CountHelpers, "");
struct rt1_ll { long long ll[CountHelpers]; T1 v1; };
static_assert(flat_tuple_size_v<rt1_ll> == CountInT + CountHelpers, "");
}
template <class T1, std::size_t CountInT>
void test_counts_on_multiple_chars_impl() {
using namespace boost::pfr;
struct t1_0 { T1 v1; };
static_assert(flat_tuple_size_v<t1_0> == CountInT, "");
static_assert(flat_tuple_size_v<T1> == CountInT, "");
static_assert(flat_tuple_size_v<std::conditional_t<std::is_fundamental<T1>::value, T1*, void*> > == 1, "");
static_assert(flat_tuple_size_v<T1[5]> == CountInT*5, "");
test_counts_on_multiple_chars_impl_1<T1, CountInT, 1>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 2>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 3>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 4>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 5>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 6>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 7>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 8>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 9>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 10>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 11>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 12>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 13>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 14>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 15>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 16>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 17>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 18>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 19>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 20>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 23>();
test_counts_on_multiple_chars_impl_1<T1, CountInT, 24>();
}
template <class T>
void test_counts_on_multiple_chars() {
test_counts_on_multiple_chars_impl<T, 1>();
struct t2 { T v1; T v2; };
test_counts_on_multiple_chars_impl<t2, 2>();
test_counts_on_multiple_chars_impl<T[2], 2>();
test_counts_on_multiple_chars_impl<T[3], 3>();
test_counts_on_multiple_chars_impl<T[4], 4>();
struct t8 { T v1; T v2; T v3; T v4; T v5; T v6; T v7; T v8; };
test_counts_on_multiple_chars_impl<t8, 8>();
}
#endif