From af0a9371265aa9198cb83ab43df09f0faaaad572 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Wed, 28 Dec 2016 22:19:29 +0300 Subject: [PATCH] More tests, some refactoring, flat functions now have their precise version, started improving the documents, fixed issues with const propagations, better separation of functions (now IO functions are not part of the core.hpp) --- doc/Jamfile.v2 | 8 +- doc/pfr.qbk | 77 ++++++++--- example/examples.cpp | 21 +-- include/boost/pfr.hpp | 2 +- .../pfr/detail/cast_to_layout_compatible.hpp | 8 +- include/boost/pfr/detail/core14.hpp | 67 +--------- include/boost/pfr/detail/fields_count.hpp | 1 - include/boost/pfr/detail/size_array.hpp | 80 ++++++++++++ include/boost/pfr/flat.hpp | 2 + include/boost/pfr/flat/core.hpp | 85 +++---------- include/boost/pfr/flat/functions_for.hpp | 5 + include/boost/pfr/flat/global_ops.hpp | 20 +-- include/boost/pfr/flat/io.hpp | 70 ++++++++++ include/boost/pfr/flat/ops.hpp | 1 + include/boost/pfr/flat/tuple_size.hpp | 41 ++++++ include/boost/pfr/precise.hpp | 3 + include/boost/pfr/precise/core.hpp | 120 +++--------------- include/boost/pfr/precise/functions_for.hpp | 81 ++++++++++++ include/boost/pfr/precise/functors.hpp | 3 +- include/boost/pfr/precise/global_ops.hpp | 115 +++++++++++++++++ include/boost/pfr/precise/io.hpp | 100 +++++++++++++++ include/boost/pfr/precise/ops.hpp | 5 +- include/boost/pfr/precise/tuple_size.hpp | 47 +++++++ test/Jamfile.v2 | 30 +++-- .../functions_for.cpp} | 17 ++- .../global_ops.cpp} | 9 ++ test/common/read_write.cpp | 4 +- test/{flat => common}/std_interactions.cpp | 11 +- .../test_tuple_sizes_on.cpp} | 40 ++++-- test/flat/count_fields_on_chars.cpp | 15 --- test/flat/count_fields_on_ints.cpp | 15 --- test/flat/count_fields_on_long_longs.cpp | 15 --- test/flat/count_fields_on_shorts.cpp | 15 --- test/flat/count_fields_on_void_ptrs.cpp | 15 --- 34 files changed, 757 insertions(+), 391 deletions(-) create mode 100644 include/boost/pfr/detail/size_array.hpp create mode 100644 include/boost/pfr/flat/io.hpp create mode 100644 include/boost/pfr/flat/tuple_size.hpp create mode 100644 include/boost/pfr/precise/functions_for.hpp create mode 100644 include/boost/pfr/precise/global_ops.hpp create mode 100644 include/boost/pfr/precise/io.hpp create mode 100644 include/boost/pfr/precise/tuple_size.hpp rename test/{flat/flat_functions_for.cpp => common/functions_for.cpp} (85%) rename test/{flat/global_flat_ops.cpp => common/global_ops.cpp} (95%) rename test/{flat => common}/std_interactions.cpp (72%) rename test/{flat/test_counts_on.hpp => common/test_tuple_sizes_on.cpp} (77%) delete mode 100644 test/flat/count_fields_on_chars.cpp delete mode 100644 test/flat/count_fields_on_ints.cpp delete mode 100644 test/flat/count_fields_on_long_longs.cpp delete mode 100644 test/flat/count_fields_on_shorts.cpp delete mode 100644 test/flat/count_fields_on_void_ptrs.cpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index e1a44ee..0a94532 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -11,6 +11,8 @@ doxygen autodoc : [ glob ../../../boost/pfr.hpp ] [ glob ../../../boost/pfr/*.hpp ] + [ glob ../../../boost/pfr/flat/*.hpp ] + [ glob ../../../boost/pfr/precise/*.hpp ] : EXTRACT_ALL=NO HIDE_UNDOC_MEMBERS=YES @@ -21,8 +23,10 @@ doxygen autodoc INLINE_SIMPLE_STRUCTS=YES SORT_MEMBER_DOCS=NO "ALIASES= \\ - \"flattening{1}=\\xmlonly\\1\\endxmlonly\" \\ - \"podops=\\b See \\b Also: \\xmlonlyThree ways of getting operators\\endxmlonly\" \\ + \"flattening{1}=\\xmlonly\\1\\endxmlonly\" \\ + \"podops=\\b See \\b Also: \\xmlonlyThree ways of getting operators\\endxmlonly\" \\ + \"constexprinit{1}=\\xmlonly\\1\\endxmlonly\" \\ + \"flatpod{1}=\\xmlonly\\1\\endxmlonly\" \\ " "PREDEFINED=\"BOOST_PFR_DOXYGEN_INVOKED\" \\ \"detail::stl_type_info=std::type_info\"" diff --git a/doc/pfr.qbk b/doc/pfr.qbk index af49af7..c923d62 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -1,4 +1,4 @@ -[library Boost.POD Flat Reflection +[library Boost.Precise and Flat Reflection [quickbook 1.6] [version 1.0] [copyright 2016 Antony Polukhin] @@ -23,7 +23,7 @@ This library provides tuple like methods for POD types, making PODs usable in co No macro or other type/member registrations required.] -Boost.POD Flat Reflection (Boost.PFR) adds following out-of-the-box functionality to PODs: +Boost.Precise and Flat Reflection (Boost.PFR) adds following out-of-the-box functionality to PODs: * comparison operators * heterogeneous comparators @@ -45,7 +45,24 @@ Boost.POD Flat Reflection (Boost.PFR) adds following out-of-the-box functionalit [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 Counting fields] [pfr_example_tuple_size] [endsect] ] + +[section Flat or Precise functions to chose] +All the functions that have `flat_` prefix and are defclared in `boost/pfr/flat/*` headers the [*flat] functions, other function are [*precise] and are defclared in `boost/pfr/precise/*`. In previous example you've seen how the the flattening works. + +Flat functions are more limited in their reflection capabilities, but guarantee to not affect debug and release builds in C++14 mode (no additional code would be produced). + +Precise functions have almost unlimited reflection capabilities, but may produce additional code in C++14 mode in debug builds and even in release builds (on old compilers or compilers with bad optimizers). + +Here's how to chose your functions: + +* If you use C++17 - use [*precise] functions +* If you work only with flat POD types in C++14 - use [*flat] functions +* If you work only with POD types in C++14 and wish them flattened - use [*flat] functions +* If you have modern compiler in C++14 mode and work with non-POD literal types with some hierarhy - you are forced to use [*precise] functions + +[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. @@ -57,24 +74,24 @@ There are three ways to start using Boost.PFR hashing, comparison and streaming [ 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 ] ] + [[ [headerref boost/pfr/flat/ops.hpp] or [headerref boost/pfr/precise/ops.hpp] ] [ no ] [ no ] [ yes ] [ yes ] [ no ] [ yes ] ] + [[ [headerref boost/pfr/flat/functions_for.hpp] or [headerref boost/pfr/precise/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/flat/global_ops.hpp] or [headerref boost/pfr/precise/global_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] +[*1) [headerref boost/pfr/precise/ops.hpp] and [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 +#include template 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. + using namespace boost::pfr::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. } }; @@ -83,7 +100,7 @@ This method's effects are local to the function. It works even for local types, However *Argument Dependant Lookup* does not work with it: ``` -#include +#include template struct uniform_comparator_less { bool operator()(const T& lhs, const T& rhs) const noexcept { @@ -93,11 +110,11 @@ struct uniform_comparator_less { }; ``` -[*2) [headerref boost/pfr/flat_functions_for.hpp] approach] +[*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 +#include struct pair_like { int first; @@ -114,7 +131,7 @@ Argument Dependant Lookup works well, `std::less` will find the operators for `s 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 +#include struct empty { operator std::string() { return "empty{}"; } @@ -126,12 +143,12 @@ struct 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] +[*3) [headerref boost/pfr/flat/global_ops.hpp] approach] This approach is for those, who wish to have comparisong/streaming/hashing for all their types. ``` -#include +#include struct pair_like { int first; @@ -161,22 +178,42 @@ Argument Dependant Lookup works well, `std::less` will find the operators for `s * Static variables are ignored * T must be aggregate initializable -C++14 limitations (C++17 fixes those): +[*Flat] functions limitations: + * 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. +C++14 [*precise] functions limitations (C++17 fixes those): + +* T must be constexpr aggregate initializable and all it's fields must be constexpr default constructible +* [funcref boost::pfr::get], [funcref boost::pfr::structure_to_tuple], [funcref boost::pfr::structure_tie], [headerref boost/pfr/precise/core.hpp boost::pfr::tuple_element] require T to be a flat POD type + [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 +* Flat functions: + # at compile-time: use aggregate initialization to detect fields count in user-provided structure + # at compile-time: make 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: create a tuple with exactly the same layout as in user-provided structure + # at run-time: `reinterpret_cast` a pointer to user-provided structure to the tuple pointer (this has no runtime penalty) + # at run-time: a tuple of references to fields is returned => all the tuple methods are available for the structure + +* Precise functions: + # at compile-time: use aggregate initialization to detect fields count in user-provided structure + * C++17: + # at compile-time: structured bindings are used to decompose a type `T` to known amount of fields fields + * C++14: + # at compile-time: let `I` be is an index of current field, it equals 0 + # at run-time: `T` is constructed and field `I` is aggregate initialized using a separate instance of structure that is convertible to anything [note Additional care is taken to make sure that all the information about `T` is avalable to the compiler and that operations on `T` have no side effects, so the compiler can optimize away the unnecessary temporary objects.] + # at compile-time: `I += 1` + # at compile-time: if `I` does not equal fields count goto step [~b.] from inside of the conversion operator of the structure that is convertible to anything + # at run-time: `reinterpret_cast` pointer to user-provided structure to the tuple pointer (this has no runtime penalty) + # at run-time: a tuple of references to fields is returned => 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. ]. diff --git a/example/examples.cpp b/example/examples.cpp index 3906fcf..a50ec1c 100644 --- a/example/examples.cpp +++ b/example/examples.cpp @@ -8,11 +8,11 @@ //[pfr_example_get /*` - The following example shows how to access structure fields by index using [funcref boost::pfr::flat_get]. + The following example shows how to access structure fields by index using [funcref boost::pfr::get]. Let's define some structure: */ -#include +#include struct foo { // defining structure int some_integer; @@ -23,15 +23,15 @@ struct foo { // defining structure 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` +auto& r1 = boost::pfr::get<0>(f); // accessing field with index 0, returns reference to `foo::some_integer` +auto& r2 = boost::pfr::get<1>(f); // accessing field with index 1, returns reference to `foo::c` //] [/pfr_example_get] -//[pfr_example_flat_tuple_size +//[pfr_example_tuple_size /*` - The following example shows how to count fields using [classref boost::pfr::flat_tuple_size]. + The following example shows how to count fields using [classref boost::pfr::tuple_size]. */ -#include +#include struct foo2 { // defining structure int some_integer; @@ -41,15 +41,15 @@ struct foo2 { // defining structure static_assert( - boost::pfr::flat_tuple_size::value // returns total count of fields in `foo2` + boost::pfr::tuple_size::value // returns total count of fields in `foo2` == 3, "" ); static_assert( - boost::pfr::flat_tuple_size::value // works with arrays too! + boost::pfr::tuple_size::value // works with arrays too! == 100, "" ); -//] [/pfr_example_flat_tuple_size] +//] [/pfr_example_tuple_size] @@ -59,6 +59,7 @@ static_assert( Take a look at the `struct my_struct`: */ +#include struct my_struct_nested { short a1; int a2; }; diff --git a/include/boost/pfr.hpp b/include/boost/pfr.hpp index 12b6e6a..3761ae4 100644 --- a/include/boost/pfr.hpp +++ b/include/boost/pfr.hpp @@ -7,7 +7,7 @@ #define BOOST_PFR_HPP /// \file boost/pfr.hpp -/// Includes all the Boost.PFR headers, except \xmlonlyboost/pfr/flat/global_ops.hpp\endxmlonly +/// Includes all the Boost.PFR headers, except \xmlonlyboost/pfr/flat/global_ops.hpp\endxmlonly and \xmlonlyboost/pfr/precise/global_ops.hpp\endxmlonly #include #include diff --git a/include/boost/pfr/detail/cast_to_layout_compatible.hpp b/include/boost/pfr/detail/cast_to_layout_compatible.hpp index fb92f04..1d486e9 100644 --- a/include/boost/pfr/detail/cast_to_layout_compatible.hpp +++ b/include/boost/pfr/detail/cast_to_layout_compatible.hpp @@ -3,8 +3,9 @@ // 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_DETAIL_cast_to_layout_compatible_HPP -#define BOOST_PFR_DETAIL_cast_to_layout_compatible_HPP +#ifndef BOOST_PFR_DETAIL_CAST_TO_LAYOUT_COMPATIBLE_HPP +#define BOOST_PFR_DETAIL_CAST_TO_LAYOUT_COMPATIBLE_HPP +#pragma once #if __cplusplus < 201402L # error C++14 is required for this header. @@ -55,7 +56,6 @@ MAY_ALIAS volatile To& cast_to_layout_compatible(volatile From& val) noexcept { } - template MAY_ALIAS To& cast_to_layout_compatible(From& val) noexcept { MAY_ALIAS To* const t = reinterpret_cast( std::addressof(val) ); @@ -75,4 +75,4 @@ MAY_ALIAS std::enable_if_t::value, To&&> cast_t }}} // namespace boost::pfr::detail -#endif // BOOST_PFR_DETAIL_cast_to_layout_compatible_HPP +#endif // BOOST_PFR_DETAIL_CAST_TO_LAYOUT_COMPATIBLE_HPP diff --git a/include/boost/pfr/detail/core14.hpp b/include/boost/pfr/detail/core14.hpp index ba7bb98..0e0d978 100644 --- a/include/boost/pfr/detail/core14.hpp +++ b/include/boost/pfr/detail/core14.hpp @@ -5,6 +5,7 @@ #ifndef BOOST_PFR_DETAIL_CORE14_HPP #define BOOST_PFR_DETAIL_CORE14_HPP +#pragma once #if __cplusplus < 201402L # error C++14 is required for this header. @@ -17,6 +18,7 @@ #include #include #include +#include #ifdef __clang__ # pragma clang diagnostic push @@ -39,67 +41,6 @@ constexpr T construct_helper() noexcept { // adding const here allows to deal wi return {}; } -///////////////////// Array that has the constexpr -template -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 -constexpr std::size_t get(const size_array& a) noexcept { - static_assert(I < N, "Array index out of bounds"); - return a.data[I]; -} - - template constexpr size_array fields_count_and_type_ids_with_zeros() noexcept; template constexpr auto flat_array_of_type_ids() noexcept; @@ -656,7 +597,7 @@ decltype(auto) tie_as_flat_tuple(T&& val) noexcept { template decltype(auto) as_tuple(T&& val) noexcept { static_assert( - is_flat_refelectable( std::make_index_sequence()>{} ), + is_flat_refelectable>( std::make_index_sequence()>{} ), "Not possible in C++14 to represent that type without loosing information. Use flat_ version or change type definition" ); return tie_as_flat_tuple(std::forward(val)); @@ -776,8 +717,6 @@ void for_each_field_dispatcher(T&& t, F&& f, std::index_sequence) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#undef MAY_ALIAS - #ifdef __clang__ # pragma clang diagnostic pop #endif diff --git a/include/boost/pfr/detail/fields_count.hpp b/include/boost/pfr/detail/fields_count.hpp index 8e940cd..432f618 100644 --- a/include/boost/pfr/detail/fields_count.hpp +++ b/include/boost/pfr/detail/fields_count.hpp @@ -5,7 +5,6 @@ #ifndef BOOST_PFR_DETAIL_FIELDS_COUNT_HPP #define BOOST_PFR_DETAIL_FIELDS_COUNT_HPP - #pragma once #if __cplusplus < 201402L diff --git a/include/boost/pfr/detail/size_array.hpp b/include/boost/pfr/detail/size_array.hpp new file mode 100644 index 0000000..69561cf --- /dev/null +++ b/include/boost/pfr/detail/size_array.hpp @@ -0,0 +1,80 @@ +// 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_DETAIL_SIZE_ARRAY_HPP +#define BOOST_PFR_DETAIL_SIZE_ARRAY_HPP + +#if __cplusplus < 201402L +# error C++14 is required for this header. +#endif + +#include // metaprogramming stuff + +namespace boost { namespace pfr { namespace detail { + +///////////////////// Array that has the constexpr +template +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 +constexpr std::size_t get(const size_array& a) noexcept { + static_assert(I < N, "Array index out of bounds"); + return a.data[I]; +} + + + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_SIZE_ARRAY_HPP diff --git a/include/boost/pfr/flat.hpp b/include/boost/pfr/flat.hpp index f5dd566..246baa6 100644 --- a/include/boost/pfr/flat.hpp +++ b/include/boost/pfr/flat.hpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #endif // BOOST_PFR_FLAT_HPP diff --git a/include/boost/pfr/flat/core.hpp b/include/boost/pfr/flat/core.hpp index 2472a81..8937805 100644 --- a/include/boost/pfr/flat/core.hpp +++ b/include/boost/pfr/flat/core.hpp @@ -14,10 +14,10 @@ #include // metaprogramming stuff #include -#include #include #include #include +#include namespace boost { namespace pfr { @@ -65,25 +65,6 @@ template using flat_tuple_element_t = typename flat_tuple_element::type; -/// \brief Has a static const member variable `value` that contains fields count in a \flattening{flattened} T. -/// -/// \b Example: -/// \code -/// std::array::value > a; -/// \endcode -template -using flat_tuple_size = boost::pfr::detail::size_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 > a; -/// \endcode -template -constexpr std::size_t flat_tuple_size_v = flat_tuple_size::value; - /// \brief Creates an `std::tuple` from a \flattening{flattened} T. /// /// \b Example: @@ -121,56 +102,22 @@ auto flat_structure_tie(T& val /* @cond */, std::enable_if_t< std::is_trivially_ ); } -/// \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 -void flat_write(std::basic_ostream& out, const T& value) { - out << '{'; - detail::print_impl<0, flat_tuple_size_v >::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 -void flat_read(std::basic_istream& in, T& value) { - const auto prev_exceptions = in.exceptions(); - in.exceptions( typename std::basic_istream::iostate(0) ); - const auto prev_flags = in.flags( typename std::basic_istream::fmtflags(0) ); - - char parenthis = {}; - in >> parenthis; - if (parenthis != '{') in.setstate(std::basic_istream::failbit); - detail::read_impl<0, flat_tuple_size_v >::read(in, detail::tie_as_flat_tuple(value)); - - in >> parenthis; - if (parenthis != '}') in.setstate(std::basic_istream::failbit); - - in.flags(prev_flags); - in.exceptions(prev_exceptions); -} - /// Calls `func` for each field of a \flattening{flattened} POD `value`. -/// `func` must have one of the following signatures: -/// * any_return_type(auto value) -/// * any_return_type(auto value, std::size_t i) -/// * any_return_type(auto value, auto i). Here decltype(i) is an `std::integral_constant` +/// +/// \param func must have one of the following signatures: +/// * template any_return_type func(U&& field) // field of value is perfect forwarded to function +/// * template any_return_type func(U&& field, std::size_t i) +/// * template any_return_type func(U&& value, I i) // Here I is an `std::integral_constant` +/// +/// \param value After \flattening{flattening} to each field of this variable will be the `func` applied. +/// +/// \b Example: +/// \code +/// struct my_struct { int i, short s; }; +/// int sum = 0; +/// for_each_field(my_struct{20, 22}, [&sum](const auto& field) { sum += field; }); +/// assert(sum == 42); +/// \endcode template void flat_for_each_field(T&& value, F&& func) { ::boost::pfr::detail::for_each_field_impl( diff --git a/include/boost/pfr/flat/functions_for.hpp b/include/boost/pfr/flat/functions_for.hpp index e676096..cb472de 100644 --- a/include/boost/pfr/flat/functions_for.hpp +++ b/include/boost/pfr/flat/functions_for.hpp @@ -3,6 +3,8 @@ // 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_FLAT_FUNCTIONS_FOR_HPP +#define BOOST_PFR_FLAT_FUNCTIONS_FOR_HPP #pragma once #if __cplusplus < 201402L @@ -10,6 +12,7 @@ #endif #include +#include /// \def BOOST_PFR_FLAT_FUNCTIONS_FOR(T) /// Defines comparison operators and stream operators for T. @@ -73,4 +76,6 @@ } \ /**/ +#endif // BOOST_PFR_FLAT_FUNCTIONS_FOR_HPP + diff --git a/include/boost/pfr/flat/global_ops.hpp b/include/boost/pfr/flat/global_ops.hpp index b13a36c..f2c62ca 100644 --- a/include/boost/pfr/flat/global_ops.hpp +++ b/include/boost/pfr/flat/global_ops.hpp @@ -11,14 +11,16 @@ #endif #include +#include +#include -/// \file boost/pfr/global_flat_ops.hpp +/// \file boost/pfr/flat/global_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 +/// #include /// 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; /// }; @@ -37,7 +39,7 @@ namespace boost { namespace pfr { namespace detail { template - using enable_comparisons = std::enable_if_t< + using enable_flat_comparisons = std::enable_if_t< std::is_same::value && std::is_pod::value, bool >; @@ -63,32 +65,32 @@ namespace boost { namespace pfr { namespace detail { template std::size_t hash_value(const T& value) noexcept; #else template - static boost::pfr::detail::enable_comparisons operator==(const T& lhs, const U& rhs) noexcept { + static boost::pfr::detail::enable_flat_comparisons operator==(const T& lhs, const U& rhs) noexcept { return ::boost::pfr::flat_equal_to{}(lhs, rhs); } template - static boost::pfr::detail::enable_comparisons operator!=(const T& lhs, const U& rhs) noexcept { + static boost::pfr::detail::enable_flat_comparisons operator!=(const T& lhs, const U& rhs) noexcept { return ::boost::pfr::flat_not_equal{}(lhs, rhs); } template - static boost::pfr::detail::enable_comparisons operator<(const T& lhs, const U& rhs) noexcept { + static boost::pfr::detail::enable_flat_comparisons operator<(const T& lhs, const U& rhs) noexcept { return ::boost::pfr::flat_less{}(lhs, rhs); } template - static boost::pfr::detail::enable_comparisons operator>(const T& lhs, const U& rhs) noexcept { + static boost::pfr::detail::enable_flat_comparisons operator>(const T& lhs, const U& rhs) noexcept { return ::boost::pfr::flat_greater{}(lhs, rhs); } template - static boost::pfr::detail::enable_comparisons operator<=(const T& lhs, const U& rhs) noexcept { + static boost::pfr::detail::enable_flat_comparisons operator<=(const T& lhs, const U& rhs) noexcept { return ::boost::pfr::flat_less_equal{}(lhs, rhs); } template - static boost::pfr::detail::enable_comparisons operator>=(const T& lhs, const U& rhs) noexcept { + static boost::pfr::detail::enable_flat_comparisons operator>=(const T& lhs, const U& rhs) noexcept { return ::boost::pfr::flat_greater_equal{}(lhs, rhs); } diff --git a/include/boost/pfr/flat/io.hpp b/include/boost/pfr/flat/io.hpp new file mode 100644 index 0000000..b2c0828 --- /dev/null +++ b/include/boost/pfr/flat/io.hpp @@ -0,0 +1,70 @@ +// 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_FLAT_IO_HPP +#define BOOST_PFR_FLAT_IO_HPP + +#if __cplusplus < 201402L +# error C++14 is required for this header. +#endif + +#include +#include // metaprogramming stuff + +#include +#include +#include +#include + +namespace boost { namespace pfr { + +/// \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 +void flat_write(std::basic_ostream& out, const T& value) { + out << '{'; + detail::print_impl<0, flat_tuple_size_v >::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 +void flat_read(std::basic_istream& in, T& value) { + const auto prev_exceptions = in.exceptions(); + in.exceptions( typename std::basic_istream::iostate(0) ); + const auto prev_flags = in.flags( typename std::basic_istream::fmtflags(0) ); + + char parenthis = {}; + in >> parenthis; + if (parenthis != '{') in.setstate(std::basic_istream::failbit); + detail::read_impl<0, flat_tuple_size_v >::read(in, detail::tie_as_flat_tuple(value)); + + in >> parenthis; + if (parenthis != '}') in.setstate(std::basic_istream::failbit); + + in.flags(prev_flags); + in.exceptions(prev_exceptions); +} + +}} // namespace boost::pfr + +#endif // BOOST_PFR_FLAT_IO_HPP diff --git a/include/boost/pfr/flat/ops.hpp b/include/boost/pfr/flat/ops.hpp index f25d7e8..ad3265c 100644 --- a/include/boost/pfr/flat/ops.hpp +++ b/include/boost/pfr/flat/ops.hpp @@ -12,6 +12,7 @@ #include #include +#include /// \file boost/pfr/flat/ops.hpp /// Contains comparison operators and stream operators for any POD types that do not have their own operators. diff --git a/include/boost/pfr/flat/tuple_size.hpp b/include/boost/pfr/flat/tuple_size.hpp new file mode 100644 index 0000000..bc06eac --- /dev/null +++ b/include/boost/pfr/flat/tuple_size.hpp @@ -0,0 +1,41 @@ +// 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_FLAT_TUPLE_SIZE_HPP +#define BOOST_PFR_FLAT_TUPLE_SIZE_HPP + +#if __cplusplus < 201402L +# error C++14 is required for this header. +#endif + +#include // metaprogramming stuff + +#include +#include + +namespace boost { namespace pfr { + +/// \brief Has a static const member variable `value` that contains fields count in a \flattening{flattened} T. +/// +/// \b Example: +/// \code +/// std::array::value > a; +/// \endcode +template +using flat_tuple_size = boost::pfr::detail::size_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 > a; +/// \endcode +template +constexpr std::size_t flat_tuple_size_v = flat_tuple_size::value; + +}} // namespace boost::pfr + +#endif // BOOST_PFR_FLAT_TUPLE_SIZE_HPP diff --git a/include/boost/pfr/precise.hpp b/include/boost/pfr/precise.hpp index 12a27ab..28f122a 100644 --- a/include/boost/pfr/precise.hpp +++ b/include/boost/pfr/precise.hpp @@ -12,5 +12,8 @@ #include #include #include +#include +#include +#include #endif // BOOST_PFR_PRECISE_HPP diff --git a/include/boost/pfr/precise/core.hpp b/include/boost/pfr/precise/core.hpp index 72e54ac..12f3b28 100644 --- a/include/boost/pfr/precise/core.hpp +++ b/include/boost/pfr/precise/core.hpp @@ -3,17 +3,17 @@ // 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_PRECISE_CORE_HPP #define BOOST_PFR_PRECISE_CORE_HPP +#pragma once #include #include // metaprogramming stuff #include -#include #include +#include #if __cplusplus >= 201606L /* Oulu meeting, not the exact value */ # include #else @@ -22,36 +22,9 @@ namespace boost { namespace pfr { -/// \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 Requires: C++14. -/// -/// \b Example: -/// \code -/// std::array::value > a; -/// \endcode -template -using tuple_size = detail::size_t_< boost::pfr::detail::fields_count() >; - - -/// \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 Requires: C++14. -/// -/// \b Example: -/// \code -/// std::array > a; -/// \endcode -template -constexpr std::size_t tuple_size_v = tuple_size::value; - /// \brief Returns reference or const reference to a field with index `I` in aggregate T. /// -/// \b Requires: C++17 or \simplepod{C++14 simple POD}. +/// \b Requires: C++17 or \flatpod{C++14 flat POD}. /// /// \b Example: /// \code @@ -75,7 +48,7 @@ constexpr decltype(auto) get(T& val) noexcept { /// \brief `tuple_element` has a `typedef type-of-a-field-with-index-I-in-aggregate-T type;` /// -/// \b Requires: C++17 or \simplepod{C++14 simple POD}. +/// \b Requires: C++17 or \flatpod{C++14 flat POD}. /// /// \b Example: /// \code @@ -87,7 +60,7 @@ using tuple_element = typename detail::sequence_tuple::tuple_element::type; /// \brief Creates an `std::tuple` from an aggregate T. /// -/// \b Requires: C++17 or \simplepod{C++14 simple POD}. +/// \b Requires: C++17 or \flatpod{C++14 flat POD}. /// /// \b Example: /// \code @@ -121,7 +94,7 @@ constexpr auto structure_to_tuple(const T& val) noexcept { /// \brief Creates an `std::tuple` with lvalue references to fields of an aggregate T. /// -/// \b Requires: C++17 or \simplepod{C++14 simple POD}. +/// \b Requires: C++17 or \flatpod{C++14 flat POD}. /// /// \b Example: /// \code @@ -140,85 +113,24 @@ constexpr auto structure_tie(T& val) noexcept { ); } - -/// \brief Writes aggregate `value` to `out` +/// Calls `func` for each field of a `value`. /// /// \b Requires: C++17 or \constexprinit{C++14 constexpr aggregate intializable type}. /// -/// \b Example: -/// \code -/// struct my_struct { int i, short s; }; -/// my_struct s{12, 13}; -/// write(std::cout, s); // outputs '{12, 13}' -/// \endcode -template -void write(std::basic_ostream& out, const T& value) { - constexpr std::size_t fields_count = detail::fields_count>(); - out << '{'; -#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */ - detail::print_impl<0, tuple_size_v >::print(out, detail::as_tuple(value)); -#else - ::boost::pfr::detail::for_each_field_dispatcher( - value, - [&out](const auto& val) { - detail::print_impl<0, fields_count>::print(out, val); - }, - std::make_index_sequence{} - ); -#endif - out << '}'; -} - -/// Reads aggregate `value` from stream `in` +/// \param func must have one of the following signatures: +/// * template any_return_type func(U&& field) // field of value is perfect forwarded to function +/// * template any_return_type func(U&& field, std::size_t i) +/// * template any_return_type func(U&& value, I i) // Here I is an `std::integral_constant` /// -/// \b Requires: C++17 or \simplepod{C++14 simple POD}. +/// \param value To each field of this variable will be the `func` applied. /// /// \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); +/// int sum = 0; +/// for_each_field(my_struct{20, 22}, [&sum](const auto& field) { sum += field; }); +/// assert(sum == 42); /// \endcode -template -void read(std::basic_istream& in, T& value) { - constexpr std::size_t fields_count = detail::fields_count>(); - - const auto prev_exceptions = in.exceptions(); - in.exceptions( typename std::basic_istream::iostate(0) ); - const auto prev_flags = in.flags( typename std::basic_istream::fmtflags(0) ); - - char parenthis = {}; - in >> parenthis; - if (parenthis != '{') in.setstate(std::basic_istream::failbit); - -#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */ - detail::read_impl<0, tuple_size_v >::read(in, detail::as_tuple(value)); -#else - ::boost::pfr::detail::for_each_field_dispatcher( - value, - [&in](const auto& val) { - detail::read_impl<0, fields_count>::read(in, val); - }, - std::make_index_sequence{} - ); -#endif - - in >> parenthis; - if (parenthis != '}') in.setstate(std::basic_istream::failbit); - - in.flags(prev_flags); - in.exceptions(prev_exceptions); -} - -/// Calls `func` for each field of a `value`. -/// `func` must have one of the following signatures: -/// * any_return_type(auto value) -/// * any_return_type(auto value, std::size_t i) -/// * any_return_type(auto value, auto i). Here decltype(i) is an `std::integral_constant` template void for_each_field(T&& value, F&& func) { constexpr std::size_t fields_count = detail::fields_count>(); diff --git a/include/boost/pfr/precise/functions_for.hpp b/include/boost/pfr/precise/functions_for.hpp new file mode 100644 index 0000000..f1de098 --- /dev/null +++ b/include/boost/pfr/precise/functions_for.hpp @@ -0,0 +1,81 @@ +// 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_PRECISE_FUNCTIONS_FOR_HPP +#define BOOST_PFR_PRECISE_FUNCTIONS_FOR_HPP +#pragma once + +#if __cplusplus < 201402L +# error C++14 is required for this header. +#endif + +#include +#include + +/// \def BOOST_PFR_PRECISE_FUNCTIONS_FOR(T) +/// Defines comparison operators and stream operators for T. +/// If type T 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 +/// 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_PRECISE_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); +/// bool operator!=(const T& lhs, const T& rhs); +/// bool operator< (const T& lhs, const T& rhs); +/// bool operator> (const T& lhs, const T& rhs); +/// bool operator<=(const T& lhs, const T& rhs); +/// bool operator>=(const T& lhs, const T& rhs); +/// +/// template +/// std::basic_ostream& operator<<(std::basic_ostream& out, const T& value); +/// +/// template +/// std::basic_istream& operator>>(std::basic_istream& in, T& value); +/// +/// // helper function for Boost unordered containers and boost::hash<>. +/// std::size_t hash_value(const T& value); +/// \endcode + +#define BOOST_PFR_PRECISE_FUNCTIONS_FOR(T) \ + static inline bool operator==(const T& lhs, const T& rhs) { return ::boost::pfr::equal_to{}(lhs, rhs); } \ + static inline bool operator!=(const T& lhs, const T& rhs) { return ::boost::pfr::not_equal{}(lhs, rhs); } \ + static inline bool operator< (const T& lhs, const T& rhs) { return ::boost::pfr::less{}(lhs, rhs); } \ + static inline bool operator> (const T& lhs, const T& rhs) { return ::boost::pfr::greater{}(lhs, rhs); } \ + static inline bool operator<=(const T& lhs, const T& rhs) { return ::boost::pfr::less_equal{}(lhs, rhs); } \ + static inline bool operator>=(const T& lhs, const T& rhs) { return ::boost::pfr::greater_equal{}(lhs, rhs); } \ + template \ + static ::std::basic_ostream& operator<<(::std::basic_ostream& out, const T& value) { \ + ::boost::pfr::write(out, value); \ + return out; \ + } \ + template \ + static ::std::basic_istream& operator>>(::std::basic_istream& in, T& value) { \ + ::boost::pfr::read(in, value); \ + return in; \ + } \ + static inline std::size_t hash_value(const T& v) { \ + return ::boost::pfr::hash{}(v); \ + } \ +/**/ + +#endif // BOOST_PFR_PRECISE_FUNCTIONS_FOR_HPP + + diff --git a/include/boost/pfr/precise/functors.hpp b/include/boost/pfr/precise/functors.hpp index 9af2e48..8018fc5 100644 --- a/include/boost/pfr/precise/functors.hpp +++ b/include/boost/pfr/precise/functors.hpp @@ -18,7 +18,8 @@ /// \file boost/pfr/functors.hpp /// Contains functors that are close to the Standard Library ones. /// Each functor iterates over fields of the type. - +/// +/// \b Requires: C++17 or \constexprinit{C++14 constexpr aggregate intializable type}. namespace boost { namespace pfr { namespace detail { diff --git a/include/boost/pfr/precise/global_ops.hpp b/include/boost/pfr/precise/global_ops.hpp new file mode 100644 index 0000000..e3a7aad --- /dev/null +++ b/include/boost/pfr/precise/global_ops.hpp @@ -0,0 +1,115 @@ +// 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_PRECISE_GLOBAL_OPS_HPP +#define BOOST_PFR_PRECISE_GLOBAL_OPS_HPP + +#if __cplusplus < 201402L +# error C++14 is required for this header. +#endif + +#include +#include +#include + +/// \file boost/pfr/precise/global_ops.hpp +/// Contains comparison operators and stream operators for any types that do not have their own operators. +/// If type 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 +/// 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 + using enable_comparisons = std::enable_if_t< + std::is_same::value, + bool + >; + +}}} // namespace boost::pfr::detail +/// @endcond + +#ifdef BOOST_PFR_DOXYGEN_INVOKED + template bool operator==(const T& lhs, const T& rhs); + template bool operator!=(const T& lhs, const T& rhs); + template bool operator< (const T& lhs, const T& rhs); + template bool operator> (const T& lhs, const T& rhs); + template bool operator<=(const T& lhs, const T& rhs); + template bool operator>=(const T& lhs, const T& rhs); + + template + std::basic_ostream& operator<<(std::basic_ostream& out, const T& value); + + template + std::basic_istream& operator>>(std::basic_istream& in, T& value); + + /// \brief helper function for Boost unordered containers and boost::hash<>. + template std::size_t hash_value(const T& value); +#else + template + static boost::pfr::detail::enable_comparisons operator==(const T& lhs, const U& rhs) { + return ::boost::pfr::equal_to{}(lhs, rhs); + } + + template + static boost::pfr::detail::enable_comparisons operator!=(const T& lhs, const U& rhs) { + return ::boost::pfr::not_equal{}(lhs, rhs); + } + + template + static boost::pfr::detail::enable_comparisons operator<(const T& lhs, const U& rhs) { + return ::boost::pfr::less{}(lhs, rhs); + } + + template + static boost::pfr::detail::enable_comparisons operator>(const T& lhs, const U& rhs) { + return ::boost::pfr::greater{}(lhs, rhs); + } + + template + static boost::pfr::detail::enable_comparisons operator<=(const T& lhs, const U& rhs) { + return ::boost::pfr::less_equal{}(lhs, rhs); + } + + template + static boost::pfr::detail::enable_comparisons operator>=(const T& lhs, const U& rhs) { + return ::boost::pfr::greater_equal{}(lhs, rhs); + } + + template + static std::enable_if_t::value, std::basic_ostream&> operator<<(std::basic_ostream& out, const T& value) { + ::boost::pfr::write(out, value); + return out; + } + + template + static std::enable_if_t::value, std::basic_istream&> operator>>(std::basic_istream& in, T& value) { + ::boost::pfr::read(in, value); + return in; + } + + template + static std::size_t hash_value(const T& value) { + return ::boost::pfr::hash{}(value); + } +#endif + +#endif // BOOST_PFR_PRECISE_GLOBAL_OPS_HPP diff --git a/include/boost/pfr/precise/io.hpp b/include/boost/pfr/precise/io.hpp new file mode 100644 index 0000000..f638dc5 --- /dev/null +++ b/include/boost/pfr/precise/io.hpp @@ -0,0 +1,100 @@ +// 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_PRECISE_IO_HPP +#define BOOST_PFR_PRECISE_IO_HPP + +#include +#include // metaprogramming stuff + +#include +#include +#include + +#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */ +# include +#else +# include +#endif + +namespace boost { namespace pfr { + +/// \brief Writes aggregate `value` to `out` +/// +/// \b Requires: C++17 or \constexprinit{C++14 constexpr aggregate intializable type}. +/// +/// \b Example: +/// \code +/// struct my_struct { int i, short s; }; +/// my_struct s{12, 13}; +/// write(std::cout, s); // outputs '{12, 13}' +/// \endcode +template +void write(std::basic_ostream& out, const T& value) { + constexpr std::size_t fields_count = detail::fields_count>(); + out << '{'; +#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */ + detail::print_impl<0, fields_count>::print(out, detail::as_tuple(value)); +#else + ::boost::pfr::detail::for_each_field_dispatcher( + value, + [&out](const auto& val) { + detail::print_impl<0, fields_count>::print(out, val); + }, + std::make_index_sequence{} + ); +#endif + out << '}'; +} + +/// Reads aggregate `value` from stream `in` +/// +/// \b Requires: C++17 or \constexprinit{C++14 constexpr aggregate intializable type}. +/// +/// \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 +void read(std::basic_istream& in, T& value) { + constexpr std::size_t fields_count = detail::fields_count>(); + + const auto prev_exceptions = in.exceptions(); + in.exceptions( typename std::basic_istream::iostate(0) ); + const auto prev_flags = in.flags( typename std::basic_istream::fmtflags(0) ); + + char parenthis = {}; + in >> parenthis; + if (parenthis != '{') in.setstate(std::basic_istream::failbit); + +#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */ + detail::read_impl<0, fields_count>::read(in, detail::as_tuple(value)); +#else + ::boost::pfr::detail::for_each_field_dispatcher( + value, + [&in](const auto& val) { + detail::read_impl<0, fields_count>::read(in, val); + }, + std::make_index_sequence{} + ); +#endif + + in >> parenthis; + if (parenthis != '}') in.setstate(std::basic_istream::failbit); + + in.flags(prev_flags); + in.exceptions(prev_exceptions); +} + +}} // namespace boost::pfr + +#endif // BOOST_PFR_PRECISE_IO_HPP diff --git a/include/boost/pfr/precise/ops.hpp b/include/boost/pfr/precise/ops.hpp index a6f6ae0..f2979cb 100644 --- a/include/boost/pfr/precise/ops.hpp +++ b/include/boost/pfr/precise/ops.hpp @@ -13,6 +13,7 @@ #include #include #include +#include /// \file boost/pfr/precise/ops.hpp /// Contains comparison operators and stream operators for types that do not have their own operators. @@ -20,6 +21,8 @@ /// /// Just write \b using \b namespace \b ops; and operators will be available in scope. /// +/// \b Requires: C++17 or \constexprinit{C++14 constexpr aggregate intializable type}. +/// /// \b Example: /// \code /// #include @@ -39,7 +42,7 @@ /// \podops for other ways to define operators and more details. /// /// \b This \b header \b contains: -namespace boost { namespace pfr { +namespace boost { namespace pfr { namespace detail { diff --git a/include/boost/pfr/precise/tuple_size.hpp b/include/boost/pfr/precise/tuple_size.hpp new file mode 100644 index 0000000..5c741e2 --- /dev/null +++ b/include/boost/pfr/precise/tuple_size.hpp @@ -0,0 +1,47 @@ +// 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_PRECISE_TUPLE_SIZE_HPP +#define BOOST_PFR_PRECISE_TUPLE_SIZE_HPP + +#include +#include // metaprogramming stuff + +#include +#include + +namespace boost { namespace pfr { + +/// \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 Requires: C++14. +/// +/// \b Example: +/// \code +/// std::array::value > a; +/// \endcode +template +using tuple_size = detail::size_t_< boost::pfr::detail::fields_count() >; + + +/// \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 Requires: C++14. +/// +/// \b Example: +/// \code +/// std::array > a; +/// \endcode +template +constexpr std::size_t tuple_size_v = tuple_size::value; + +}} // namespace boost::pfr + +#endif // BOOST_PFR_PRECISE_TUPLE_SIZE_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 946c3ab..d48d0e1 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -7,22 +7,26 @@ test-suite pfr : - [ run common/ops.cpp : : : BOOST_PFR_TEST_FLAT : flat_ops ] - [ run common/ops.cpp : : : BOOST_PFR_TEST_PRECISE : precise_ops ] - [ run common/read_write.cpp : : : BOOST_PFR_TEST_FLAT : flat_read_write ] - [ run common/read_write.cpp : : : BOOST_PFR_TEST_PRECISE : precise_read_write ] + [ run common/ops.cpp : : : BOOST_PFR_TEST_FLAT : flat_ops ] + [ run common/ops.cpp : : : BOOST_PFR_TEST_PRECISE : precise_ops ] + [ run common/global_ops.cpp : : : BOOST_PFR_TEST_FLAT : flat_global_ops ] + [ run common/global_ops.cpp : : : BOOST_PFR_TEST_PRECISE : precise_global_ops ] + [ run common/functions_for.cpp : : : BOOST_PFR_TEST_FLAT : flat_function_for ] + [ run common/functions_for.cpp : : : BOOST_PFR_TEST_PRECISE : precise_function_for ] + [ run common/read_write.cpp : : : BOOST_PFR_TEST_FLAT : flat_read_write ] + [ run common/read_write.cpp : : : BOOST_PFR_TEST_PRECISE : precise_read_write ] + [ run common/std_interactions.cpp : : : BOOST_PFR_TEST_FLAT : flat_std_interactions ] + [ run common/std_interactions.cpp : : : BOOST_PFR_TEST_PRECISE : precise_std_interactions ] + + [ run common/test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=char : test_tuple_sizes_on_chars ] + [ run common/test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=int : test_tuple_sizes_on_ints ] + [ run common/test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=short : test_tuple_sizes_on_shorts ] + [ run common/test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=void* : test_tuple_sizes_on_voidptrs ] + [ run common/test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=std::size_t : test_tuple_sizes_on_size_ts ] + [ run common/test_tuple_sizes_on.cpp : : : BOOST_PFR_RUN_TEST_ON=char BOOST_PFR_RUN_HUGE_TESTS : test_tuple_sizes_on_chars_huge ] [ run flat/core.cpp ] - [ run flat/count_fields_on_chars.cpp ] - [ run flat/count_fields_on_chars.cpp : : : BOOST_PFR_RUN_HUGE_TESTS : count_fields_on_chars_huge ] - [ run flat/count_fields_on_ints.cpp ] - [ run flat/count_fields_on_long_longs.cpp ] - [ run flat/count_fields_on_shorts.cpp ] - [ run flat/count_fields_on_void_ptrs.cpp ] [ run flat/flat_tuple_size.cpp ] - [ run flat/std_interactions.cpp ] - [ run flat/flat_functions_for.cpp ] - [ run flat/global_flat_ops.cpp ] [ run flat/flat_motivating_example.cpp ] [ run flat/flat_for_each_field.cpp ] [ compile-fail flat/flat_tuple_size_on_non_aggregate.cpp ] diff --git a/test/flat/flat_functions_for.cpp b/test/common/functions_for.cpp similarity index 85% rename from test/flat/flat_functions_for.cpp rename to test/common/functions_for.cpp index ebe49e3..3190e71 100644 --- a/test/flat/flat_functions_for.cpp +++ b/test/common/functions_for.cpp @@ -3,7 +3,16 @@ // 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) +#ifdef BOOST_PFR_TEST_FLAT #include +#define BOOST_PFR_TEST_FUNCTIONS_FOR BOOST_PFR_FLAT_FUNCTIONS_FOR +#endif + +#ifdef BOOST_PFR_TEST_PRECISE +#include +#define BOOST_PFR_TEST_FUNCTIONS_FOR BOOST_PFR_PRECISE_FUNCTIONS_FOR +#endif + #include #include @@ -28,7 +37,7 @@ 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) +BOOST_PFR_TEST_FUNCTIONS_FOR(comparable_struct) void test_comparable_struct() { comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11}; @@ -59,7 +68,7 @@ void test_comparable_struct() { } struct empty { operator std::string() { return "empty{}"; } }; -BOOST_PFR_FLAT_FUNCTIONS_FOR(empty) +BOOST_PFR_TEST_FUNCTIONS_FOR(empty) void test_empty_struct() { BOOST_TEST_EQ(empty{}, empty{}); @@ -67,7 +76,7 @@ void test_empty_struct() { namespace foo { struct testing { bool b1, b2; int i; }; - BOOST_PFR_FLAT_FUNCTIONS_FOR(testing); + BOOST_PFR_TEST_FUNCTIONS_FOR(testing); } template @@ -93,7 +102,7 @@ void test_implicit_conversions() { ss.str(""); ss << empty{}; - BOOST_TEST_EQ(ss.str(), "{}"); // Breaks implicit conversion for types marked with BOOST_PFR_FLAT_FUNCTIONS_FOR + BOOST_TEST_EQ(ss.str(), "{}"); // Breaks implicit conversion for types marked with BOOST_PFR_TEST_FUNCTIONS_FOR } int main() { diff --git a/test/flat/global_flat_ops.cpp b/test/common/global_ops.cpp similarity index 95% rename from test/flat/global_flat_ops.cpp rename to test/common/global_ops.cpp index 3109243..ddf00d0 100644 --- a/test/flat/global_flat_ops.cpp +++ b/test/common/global_ops.cpp @@ -3,7 +3,16 @@ // 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) + +#ifdef BOOST_PFR_TEST_FLAT #include +#endif + +#ifdef BOOST_PFR_TEST_PRECISE +#include +#endif + + #include #include diff --git a/test/common/read_write.cpp b/test/common/read_write.cpp index 46a7546..d1ed7b7 100644 --- a/test/common/read_write.cpp +++ b/test/common/read_write.cpp @@ -9,7 +9,7 @@ #ifdef BOOST_PFR_TEST_FLAT -#include +#include namespace boost { namespace test { template @@ -26,7 +26,7 @@ namespace boost { namespace test { #endif #ifdef BOOST_PFR_TEST_PRECISE -#include +#include namespace boost { namespace test { using boost::pfr::read; using boost::pfr::write; diff --git a/test/flat/std_interactions.cpp b/test/common/std_interactions.cpp similarity index 72% rename from test/flat/std_interactions.cpp rename to test/common/std_interactions.cpp index 83d8a36..b3f55c9 100644 --- a/test/flat/std_interactions.cpp +++ b/test/common/std_interactions.cpp @@ -3,13 +3,22 @@ // 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) +#ifdef BOOST_PFR_TEST_FLAT #include +#define BOOST_PFR_TEST_FUNCTION(x) boost::pfr::flat_get(x) +#endif + +#ifdef BOOST_PFR_TEST_PRECISE +#include +#define BOOST_PFR_TEST_FUNCTION(x) boost::pfr::get(x) +#endif + #include namespace helper { template decltype(auto) get(T&& v) { - return boost::pfr::flat_get(std::forward(v)); + return BOOST_PFR_TEST_FUNCTION(std::forward(v)); } } diff --git a/test/flat/test_counts_on.hpp b/test/common/test_tuple_sizes_on.cpp similarity index 77% rename from test/flat/test_counts_on.hpp rename to test/common/test_tuple_sizes_on.cpp index 22cdebf..a1f40b7 100644 --- a/test/flat/test_counts_on.hpp +++ b/test/common/test_tuple_sizes_on.cpp @@ -3,21 +3,21 @@ // 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 +#include template void test_counts_on_multiple_chars_impl_1() { - using namespace boost::pfr; + using boost::pfr::flat_tuple_size_v; + using boost::pfr::tuple_size_v; + struct t1_c { T1 v1; char c[CountHelpers]; }; static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); struct t1_s { T1 v1; short s[CountHelpers]; }; static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); - struct t1_i { T1 v1; int i[CountHelpers]; }; + struct t1_i { T1 v1; const int i[CountHelpers]; }; static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); struct t1_p { T1 v1; void* p[CountHelpers]; }; @@ -30,7 +30,7 @@ void test_counts_on_multiple_chars_impl_1() { struct rt1_c { char c[CountHelpers]; T1 v1; }; static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); - struct rt1_s { short s[CountHelpers]; T1 v1; }; + struct rt1_s { const short s[CountHelpers]; T1 v1; }; static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); struct rt1_i { int i[CountHelpers]; T1 v1; }; @@ -41,28 +41,41 @@ void test_counts_on_multiple_chars_impl_1() { struct rt1_ll { long long ll[CountHelpers]; T1 v1; }; static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); + + struct rt1_ll_1 { rt1_ll v1; }; + static_assert(flat_tuple_size_v == CountInT + CountHelpers, ""); + static_assert(tuple_size_v == 1, ""); } template void test_counts_on_multiple_chars_impl() { - using namespace boost::pfr; + using boost::pfr::flat_tuple_size_v; + using boost::pfr::tuple_size_v; struct t1_0 { T1 v1; }; static_assert(flat_tuple_size_v == CountInT, ""); static_assert(flat_tuple_size_v == CountInT, ""); static_assert(flat_tuple_size_v::value, T1*, void*> > == 1, ""); + static_assert(tuple_size_v == 1, ""); + struct t1_0_1 { t1_0 t1; }; + static_assert(flat_tuple_size_v == CountInT, ""); + static_assert(tuple_size_v == 1, ""); - static_assert(flat_tuple_size_v == CountInT*5, ""); + struct t1_0_2 { t1_0 t1; t1_0 t2; }; + static_assert(flat_tuple_size_v == CountInT * 2, ""); + static_assert(tuple_size_v == 2, ""); + + static_assert(flat_tuple_size_v == CountInT * 5, ""); test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); +#ifdef BOOST_PFR_RUN_HUGE_TESTS test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); -#ifdef BOOST_PFR_RUN_HUGE_TESTS test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); test_counts_on_multiple_chars_impl_1(); @@ -83,9 +96,13 @@ void test_counts_on_multiple_chars_impl() { template void test_counts_on_multiple_chars() { + using boost::pfr::tuple_size_v; + test_counts_on_multiple_chars_impl(); struct t2 { T v1; T v2; }; + static_assert(tuple_size_v == 2, ""); + test_counts_on_multiple_chars_impl(); test_counts_on_multiple_chars_impl(); @@ -96,6 +113,9 @@ void test_counts_on_multiple_chars() { test_counts_on_multiple_chars_impl(); } -#endif +int main() { + test_counts_on_multiple_chars< BOOST_PFR_RUN_TEST_ON >(); + return 0; +} diff --git a/test/flat/count_fields_on_chars.cpp b/test/flat/count_fields_on_chars.cpp deleted file mode 100644 index 0e03b4e..0000000 --- a/test/flat/count_fields_on_chars.cpp +++ /dev/null @@ -1,15 +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 -#include "test_counts_on.hpp" - -int main() { - test_counts_on_multiple_chars(); - - return 0; -} - - diff --git a/test/flat/count_fields_on_ints.cpp b/test/flat/count_fields_on_ints.cpp deleted file mode 100644 index 55fc6c9..0000000 --- a/test/flat/count_fields_on_ints.cpp +++ /dev/null @@ -1,15 +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 -#include "test_counts_on.hpp" - -int main() { - test_counts_on_multiple_chars(); - - return 0; -} - - diff --git a/test/flat/count_fields_on_long_longs.cpp b/test/flat/count_fields_on_long_longs.cpp deleted file mode 100644 index 5ddd14a..0000000 --- a/test/flat/count_fields_on_long_longs.cpp +++ /dev/null @@ -1,15 +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 -#include "test_counts_on.hpp" - -int main() { - test_counts_on_multiple_chars(); - - return 0; -} - - diff --git a/test/flat/count_fields_on_shorts.cpp b/test/flat/count_fields_on_shorts.cpp deleted file mode 100644 index 9c7953d..0000000 --- a/test/flat/count_fields_on_shorts.cpp +++ /dev/null @@ -1,15 +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 -#include "test_counts_on.hpp" - -int main() { - test_counts_on_multiple_chars(); - - return 0; -} - - diff --git a/test/flat/count_fields_on_void_ptrs.cpp b/test/flat/count_fields_on_void_ptrs.cpp deleted file mode 100644 index aa569fa..0000000 --- a/test/flat/count_fields_on_void_ptrs.cpp +++ /dev/null @@ -1,15 +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 -#include "test_counts_on.hpp" - -int main() { - test_counts_on_multiple_chars(); - - return 0; -} - -