1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2009 Sebastian Redl
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // For more information, see www.boost.org
9 // ----------------------------------------------------------------------------
10 
11 #ifndef BOOST_PROPERTY_TREE_STREAM_TRANSLATOR_HPP_INCLUDED
12 #define BOOST_PROPERTY_TREE_STREAM_TRANSLATOR_HPP_INCLUDED
13 
14 #include <boost/property_tree/ptree_fwd.hpp>
15 
16 #include <boost/optional.hpp>
17 #include <boost/optional/optional_io.hpp>
18 #include <boost/utility/enable_if.hpp>
19 #include <boost/type_traits/decay.hpp>
20 #include <boost/type_traits/integral_constant.hpp>
21 #include <sstream>
22 #include <string>
23 #include <locale>
24 #include <limits>
25 
26 namespace boost { namespace property_tree
27 {
28 
29     template <typename Ch, typename Traits, typename E, typename Enabler = void>
30     struct customize_stream
31     {
insertboost::property_tree::customize_stream32         static void insert(std::basic_ostream<Ch, Traits>& s, const E& e) {
33             s << e;
34         }
extractboost::property_tree::customize_stream35         static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
36             s >> e;
37             if(!s.eof()) {
38                 s >> std::ws;
39             }
40         }
41     };
42 
43     // No whitespace skipping for single characters.
44     template <typename Ch, typename Traits>
45     struct customize_stream<Ch, Traits, Ch, void>
46     {
insertboost::property_tree::customize_stream47         static void insert(std::basic_ostream<Ch, Traits>& s, Ch e) {
48             s << e;
49         }
extractboost::property_tree::customize_stream50         static void extract(std::basic_istream<Ch, Traits>& s, Ch& e) {
51             s.unsetf(std::ios_base::skipws);
52             s >> e;
53         }
54     };
55 
56     // Ugly workaround for numeric_traits that don't have members when not
57     // specialized, e.g. MSVC.
58     namespace detail
59     {
60         template <bool is_specialized>
61         struct is_inexact_impl
62         {
63             template <typename T>
64             struct test
65             {
66                 typedef boost::false_type type;
67             };
68         };
69         template <>
70         struct is_inexact_impl<true>
71         {
72             template <typename T>
73             struct test
74             {
75               typedef boost::integral_constant<bool,
76                   !std::numeric_limits<T>::is_exact> type;
77             };
78         };
79 
80         template <typename F>
81         struct is_inexact
82         {
83             typedef typename boost::decay<F>::type decayed;
84             typedef typename is_inexact_impl<
85                 std::numeric_limits<decayed>::is_specialized
86             >::BOOST_NESTED_TEMPLATE test<decayed>::type type;
87             static const bool value = type::value;
88         };
89     }
90 
91     template <typename Ch, typename Traits, typename F>
92     struct customize_stream<Ch, Traits, F,
93         typename boost::enable_if< detail::is_inexact<F> >::type
94     >
95     {
insertboost::property_tree::customize_stream96         static void insert(std::basic_ostream<Ch, Traits>& s, const F& e) {
97 #ifndef BOOST_NO_CXX11_NUMERIC_LIMITS
98             s.precision(std::numeric_limits<F>::max_digits10);
99 #else
100             s.precision(std::numeric_limits<F>::digits10 + 2);
101 #endif
102             s << e;
103         }
extractboost::property_tree::customize_stream104         static void extract(std::basic_istream<Ch, Traits>& s, F& e) {
105             s >> e;
106             if(!s.eof()) {
107                 s >> std::ws;
108             }
109         }
110     };
111 
112     template <typename Ch, typename Traits>
113     struct customize_stream<Ch, Traits, bool, void>
114     {
insertboost::property_tree::customize_stream115         static void insert(std::basic_ostream<Ch, Traits>& s, bool e) {
116             s.setf(std::ios_base::boolalpha);
117             s << e;
118         }
extractboost::property_tree::customize_stream119         static void extract(std::basic_istream<Ch, Traits>& s, bool& e) {
120             s >> e;
121             if(s.fail()) {
122                 // Try again in word form.
123                 s.clear();
124                 s.setf(std::ios_base::boolalpha);
125                 s >> e;
126             }
127             if(!s.eof()) {
128                 s >> std::ws;
129             }
130         }
131     };
132 
133     template <typename Ch, typename Traits>
134     struct customize_stream<Ch, Traits, signed char, void>
135     {
insertboost::property_tree::customize_stream136         static void insert(std::basic_ostream<Ch, Traits>& s, signed char e) {
137             s << (int)e;
138         }
extractboost::property_tree::customize_stream139         static void extract(std::basic_istream<Ch, Traits>& s, signed char& e) {
140             int i;
141             s >> i;
142             // out of range?
143             if(i > (std::numeric_limits<signed char>::max)() ||
144                 i < (std::numeric_limits<signed char>::min)())
145             {
146                 s.clear(); // guarantees eof to be unset
147                 e = 0;
148                 s.setstate(std::ios_base::badbit);
149                 return;
150             }
151             e = (signed char)i;
152             if(!s.eof()) {
153                 s >> std::ws;
154             }
155         }
156     };
157 
158     template <typename Ch, typename Traits>
159     struct customize_stream<Ch, Traits, unsigned char, void>
160     {
insertboost::property_tree::customize_stream161         static void insert(std::basic_ostream<Ch, Traits>& s, unsigned char e) {
162             s << (unsigned)e;
163         }
extractboost::property_tree::customize_stream164         static void extract(std::basic_istream<Ch,Traits>& s, unsigned char& e){
165             unsigned i;
166             s >> i;
167             // out of range?
168             if(i > (std::numeric_limits<unsigned char>::max)()) {
169                 s.clear(); // guarantees eof to be unset
170                 e = 0;
171                 s.setstate(std::ios_base::badbit);
172                 return;
173             }
174             e = (unsigned char)i;
175             if(!s.eof()) {
176                 s >> std::ws;
177             }
178         }
179     };
180 
181     /// Implementation of Translator that uses the stream overloads.
182     template <typename Ch, typename Traits, typename Alloc, typename E>
183     class stream_translator
184     {
185         typedef customize_stream<Ch, Traits, E> customized;
186     public:
187         typedef std::basic_string<Ch, Traits, Alloc> internal_type;
188         typedef E external_type;
189 
stream_translator(std::locale loc=std::locale ())190         explicit stream_translator(std::locale loc = std::locale())
191             : m_loc(loc)
192         {}
193 
get_value(const internal_type & v)194         boost::optional<E> get_value(const internal_type &v) {
195             std::basic_istringstream<Ch, Traits, Alloc> iss(v);
196             iss.imbue(m_loc);
197             E e;
198             customized::extract(iss, e);
199             if(iss.fail() || iss.bad() || iss.get() != Traits::eof()) {
200                 return boost::optional<E>();
201             }
202             return e;
203         }
put_value(const E & v)204         boost::optional<internal_type> put_value(const E &v) {
205             std::basic_ostringstream<Ch, Traits, Alloc> oss;
206             oss.imbue(m_loc);
207             customized::insert(oss, v);
208             if(oss) {
209                 return oss.str();
210             }
211             return boost::optional<internal_type>();
212         }
213 
214     private:
215         std::locale m_loc;
216     };
217 
218     // This is the default translator when basic_string is the internal type.
219     // Unless the external type is also basic_string, in which case
220     // id_translator takes over.
221     template <typename Ch, typename Traits, typename Alloc, typename E>
222     struct translator_between<std::basic_string<Ch, Traits, Alloc>, E>
223     {
224         typedef stream_translator<Ch, Traits, Alloc, E> type;
225     };
226 
227 }}
228 
229 #endif
230