value_to.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. //
  2. // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
  3. // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
  4. // Copyright (c) 2021 Dmitry Arkhipov (grisumbras@gmail.com)
  5. //
  6. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  7. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  8. //
  9. // Official repository: https://github.com/boostorg/json
  10. //
  11. #ifndef BOOST_JSON_DETAIL_VALUE_TO_HPP
  12. #define BOOST_JSON_DETAIL_VALUE_TO_HPP
  13. #include <boost/json/value.hpp>
  14. #include <boost/json/conversion.hpp>
  15. #include <boost/describe/enum_from_string.hpp>
  16. #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
  17. # include <optional>
  18. #endif
  19. namespace boost {
  20. namespace json {
  21. template<class T, class U,
  22. typename std::enable_if<
  23. ! std::is_reference<T>::value &&
  24. std::is_same<U, value>::value>::type>
  25. T value_to(U const&);
  26. template<class T>
  27. typename result_for<T, value>::type
  28. try_value_to(const value& jv);
  29. namespace detail {
  30. template<class T>
  31. using has_reserve_member_helper = decltype(std::declval<T&>().reserve(0));
  32. template<class T>
  33. using has_reserve_member = mp11::mp_valid<has_reserve_member_helper, T>;
  34. template<class T>
  35. using reserve_implementation = mp11::mp_cond<
  36. is_tuple_like<T>, mp11::mp_int<2>,
  37. has_reserve_member<T>, mp11::mp_int<1>,
  38. mp11::mp_true, mp11::mp_int<0>>;
  39. template<class T>
  40. error_code
  41. try_reserve(
  42. T&,
  43. std::size_t size,
  44. mp11::mp_int<2>)
  45. {
  46. error_code ec;
  47. constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
  48. if ( N != size )
  49. {
  50. BOOST_JSON_FAIL(ec, error::size_mismatch);
  51. }
  52. return ec;
  53. }
  54. template<typename T>
  55. error_code
  56. try_reserve(
  57. T& cont,
  58. std::size_t size,
  59. mp11::mp_int<1>)
  60. {
  61. cont.reserve(size);
  62. return error_code();
  63. }
  64. template<typename T>
  65. error_code
  66. try_reserve(
  67. T&,
  68. std::size_t,
  69. mp11::mp_int<0>)
  70. {
  71. return error_code();
  72. }
  73. template<class T>
  74. using has_push_back_helper
  75. = decltype(std::declval<T&>().push_back(std::declval<value_type<T>>()));
  76. template<class T>
  77. using has_push_back = mp11::mp_valid<has_push_back_helper, T>;
  78. template<class T>
  79. using inserter_implementation = mp11::mp_cond<
  80. is_tuple_like<T>, mp11::mp_int<2>,
  81. has_push_back<T>, mp11::mp_int<1>,
  82. mp11::mp_true, mp11::mp_int<0>>;
  83. template<class T>
  84. iterator_type<T>
  85. inserter(
  86. T& target,
  87. mp11::mp_int<2>)
  88. {
  89. return target.begin();
  90. }
  91. template<class T>
  92. std::back_insert_iterator<T>
  93. inserter(
  94. T& target,
  95. mp11::mp_int<1>)
  96. {
  97. return std::back_inserter(target);
  98. }
  99. template<class T>
  100. std::insert_iterator<T>
  101. inserter(
  102. T& target,
  103. mp11::mp_int<0>)
  104. {
  105. return std::inserter(target, end(target));
  106. }
  107. // identity conversion
  108. inline
  109. result<value>
  110. value_to_impl(
  111. try_value_to_tag<value>,
  112. value const& jv,
  113. value_conversion_tag)
  114. {
  115. return jv;
  116. }
  117. inline
  118. value
  119. value_to_impl(
  120. value_to_tag<value>,
  121. value const& jv,
  122. value_conversion_tag)
  123. {
  124. return jv;
  125. }
  126. // object
  127. inline
  128. result<object>
  129. value_to_impl(
  130. try_value_to_tag<object>,
  131. value const& jv,
  132. object_conversion_tag)
  133. {
  134. object const* obj = jv.if_object();
  135. if( obj )
  136. return *obj;
  137. error_code ec;
  138. BOOST_JSON_FAIL(ec, error::not_object);
  139. return ec;
  140. }
  141. // array
  142. inline
  143. result<array>
  144. value_to_impl(
  145. try_value_to_tag<array>,
  146. value const& jv,
  147. array_conversion_tag)
  148. {
  149. array const* arr = jv.if_array();
  150. if( arr )
  151. return *arr;
  152. error_code ec;
  153. BOOST_JSON_FAIL(ec, error::not_array);
  154. return ec;
  155. }
  156. // string
  157. inline
  158. result<string>
  159. value_to_impl(
  160. try_value_to_tag<string>,
  161. value const& jv,
  162. string_conversion_tag)
  163. {
  164. string const* str = jv.if_string();
  165. if( str )
  166. return *str;
  167. error_code ec;
  168. BOOST_JSON_FAIL(ec, error::not_string);
  169. return ec;
  170. }
  171. // bool
  172. inline
  173. result<bool>
  174. value_to_impl(
  175. try_value_to_tag<bool>,
  176. value const& jv,
  177. bool_conversion_tag)
  178. {
  179. auto b = jv.if_bool();
  180. if( b )
  181. return *b;
  182. error_code ec;
  183. BOOST_JSON_FAIL(ec, error::not_bool);
  184. return {boost::system::in_place_error, ec};
  185. }
  186. // integral and floating point
  187. template<class T>
  188. result<T>
  189. value_to_impl(
  190. try_value_to_tag<T>,
  191. value const& jv,
  192. number_conversion_tag)
  193. {
  194. error_code ec;
  195. auto const n = jv.to_number<T>(ec);
  196. if( ec.failed() )
  197. return {boost::system::in_place_error, ec};
  198. return {boost::system::in_place_value, n};
  199. }
  200. // null-like conversion
  201. template<class T>
  202. result<T>
  203. value_to_impl(
  204. try_value_to_tag<T>,
  205. value const& jv,
  206. null_like_conversion_tag)
  207. {
  208. if( jv.is_null() )
  209. return {boost::system::in_place_value, T{}};
  210. error_code ec;
  211. BOOST_JSON_FAIL(ec, error::not_null);
  212. return {boost::system::in_place_error, ec};
  213. }
  214. // string-like types
  215. template<class T>
  216. result<T>
  217. value_to_impl(
  218. try_value_to_tag<T>,
  219. value const& jv,
  220. string_like_conversion_tag)
  221. {
  222. auto str = jv.if_string();
  223. if( str )
  224. return {boost::system::in_place_value, T(str->subview())};
  225. error_code ec;
  226. BOOST_JSON_FAIL(ec, error::not_string);
  227. return {boost::system::in_place_error, ec};
  228. }
  229. // map-like containers
  230. template<class T>
  231. result<T>
  232. value_to_impl(
  233. try_value_to_tag<T>,
  234. value const& jv,
  235. map_like_conversion_tag)
  236. {
  237. error_code ec;
  238. object const* obj = jv.if_object();
  239. if( !obj )
  240. {
  241. BOOST_JSON_FAIL(ec, error::not_object);
  242. return {boost::system::in_place_error, ec};
  243. }
  244. T res;
  245. ec = detail::try_reserve(res, obj->size(), reserve_implementation<T>());
  246. if( ec.failed() )
  247. return {boost::system::in_place_error, ec};
  248. auto ins = detail::inserter(res, inserter_implementation<T>());
  249. for( key_value_pair const& kv: *obj )
  250. {
  251. auto elem_res = try_value_to<mapped_type<T>>(kv.value());
  252. if( elem_res.has_error() )
  253. return {boost::system::in_place_error, elem_res.error()};
  254. *ins++ = value_type<T>{
  255. key_type<T>(kv.key()),
  256. std::move(*elem_res)};
  257. }
  258. return res;
  259. }
  260. template<class T>
  261. T
  262. value_to_impl(
  263. value_to_tag<T>,
  264. value const& jv,
  265. map_like_conversion_tag)
  266. {
  267. error_code ec;
  268. object const* obj = jv.if_object();
  269. if( !obj )
  270. {
  271. BOOST_JSON_FAIL(ec, error::not_object);
  272. throw_system_error( ec );
  273. }
  274. T result;
  275. ec = detail::try_reserve(result, obj->size(), reserve_implementation<T>());
  276. if( ec.failed() )
  277. throw_system_error( ec );
  278. auto ins = detail::inserter(result, inserter_implementation<T>());
  279. for( key_value_pair const& kv: *obj )
  280. *ins++ = value_type<T>{
  281. key_type<T>(kv.key()),
  282. value_to<mapped_type<T>>(kv.value())};
  283. return result;
  284. }
  285. // all other containers
  286. template<class T>
  287. result<T>
  288. value_to_impl(
  289. try_value_to_tag<T>,
  290. value const& jv,
  291. sequence_conversion_tag)
  292. {
  293. error_code ec;
  294. array const* arr = jv.if_array();
  295. if( !arr )
  296. {
  297. BOOST_JSON_FAIL(ec, error::not_array);
  298. return {boost::system::in_place_error, ec};
  299. }
  300. T result;
  301. ec = detail::try_reserve(result, arr->size(), reserve_implementation<T>());
  302. if( ec.failed() )
  303. return {boost::system::in_place_error, ec};
  304. auto ins = detail::inserter(result, inserter_implementation<T>());
  305. for( value const& val: *arr )
  306. {
  307. auto elem_res = try_value_to<value_type<T>>(val);
  308. if( elem_res.has_error() )
  309. return {boost::system::in_place_error, elem_res.error()};
  310. *ins++ = std::move(*elem_res);
  311. }
  312. return result;
  313. }
  314. template<class T>
  315. T
  316. value_to_impl(
  317. value_to_tag<T>,
  318. value const& jv,
  319. sequence_conversion_tag)
  320. {
  321. error_code ec;
  322. array const* arr = jv.if_array();
  323. if( !arr )
  324. {
  325. BOOST_JSON_FAIL(ec, error::not_array);
  326. throw_system_error( ec );
  327. }
  328. T result;
  329. ec = detail::try_reserve(result, arr->size(), reserve_implementation<T>());
  330. if( ec.failed() )
  331. throw_system_error( ec );
  332. auto ins = detail::inserter(result, inserter_implementation<T>());
  333. for( value const& val: *arr )
  334. *ins++ = value_to<value_type<T>>(val);
  335. return result;
  336. }
  337. // tuple-like types
  338. template <class T>
  339. result<T>
  340. try_make_tuple_elem(value const& jv, error_code& ec)
  341. {
  342. if( ec.failed() )
  343. return {boost::system::in_place_error, ec};
  344. auto result = try_value_to<T>(jv);
  345. ec = result.error();
  346. return result;
  347. }
  348. template <class T, std::size_t... Is>
  349. result<T>
  350. try_make_tuple_like(array const& arr, boost::mp11::index_sequence<Is...>)
  351. {
  352. error_code ec;
  353. auto items = std::make_tuple(
  354. try_make_tuple_elem<tuple_element_t<Is, T>>(
  355. arr[Is], ec)
  356. ...);
  357. if( ec.failed() )
  358. return {boost::system::in_place_error, ec};
  359. return {
  360. boost::system::in_place_value, T(std::move(*std::get<Is>(items))...)};
  361. }
  362. template <class T, std::size_t... Is>
  363. T
  364. make_tuple_like(array const& arr, boost::mp11::index_sequence<Is...>)
  365. {
  366. return T(value_to<tuple_element_t<Is, T>>(arr[Is])...);
  367. }
  368. template <class T>
  369. result<T>
  370. value_to_impl(
  371. try_value_to_tag<T>,
  372. value const& jv,
  373. tuple_conversion_tag)
  374. {
  375. error_code ec;
  376. array const* arr = jv.if_array();
  377. if( !arr )
  378. {
  379. BOOST_JSON_FAIL(ec, error::not_array);
  380. return {boost::system::in_place_error, ec};
  381. }
  382. constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
  383. if( N != arr->size() )
  384. {
  385. BOOST_JSON_FAIL(ec, error::size_mismatch);
  386. return {boost::system::in_place_error, ec};
  387. }
  388. return try_make_tuple_like<T>(
  389. *arr, boost::mp11::make_index_sequence<N>());
  390. }
  391. template <class T>
  392. T
  393. value_to_impl(
  394. value_to_tag<T>,
  395. value const& jv,
  396. tuple_conversion_tag)
  397. {
  398. error_code ec;
  399. array const* arr = jv.if_array();
  400. if( !arr )
  401. {
  402. BOOST_JSON_FAIL(ec, error::not_array);
  403. throw_system_error( ec );
  404. }
  405. constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
  406. if( N != arr->size() )
  407. {
  408. BOOST_JSON_FAIL(ec, error::size_mismatch);
  409. throw_system_error( ec );
  410. }
  411. return make_tuple_like<T>(
  412. *arr, boost::mp11::make_index_sequence<N>());
  413. }
  414. template< class T>
  415. struct is_optional
  416. : std::false_type
  417. { };
  418. #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
  419. template< class T>
  420. struct is_optional< std::optional<T> >
  421. : std::true_type
  422. { };
  423. #endif // BOOST_NO_CXX17_HDR_OPTIONAL
  424. template< class T >
  425. struct to_described_member
  426. {
  427. using Ds = describe::describe_members<
  428. T, describe::mod_public | describe::mod_inherited>;
  429. template< class D >
  430. using described_member_t = remove_cvref<decltype(
  431. std::declval<T&>().* D::pointer )>;
  432. result<T>& res;
  433. object const& obj;
  434. std::size_t count;
  435. template< class I >
  436. void
  437. operator()(I)
  438. {
  439. if( !res )
  440. return;
  441. using D = mp11::mp_at<Ds, I>;
  442. using M = described_member_t<D>;
  443. auto const found = obj.find(D::name);
  444. if( found == obj.end() )
  445. {
  446. BOOST_IF_CONSTEXPR( !is_optional<M>::value )
  447. {
  448. error_code ec;
  449. BOOST_JSON_FAIL(ec, error::unknown_name);
  450. res = {boost::system::in_place_error, ec};
  451. }
  452. return;
  453. }
  454. #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
  455. # pragma GCC diagnostic push
  456. # pragma GCC diagnostic ignored "-Wunused"
  457. # pragma GCC diagnostic ignored "-Wunused-variable"
  458. #endif
  459. auto member_res = try_value_to<M>(found->value());
  460. #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
  461. # pragma GCC diagnostic pop
  462. #endif
  463. if( member_res )
  464. {
  465. (*res).* D::pointer = std::move(*member_res);
  466. ++count;
  467. }
  468. else
  469. res = {boost::system::in_place_error, member_res.error()};
  470. }
  471. };
  472. // described classes
  473. template<class T>
  474. result<T>
  475. value_to_impl(
  476. try_value_to_tag<T>,
  477. value const& jv,
  478. described_class_conversion_tag)
  479. {
  480. result<T> res;
  481. auto* obj = jv.if_object();
  482. if( !obj )
  483. {
  484. error_code ec;
  485. BOOST_JSON_FAIL(ec, error::not_object);
  486. res = {boost::system::in_place_error, ec};
  487. return res;
  488. }
  489. to_described_member<T> member_converter{res, *obj, 0u};
  490. using Ds = typename decltype(member_converter)::Ds;
  491. constexpr std::size_t N = mp11::mp_size<Ds>::value;
  492. mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
  493. if( !res )
  494. return res;
  495. if( member_converter.count != obj->size() )
  496. {
  497. error_code ec;
  498. BOOST_JSON_FAIL(ec, error::size_mismatch);
  499. res = {boost::system::in_place_error, ec};
  500. return res;
  501. }
  502. return res;
  503. }
  504. // described enums
  505. template<class T>
  506. result<T>
  507. value_to_impl(
  508. try_value_to_tag<T>,
  509. value const& jv,
  510. described_enum_conversion_tag)
  511. {
  512. T val = {};
  513. (void)jv;
  514. #ifdef BOOST_DESCRIBE_CXX14
  515. error_code ec;
  516. auto str = jv.if_string();
  517. if( !str )
  518. {
  519. BOOST_JSON_FAIL(ec, error::not_string);
  520. return {system::in_place_error, ec};
  521. }
  522. if( !describe::enum_from_string(str->data(), val) )
  523. {
  524. BOOST_JSON_FAIL(ec, error::unknown_name);
  525. return {system::in_place_error, ec};
  526. }
  527. #endif
  528. return {system::in_place_value, val};
  529. }
  530. //----------------------------------------------------------
  531. // User-provided conversion
  532. template<class T>
  533. typename std::enable_if<
  534. mp11::mp_valid<has_user_conversion_to_impl, T>::value,
  535. T>::type
  536. value_to_impl(
  537. value_to_tag<T> tag,
  538. value const& jv,
  539. user_conversion_tag)
  540. {
  541. return tag_invoke(tag, jv);
  542. }
  543. template<class T>
  544. typename std::enable_if<
  545. !mp11::mp_valid<has_user_conversion_to_impl, T>::value,
  546. T>::type
  547. value_to_impl(
  548. value_to_tag<T>,
  549. value const& jv,
  550. user_conversion_tag)
  551. {
  552. auto res = tag_invoke(try_value_to_tag<T>(), jv);
  553. if( res.has_error() )
  554. throw_system_error( res.error() );
  555. return std::move(*res);
  556. }
  557. template<class T>
  558. typename std::enable_if<
  559. mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
  560. result<T>>::type
  561. value_to_impl(
  562. try_value_to_tag<T>,
  563. value const& jv,
  564. user_conversion_tag)
  565. {
  566. return tag_invoke(try_value_to_tag<T>(), jv);
  567. }
  568. template<class T>
  569. typename std::enable_if<
  570. !mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
  571. result<T>>::type
  572. value_to_impl(
  573. try_value_to_tag<T>,
  574. value const& jv,
  575. user_conversion_tag)
  576. {
  577. try
  578. {
  579. return {
  580. boost::system::in_place_value, tag_invoke(value_to_tag<T>(), jv)};
  581. }
  582. catch( std::bad_alloc const&)
  583. {
  584. throw;
  585. }
  586. catch( system_error const& e)
  587. {
  588. return {boost::system::in_place_error, e.code()};
  589. }
  590. catch( ... )
  591. {
  592. error_code ec;
  593. BOOST_JSON_FAIL(ec, error::exception);
  594. return {boost::system::in_place_error, ec};
  595. }
  596. }
  597. // no suitable conversion implementation
  598. template<class T>
  599. T
  600. value_to_impl(
  601. value_to_tag<T>,
  602. value const&,
  603. no_conversion_tag)
  604. {
  605. static_assert(
  606. !std::is_same<T, T>::value,
  607. "No suitable tag_invoke overload found for the type");
  608. }
  609. // generic wrapper over non-throwing implementations
  610. template<class T, class Impl>
  611. T
  612. value_to_impl(
  613. value_to_tag<T>,
  614. value const& jv,
  615. Impl impl)
  616. {
  617. return value_to_impl(try_value_to_tag<T>(), jv, impl).value();
  618. }
  619. template<class T>
  620. using value_to_implementation
  621. = conversion_implementation<T, value_to_conversion>;
  622. } // detail
  623. // std::optional
  624. #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
  625. template<class T>
  626. result<std::optional<T>>
  627. tag_invoke(
  628. try_value_to_tag<std::optional<T>>,
  629. value const& jv)
  630. {
  631. if( jv.is_null() )
  632. return std::optional<T>();
  633. else
  634. return try_value_to<T>(jv);
  635. }
  636. inline
  637. result<std::nullopt_t>
  638. tag_invoke(
  639. try_value_to_tag<std::nullopt_t>,
  640. value const& jv)
  641. {
  642. if( jv.is_null() )
  643. return std::nullopt;
  644. error_code ec;
  645. BOOST_JSON_FAIL(ec, error::not_null);
  646. return ec;
  647. }
  648. #endif
  649. // std::variant
  650. #ifndef BOOST_NO_CXX17_HDR_VARIANT
  651. template<class... Ts>
  652. result< std::variant<Ts...> >
  653. tag_invoke(
  654. try_value_to_tag< std::variant<Ts...> >,
  655. value const& jv)
  656. {
  657. error_code ec;
  658. BOOST_JSON_FAIL(ec, error::exhausted_variants);
  659. using Variant = std::variant<Ts...>;
  660. result<Variant> res = {system::in_place_error, ec};
  661. mp11::mp_for_each< mp11::mp_iota_c<sizeof...(Ts)> >([&](auto I) {
  662. if( res )
  663. return;
  664. using T = std::variant_alternative_t<I.value, Variant>;
  665. auto attempt = try_value_to<T>(jv);
  666. if( attempt )
  667. res.emplace(std::in_place_index_t<I>(), std::move(*attempt));
  668. });
  669. if( res.has_error() )
  670. {
  671. res = {system::in_place_error, ec};
  672. }
  673. return res;
  674. }
  675. #endif // BOOST_NO_CXX17_HDR_VARIANT
  676. } // namespace json
  677. } // namespace boost
  678. #endif