]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2016-2020 Antony Polukhin |
2 | // | |
3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | ||
7 | #ifndef BOOST_PFR_IO_FIELDS_HPP | |
8 | #define BOOST_PFR_IO_FIELDS_HPP | |
9 | #pragma once | |
10 | ||
11 | #include <boost/pfr/detail/config.hpp> | |
12 | ||
13 | #include <boost/pfr/detail/core.hpp> | |
14 | ||
15 | #include <type_traits> | |
16 | #include <utility> // metaprogramming stuff | |
17 | ||
18 | #include <boost/pfr/detail/sequence_tuple.hpp> | |
19 | #include <boost/pfr/detail/io.hpp> | |
20 | #include <boost/pfr/detail/make_integer_sequence.hpp> | |
21 | #include <boost/pfr/tuple_size.hpp> | |
22 | ||
23 | /// \file boost/pfr/io_fields.hpp | |
24 | /// Contains IO manupulator \forcedlink{io_fields} to read/write \aggregate `value` field-by-field. | |
25 | /// | |
26 | /// \b Example: | |
27 | /// \code | |
28 | /// struct my_struct { | |
29 | /// int i; | |
30 | /// short s; | |
31 | /// }; | |
32 | /// | |
33 | /// std::ostream& operator<<(std::ostream& os, const my_struct& x) { | |
34 | /// return os << boost::pfr::io_fields(x); // Equivalent to: os << "{ " << x.i << " ," << x.s << " }" | |
35 | /// } | |
36 | /// | |
37 | /// std::istream& operator>>(std::istream& is, my_struct& x) { | |
38 | /// return is >> boost::pfr::io_fields(x); // Equivalent to: is >> "{ " >> x.i >> " ," >> x.s >> " }" | |
39 | /// } | |
40 | /// \endcode | |
41 | /// | |
42 | /// \podops for other ways to define operators and more details. | |
43 | /// | |
44 | /// \b Synopsis: | |
45 | ||
46 | namespace boost { namespace pfr { | |
47 | ||
48 | namespace detail { | |
49 | ||
50 | template <class T> | |
51 | struct io_fields_impl { | |
52 | T value; | |
53 | }; | |
54 | ||
55 | ||
56 | template <class Char, class Traits, class T> | |
57 | std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, io_fields_impl<const T&>&& x) { | |
58 | const T& value = x.value; | |
59 | constexpr std::size_t fields_count_val = boost::pfr::detail::fields_count<T>(); | |
60 | out << '{'; | |
61 | #if BOOST_PFR_USE_CPP17 || BOOST_PFR_USE_LOOPHOLE | |
62 | detail::print_impl<0, fields_count_val>::print(out, detail::tie_as_tuple(value)); | |
63 | #else | |
64 | ::boost::pfr::detail::for_each_field_dispatcher( | |
65 | value, | |
66 | [&out](const auto& val) { | |
67 | // We can not reuse `fields_count_val` in lambda because compilers had issues with | |
68 | // passing constexpr variables into lambdas. Computing is again is the most portable solution. | |
69 | constexpr std::size_t fields_count_val_lambda = boost::pfr::detail::fields_count<T>(); | |
70 | detail::print_impl<0, fields_count_val_lambda>::print(out, val); | |
71 | }, | |
72 | detail::make_index_sequence<fields_count_val>{} | |
73 | ); | |
74 | #endif | |
75 | return out << '}'; | |
76 | } | |
77 | ||
78 | ||
79 | template <class Char, class Traits, class T> | |
80 | std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, io_fields_impl<T>&& x) { | |
81 | return out << io_fields_impl<const std::remove_reference_t<T>&>{x.value}; | |
82 | } | |
83 | ||
84 | template <class Char, class Traits, class T> | |
85 | std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, io_fields_impl<T&>&& x) { | |
86 | T& value = x.value; | |
87 | constexpr std::size_t fields_count_val = boost::pfr::detail::fields_count<T>(); | |
88 | ||
89 | const auto prev_exceptions = in.exceptions(); | |
90 | in.exceptions( typename std::basic_istream<Char, Traits>::iostate(0) ); | |
91 | const auto prev_flags = in.flags( typename std::basic_istream<Char, Traits>::fmtflags(0) ); | |
92 | ||
93 | char parenthis = {}; | |
94 | in >> parenthis; | |
95 | if (parenthis != '{') in.setstate(std::basic_istream<Char, Traits>::failbit); | |
96 | ||
97 | #if BOOST_PFR_USE_CPP17 || BOOST_PFR_USE_LOOPHOLE | |
98 | detail::read_impl<0, fields_count_val>::read(in, detail::tie_as_tuple(value)); | |
99 | #else | |
100 | ::boost::pfr::detail::for_each_field_dispatcher( | |
101 | value, | |
102 | [&in](const auto& val) { | |
103 | // We can not reuse `fields_count_val` in lambda because compilers had issues with | |
104 | // passing constexpr variables into lambdas. Computing is again is the most portable solution. | |
105 | constexpr std::size_t fields_count_val_lambda = boost::pfr::detail::fields_count<T>(); | |
106 | detail::read_impl<0, fields_count_val_lambda>::read(in, val); | |
107 | }, | |
108 | detail::make_index_sequence<fields_count_val>{} | |
109 | ); | |
110 | #endif | |
111 | ||
112 | in >> parenthis; | |
113 | if (parenthis != '}') in.setstate(std::basic_istream<Char, Traits>::failbit); | |
114 | ||
115 | in.flags(prev_flags); | |
116 | in.exceptions(prev_exceptions); | |
117 | ||
118 | return in; | |
119 | } | |
120 | ||
121 | template <class Char, class Traits, class T> | |
122 | std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, io_fields_impl<const T&>&& ) { | |
123 | static_assert(sizeof(T) && false, "====================> Boost.PFR: Atetmpt to use istream operator on a boost::pfr::io_fields wrapped type T with const qualifier."); | |
124 | return in; | |
125 | } | |
126 | ||
127 | template <class Char, class Traits, class T> | |
128 | std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, io_fields_impl<T>&& ) { | |
129 | static_assert(sizeof(T) && false, "====================> Boost.PFR: Atetmpt to use istream operator on a boost::pfr::io_fields wrapped temporary of type T."); | |
130 | return in; | |
131 | } | |
132 | ||
133 | } // namespace detail | |
134 | ||
135 | /// IO manupulator to read/write \aggregate `value` field-by-field. | |
136 | /// | |
137 | /// \b Example: | |
138 | /// \code | |
139 | /// struct my_struct { | |
140 | /// int i; | |
141 | /// short s; | |
142 | /// }; | |
143 | /// | |
144 | /// std::ostream& operator<<(std::ostream& os, const my_struct& x) { | |
145 | /// return os << boost::pfr::io_fields(x); // Equivalent to: os << "{ " << x.i << " ," << x.s << " }" | |
146 | /// } | |
147 | /// | |
148 | /// std::istream& operator>>(std::istream& is, my_struct& x) { | |
149 | /// return is >> boost::pfr::io_fields(x); // Equivalent to: is >> "{ " >> x.i >> " ," >> x.s >> " }" | |
150 | /// } | |
151 | /// \endcode | |
152 | /// | |
153 | /// Input and output streaming operators for `boost::pfr::io_fields` are symmetric, meaning that you get the original value by streaming it and | |
154 | /// reading back if each fields streaming operator is symmetric. | |
155 | /// | |
156 | /// \customio | |
157 | template <class T> | |
158 | auto io_fields(T&& value) noexcept { | |
159 | return detail::io_fields_impl<T>{std::forward<T>(value)}; | |
160 | } | |
161 | ||
162 | }} // namespace boost::pfr | |
163 | ||
164 | #endif // BOOST_PFR_IO_FIELDS_HPP |