1 | // Boost.Convert test and usage example |
2 | // Copyright (c) 2009-2020 Vladimir Batov. |
3 | // Use, modification and distribution are subject to the Boost Software License, |
4 | // Version 1.0. See http://www.boost.org/LICENSE_1_0.txt. |
5 | |
6 | #include "./test.hpp" |
7 | |
8 | #if !defined(BOOST_CONVERT_CXX14) |
9 | int main(int, char const* []) { return 0; } |
10 | #else |
11 | |
12 | #include <boost/convert.hpp> |
13 | #include <boost/convert/stream.hpp> |
14 | #include <boost/test/tools/floating_point_comparison.hpp> |
15 | #include <cstdio> |
16 | #include <cstdlib> |
17 | #include <stdlib.h> |
18 | |
19 | //[stream_using |
20 | using std::string; |
21 | using std::wstring; |
22 | using boost::convert; |
23 | //] |
24 | //[stream_cnv_namespace_shortcut |
25 | namespace cnv = boost::cnv; |
26 | namespace arg = boost::cnv::parameter; |
27 | //] |
28 | |
29 | static |
30 | void |
31 | test_dbl_to_str() |
32 | { |
33 | boost::cnv::cstream cnv; |
34 | |
35 | cnv(std::fixed); |
36 | |
37 | BOOST_TEST(convert<string>( 99.999, cnv(arg::precision = 2)).value_or("bad" ) == "100.00" ); |
38 | BOOST_TEST(convert<string>( 99.949, cnv(arg::precision = 2)).value_or("bad" ) == "99.95" ); |
39 | BOOST_TEST(convert<string>(-99.949, cnv(arg::precision = 2)).value_or("bad" ) == "-99.95" ); |
40 | BOOST_TEST(convert<string>( 99.949, cnv(arg::precision = 1)).value_or("bad" ) == "99.9" ); |
41 | BOOST_TEST(convert<string>( 0.999, cnv(arg::precision = 2)).value_or("bad" ) == "1.00" ); |
42 | BOOST_TEST(convert<string>( -0.999, cnv(arg::precision = 2)).value_or("bad" ) == "-1.00" ); |
43 | BOOST_TEST(convert<string>( 0.949, cnv(arg::precision = 2)).value_or("bad" ) == "0.95" ); |
44 | BOOST_TEST(convert<string>( -0.949, cnv(arg::precision = 2)).value_or("bad" ) == "-0.95" ); |
45 | BOOST_TEST(convert<string>( 1.949, cnv(arg::precision = 1)).value_or("bad" ) == "1.9" ); |
46 | BOOST_TEST(convert<string>( -1.949, cnv(arg::precision = 1)).value_or("bad" ) == "-1.9" ); |
47 | } |
48 | |
49 | static |
50 | void |
51 | test_numbase() |
52 | { |
53 | //[stream_numbase_example1 |
54 | /*`The following example demonstrates the deployment of `std::dec`, `std::oct` `std::hex` |
55 | manipulators: |
56 | */ |
57 | boost::cnv::cstream ccnv; |
58 | |
59 | BOOST_TEST(convert<int>( "11" , ccnv(std::hex)).value_or(0) == 17); // 11(16) = 17(10) |
60 | BOOST_TEST(convert<int>( "11" , ccnv(std::oct)).value_or(0) == 9); // 11(8) = 9(10) |
61 | BOOST_TEST(convert<int>( "11" , ccnv(std::dec)).value_or(0) == 11); |
62 | |
63 | BOOST_TEST(convert<string>( 18, ccnv(std::hex)).value_or("bad" ) == "12" ); // 18(10) = 12(16) |
64 | BOOST_TEST(convert<string>( 10, ccnv(std::oct)).value_or("bad" ) == "12" ); // 10(10) = 12(8) |
65 | BOOST_TEST(convert<string>( 12, ccnv(std::dec)).value_or("bad" ) == "12" ); |
66 | BOOST_TEST(convert<string>(255, ccnv(arg::base = boost::cnv::base::oct)).value_or("bad" ) == "377" ); |
67 | BOOST_TEST(convert<string>(255, ccnv(arg::base = boost::cnv::base::hex)).value_or("bad" ) == "ff" ); |
68 | BOOST_TEST(convert<string>(255, ccnv(arg::base = boost::cnv::base::dec)).value_or("bad" ) == "255" ); |
69 | |
70 | ccnv(std::showbase); |
71 | |
72 | BOOST_TEST(convert<string>(18, ccnv(std::hex)).value_or("bad" ) == "0x12" ); |
73 | BOOST_TEST(convert<string>(10, ccnv(std::oct)).value_or("bad" ) == "012" ); |
74 | |
75 | ccnv(std::uppercase); |
76 | |
77 | BOOST_TEST(convert<string>(18, ccnv(std::hex)).value_or("bad" ) == "0X12" ); |
78 | //] |
79 | //[stream_numbase_example2 |
80 | BOOST_TEST(convert<int>("11" , ccnv(arg::base = cnv::base::hex)).value_or(0) == 17); |
81 | BOOST_TEST(convert<int>("11" , ccnv(arg::base = cnv::base::oct)).value_or(0) == 9); |
82 | BOOST_TEST(convert<int>("11" , ccnv(arg::base = cnv::base::dec)).value_or(0) == 11); |
83 | //] |
84 | //[wide_stream_numeric_base |
85 | boost::cnv::wstream wcnv; |
86 | |
87 | BOOST_TEST(convert<int>(L"11" , wcnv(std::hex)).value_or(0) == 17); // 11(16) = 17(10) |
88 | BOOST_TEST(convert<int>(L"11" , wcnv(std::oct)).value_or(0) == 9); // 11(8) = 9(10) |
89 | BOOST_TEST(convert<int>(L"11" , wcnv(std::dec)).value_or(0) == 11); |
90 | |
91 | BOOST_TEST(convert<wstring>(254, wcnv(arg::base = cnv::base::dec)).value_or(L"bad" ) == L"254" ); |
92 | BOOST_TEST(convert<wstring>(254, wcnv(arg::base = cnv::base::hex)).value_or(L"bad" ) == L"fe" ); |
93 | BOOST_TEST(convert<wstring>(254, wcnv(arg::base = cnv::base::oct)).value_or(L"bad" ) == L"376" ); |
94 | //] |
95 | } |
96 | |
97 | static |
98 | void |
99 | test_boolalpha() |
100 | { |
101 | boost::cnv::cstream cnv; |
102 | //[stream_boolalpha_example |
103 | BOOST_TEST(convert<string>( true, cnv(std::boolalpha)).value_or("bad" ) == "true" ); |
104 | BOOST_TEST(convert<string>(false, cnv(std::boolalpha)).value_or("bad" ) == "false" ); |
105 | |
106 | BOOST_TEST(convert<bool>( "true" , cnv(std::boolalpha)).value_or(false) == true); |
107 | BOOST_TEST(convert<bool>("false" , cnv(std::boolalpha)).value_or( true) == false); |
108 | |
109 | BOOST_TEST(convert<string>( true, cnv(std::noboolalpha)).value_or("bad" ) == "1" ); |
110 | BOOST_TEST(convert<string>(false, cnv(std::noboolalpha)).value_or("bad" ) == "0" ); |
111 | |
112 | BOOST_TEST(convert<bool>("1" , cnv(std::noboolalpha)).value_or(false) == true); |
113 | BOOST_TEST(convert<bool>("0" , cnv(std::noboolalpha)).value_or( true) == false); |
114 | //] |
115 | } |
116 | |
117 | static |
118 | void |
119 | test_skipws_char() |
120 | { |
121 | //[stream_skipws_example |
122 | boost::cnv::cstream ccnv; |
123 | char const* const cstr_good = " 123" ; |
124 | char const* const cstr_bad = " 123 " ; // std::skipws only affects leading spaces. |
125 | |
126 | ccnv(std::skipws); // Ignore leading whitespaces |
127 | // ccnv(arg::skipws = true); // Ignore leading whitespaces. Alternative interface |
128 | |
129 | BOOST_TEST(convert<int>(cstr_good, ccnv).value_or(0) == 123); |
130 | BOOST_TEST(convert<string>(" 123" , ccnv).value_or("bad" ) == "123" ); |
131 | |
132 | BOOST_TEST(!convert<int>(cstr_bad, ccnv)); |
133 | |
134 | ccnv(std::noskipws); // Do not ignore leading whitespaces |
135 | // ccnv(arg::skipws = false); // Do not ignore leading whitespaces. Alternative interface |
136 | |
137 | // All conversions fail. |
138 | BOOST_TEST(!convert<int>(cstr_good, ccnv)); |
139 | BOOST_TEST(!convert<int>( cstr_bad, ccnv)); |
140 | //] |
141 | } |
142 | |
143 | static |
144 | void |
145 | test_skipws_wchar() |
146 | { |
147 | //[wide_stream_skipws |
148 | boost::cnv::wstream wcnv; |
149 | |
150 | wcnv(std::noskipws); // Do not ignore leading whitespaces |
151 | |
152 | BOOST_TEST( convert<int>( L"123" , wcnv).value_or(0) == 123); |
153 | BOOST_TEST(!convert<int>( L" 123" , wcnv)); |
154 | BOOST_TEST(!convert<int>(L" 123 " , wcnv)); |
155 | |
156 | wcnv(std::skipws); // Ignore leading whitespaces |
157 | // wcnv(arg::skipws = true); // Ignore leading whitespaces. Alternative interface |
158 | |
159 | BOOST_TEST( convert<int>( L" 123" , wcnv).value_or(0) == 123); |
160 | BOOST_TEST(!convert<int>(L" 123 " , wcnv)); |
161 | //] |
162 | } |
163 | |
164 | static |
165 | void |
166 | test_width() |
167 | { |
168 | //[stream_width_example |
169 | boost::cnv::cstream cnv; |
170 | |
171 | boost::optional<string> s01 = convert<string>(12, cnv(std::setw(4))); |
172 | boost::optional<string> s02 = convert<string>(12, cnv(std::setw(5))(std::setfill('*'))); |
173 | boost::optional<string> s03 = convert<string>(12, cnv(std::setw(5))(std::setfill('*'))(std::left)); |
174 | |
175 | BOOST_TEST(s01 && s01.value() == " 12" ); // Field width = 4. |
176 | BOOST_TEST(s02 && s02.value() == "***12" ); // Field width = 5, filler = '*'. |
177 | BOOST_TEST(s03 && s03.value() == "12***" ); // Field width = 5, filler = '*', left adjustment |
178 | |
179 | /*`It needs to be remembered that `boost::cnv::stream` converter uses `std::stream` as its underlying |
180 | conversion engine. Consequently, formatting-related behavior are driven by the `std::stream`. Namely, |
181 | after every operation is performed, the ['default field width is restored]. The values of |
182 | the fill character and the adjustment remain unchanged until they are modified explicitly. |
183 | */ |
184 | |
185 | // The fill and adjustment remain '*' and 'left'. |
186 | boost::optional<string> s11 = convert<string>(12, cnv(arg::width = 4)); |
187 | boost::optional<string> s12 = convert<string>(12, cnv(arg::width = 5) |
188 | (arg::fill = ' ') |
189 | (arg::adjust = cnv::adjust::right)); |
190 | |
191 | BOOST_TEST(s11 && s11.value() == "12**" ); // Field width was set to 4. |
192 | BOOST_TEST(s12 && s12.value() == " 12" ); // Field width was set to 5 with the ' ' filler. |
193 | //] |
194 | } |
195 | |
196 | static |
197 | void |
198 | test_manipulators() |
199 | { |
200 | auto ccnv = boost::cnv::cstream(); |
201 | auto wcnv = boost::cnv::wstream(); |
202 | |
203 | int const hex_v01 = boost::convert<int>("FF" , ccnv(std::hex)).value_or(0); |
204 | int const hex_v02 = boost::convert<int>(L"F" , wcnv(std::hex)).value_or(0); |
205 | int const hex_v03 = boost::convert<int>("FF" , ccnv(std::dec)).value_or(-5); |
206 | int const hex_v04 = boost::convert<int>(L"F" , wcnv(std::dec)).value_or(-6); |
207 | |
208 | BOOST_TEST(hex_v01 == 255); // "FF" |
209 | BOOST_TEST(hex_v02 == 15); // L"F" |
210 | BOOST_TEST(hex_v03 == -5); // Failed conversion |
211 | BOOST_TEST(hex_v04 == -6); // Failed conversion |
212 | |
213 | ccnv(std::noshowbase)(std::nouppercase)(std::oct); |
214 | |
215 | BOOST_TEST(boost::convert<string>(255, ccnv).value_or("bad" ) == "377" ); |
216 | BOOST_TEST(boost::convert<string>( 15, ccnv).value_or("bad" ) == "17" ); |
217 | |
218 | ccnv(std::showbase); |
219 | |
220 | BOOST_TEST(boost::convert<string>(255, ccnv).value_or("bad" ) == "0377" ); |
221 | BOOST_TEST(boost::convert<string>( 15, ccnv).value_or("bad" ) == "017" ); |
222 | |
223 | ccnv(std::uppercase)(std::hex); |
224 | |
225 | BOOST_TEST(boost::convert<string>(255, ccnv).value_or("bad" ) == "0XFF" ); |
226 | BOOST_TEST(boost::convert<string>( 15, ccnv).value_or("bad" ) == "0XF" ); |
227 | |
228 | ccnv(std::noshowbase)(std::nouppercase)(std::oct); |
229 | |
230 | BOOST_TEST(boost::convert<string>(255, ccnv).value_or("bad" ) == "377" ); |
231 | BOOST_TEST(boost::convert<string>( 15, ccnv).value_or("bad" ) == "17" ); |
232 | |
233 | ccnv(std::showbase)(arg::uppercase = true)(arg::base = cnv::base::hex); |
234 | |
235 | BOOST_TEST(boost::convert<string>(255, ccnv).value_or("bad" ) == "0XFF" ); |
236 | BOOST_TEST(boost::convert<string>( 15, ccnv).value_or("bad" ) == "0XF" ); |
237 | } |
238 | |
239 | void |
240 | test_locale_example() |
241 | { |
242 | //[stream_locale_example1 |
243 | boost::cnv::cstream cnv; |
244 | std::locale rus_locale; |
245 | std::locale eng_locale; |
246 | |
247 | char const* eng_locale_name = test::cnv::is_msc ? "English_United States.1251" : "en_US.UTF-8" ; |
248 | char const* rus_locale_name = test::cnv::is_msc ? "Russian_Russia.1251" : "ru_RU.UTF-8" ; |
249 | char const* rus_expected = test::cnv::is_msc ? "1,235e-002" : "1,235e-02" ; |
250 | char const* eng_expected = test::cnv::is_msc ? "1.235e-002" : "1.235e-02" ; |
251 | char const* dbl_expected = test::cnv::is_msc ? "1.2345E-002" : "1.2345E-02" ; |
252 | |
253 | // cnv(std::setprecision(4))(std::uppercase)(std::scientific); |
254 | cnv(arg::precision = 4) |
255 | (arg::uppercase = true) |
256 | (arg::notation = cnv::notation::scientific); |
257 | |
258 | double double_v01 = convert<double>(dbl_expected, cnv).value_or(0); |
259 | string double_s02 = convert<string>(double_v01, cnv).value_or("bad" ); |
260 | |
261 | BOOST_TEST(dbl_expected == double_s02); |
262 | |
263 | try { rus_locale = std::locale(rus_locale_name); } |
264 | catch (...) { printf("Bad locale %s.\n" , rus_locale_name); exit(1); } |
265 | |
266 | try { eng_locale = std::locale(eng_locale_name); } |
267 | catch (...) { printf("Bad locale %s.\n" , eng_locale_name); exit(1); } |
268 | |
269 | // cnv(std::setprecision(3))(std::nouppercase); |
270 | cnv(arg::precision = 3)(arg::uppercase = false); |
271 | |
272 | string double_rus = convert<string>(double_v01, cnv(rus_locale)).value_or("bad double_rus" ); |
273 | string double_eng = convert<string>(double_v01, cnv(eng_locale)).value_or("bad double_eng" ); |
274 | |
275 | BOOST_TEST(double_rus == rus_expected); |
276 | BOOST_TEST(double_eng == eng_expected); |
277 | //] |
278 | } |
279 | |
280 | void |
281 | test_locale(double v, boost::cnv::cstream const& cnv, char const* expected) |
282 | { |
283 | boost::optional<string> res = convert<string>(v, cnv); |
284 | std::string str = res ? *res : "conversion failed" ; |
285 | |
286 | BOOST_TEST(res); |
287 | BOOST_TEST(str == expected); |
288 | |
289 | if (str != expected) |
290 | printf("%s [%d]: result=<%s>, expected=<%s>\n" , __FILE__, __LINE__, str.c_str(), expected); |
291 | } |
292 | |
293 | static |
294 | void |
295 | test_locale() |
296 | { |
297 | boost::cnv::cstream cnv; |
298 | std::locale rus_locale; |
299 | std::locale eng_locale; |
300 | bool eng_ignore = false; |
301 | bool rus_ignore = false; |
302 | char const* eng_locale_name = test::cnv::is_msc ? "English_United States.1251" : "en_US.UTF-8" ; |
303 | char const* rus_locale_name = test::cnv::is_msc ? "Russian_Russia.1251" : "ru_RU.UTF-8" ; |
304 | char const* eng_expected = test::cnv::is_old_msc ? "1.235e-002" : "1.235e-02" ; |
305 | char const* rus_expected = test::cnv::is_old_msc ? "1,235e-002" : "1,235e-02" ; |
306 | char const* dbl_expected = test::cnv::is_old_msc ? "1.2345E-002" : "1.2345E-02" ; |
307 | |
308 | cnv(arg::precision = 4) |
309 | (arg::uppercase = true) |
310 | (arg::notation = cnv::notation::scientific); |
311 | |
312 | double const double_v01 = convert<double>(dbl_expected, cnv).value_or(0); |
313 | string const double_s02 = convert<string>(double_v01, cnv).value_or("bad" ); |
314 | |
315 | BOOST_TEST(double_v01 != 0); |
316 | BOOST_TEST(dbl_expected == double_s02); |
317 | |
318 | if (dbl_expected != double_s02) |
319 | printf("%s [%d]: <%s> != <%s>\n" , __FILE__, __LINE__, dbl_expected, double_s02.c_str()); |
320 | |
321 | try { eng_locale = std::locale(eng_locale_name); } |
322 | catch (...) { printf("Bad locale %s. Ignored.\n" , eng_locale_name); eng_ignore = true; } |
323 | |
324 | try { rus_locale = std::locale(rus_locale_name); } |
325 | catch (...) { printf("Bad locale %s. Ignored.\n" , rus_locale_name); rus_ignore = true; } |
326 | |
327 | // cnv(std::setprecision(3))(std::nouppercase); |
328 | cnv(arg::precision = 3)(arg::uppercase = false); |
329 | |
330 | if (!eng_ignore) test_locale(double_v01, cnv(eng_locale), eng_expected); |
331 | if (!rus_ignore) test_locale(double_v01, cnv(rus_locale), rus_expected); |
332 | } |
333 | |
334 | static |
335 | void |
336 | test_user_str() |
337 | { |
338 | //[stream_my_string |
339 | boost::cnv::cstream cnv; |
340 | my_string my_str("123" ); |
341 | |
342 | cnv(std::setprecision(2))(std::fixed); |
343 | |
344 | BOOST_TEST(convert<int>(my_str, cnv).value_or(0) == 123); |
345 | |
346 | BOOST_TEST(convert<my_string>( 99.999, cnv).value_or("bad" ) == "100.00" ); |
347 | BOOST_TEST(convert<my_string>( 99.949, cnv).value_or("bad" ) == "99.95" ); |
348 | BOOST_TEST(convert<my_string>(-99.949, cnv).value_or("bad" ) == "-99.95" ); |
349 | //] |
350 | } |
351 | |
352 | static |
353 | void |
354 | test_notation() |
355 | { |
356 | //[stream_notation |
357 | boost::cnv::cstream cnv; |
358 | |
359 | BOOST_TEST( "-3.14159" == convert<string>(-3.14159, cnv(arg::notation = cnv::notation::fixed)(arg::precision = 5)).value()); |
360 | BOOST_TEST("-3.142e+00" == convert<string>(-3.14159, cnv(arg::notation = cnv::notation::scientific)(arg::precision = 3)).value()); |
361 | |
362 | // precision doesn't affect hexfloat |
363 | BOOST_TEST("-0x1.921f9f01b866ep+1" == convert<string>(-3.14159, cnv(arg::notation = cnv::notation::hex)).value()); |
364 | |
365 | const auto close = boost::math::fpc::close_at_tolerance<double>(1); |
366 | |
367 | BOOST_TEST_WITH(-3.14159, convert<double>("-3.14159" , cnv(arg::notation = cnv::notation::fixed)).value(), close); |
368 | BOOST_TEST_WITH(-3.14159, convert<double>("-3.142e+00" , cnv(arg::notation = cnv::notation::scientific)).value(), close); |
369 | // not supported due to https://gcc.gnu.org/bugzilla//show_bug.cgi?id=81122 |
370 | // BOOST_TEST_WITH(-3.14159, convert<double>("-0x1.921f9f01b866ep+1", cnv(arg::notation = cnv::notation::hex)).value(), close); |
371 | //] |
372 | } |
373 | |
374 | int |
375 | main(int, char const* []) |
376 | { |
377 | try |
378 | { |
379 | // QNX fails to handle std::skipws for wchat_t. |
380 | // Excluding from tests so that I do not have to stare on the yellow box (regression failure) |
381 | /*********************/ test_skipws_char(); |
382 | if (!test::cnv::is_qnx) test_skipws_wchar(); |
383 | |
384 | test_numbase(); |
385 | test_boolalpha(); |
386 | test_width(); |
387 | test_manipulators(); |
388 | test_locale(); |
389 | test_dbl_to_str(); |
390 | test_user_str(); |
391 | test_notation(); |
392 | } |
393 | catch(boost::bad_optional_access const&) |
394 | { |
395 | BOOST_TEST(!"Caught boost::bad_optional_access exception" ); |
396 | } |
397 | catch(...) |
398 | { |
399 | BOOST_TEST(!"Caught an unknown exception" ); |
400 | } |
401 | return boost::report_errors(); |
402 | } |
403 | |
404 | #endif |
405 | |