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; -} - -