value_to.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  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. namespace detail {
  22. template<class T>
  23. using has_reserve_member_helper = decltype(std::declval<T&>().reserve(0));
  24. template<class T>
  25. using has_reserve_member = mp11::mp_valid<has_reserve_member_helper, T>;
  26. template<class T>
  27. using reserve_implementation = mp11::mp_cond<
  28. is_tuple_like<T>, mp11::mp_int<2>,
  29. has_reserve_member<T>, mp11::mp_int<1>,
  30. mp11::mp_true, mp11::mp_int<0>>;
  31. template<class T>
  32. error
  33. try_reserve(
  34. T&,
  35. std::size_t size,
  36. mp11::mp_int<2>)
  37. {
  38. constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
  39. if ( N != size )
  40. return error::size_mismatch;
  41. return error();
  42. }
  43. template<typename T>
  44. error
  45. try_reserve(
  46. T& cont,
  47. std::size_t size,
  48. mp11::mp_int<1>)
  49. {
  50. cont.reserve(size);
  51. return error();
  52. }
  53. template<typename T>
  54. error
  55. try_reserve(
  56. T&,
  57. std::size_t,
  58. mp11::mp_int<0>)
  59. {
  60. return error();
  61. }
  62. // identity conversion
  63. template< class Ctx >
  64. result<value>
  65. value_to_impl(
  66. value_conversion_tag,
  67. try_value_to_tag<value>,
  68. value const& jv,
  69. Ctx const& )
  70. {
  71. return jv;
  72. }
  73. template< class Ctx >
  74. value
  75. value_to_impl(
  76. value_conversion_tag, value_to_tag<value>, value const& jv, Ctx const& )
  77. {
  78. return jv;
  79. }
  80. // object
  81. template< class Ctx >
  82. result<object>
  83. value_to_impl(
  84. object_conversion_tag,
  85. try_value_to_tag<object>,
  86. value const& jv,
  87. Ctx const& )
  88. {
  89. object const* obj = jv.if_object();
  90. if( obj )
  91. return *obj;
  92. error_code ec;
  93. BOOST_JSON_FAIL(ec, error::not_object);
  94. return ec;
  95. }
  96. // array
  97. template< class Ctx >
  98. result<array>
  99. value_to_impl(
  100. array_conversion_tag,
  101. try_value_to_tag<array>,
  102. value const& jv,
  103. Ctx const& )
  104. {
  105. array const* arr = jv.if_array();
  106. if( arr )
  107. return *arr;
  108. error_code ec;
  109. BOOST_JSON_FAIL(ec, error::not_array);
  110. return ec;
  111. }
  112. // string
  113. template< class Ctx >
  114. result<string>
  115. value_to_impl(
  116. string_conversion_tag,
  117. try_value_to_tag<string>,
  118. value const& jv,
  119. Ctx const& )
  120. {
  121. string const* str = jv.if_string();
  122. if( str )
  123. return *str;
  124. error_code ec;
  125. BOOST_JSON_FAIL(ec, error::not_string);
  126. return ec;
  127. }
  128. // bool
  129. template< class Ctx >
  130. result<bool>
  131. value_to_impl(
  132. bool_conversion_tag, try_value_to_tag<bool>, value const& jv, Ctx const& )
  133. {
  134. auto b = jv.if_bool();
  135. if( b )
  136. return *b;
  137. error_code ec;
  138. BOOST_JSON_FAIL(ec, error::not_bool);
  139. return {boost::system::in_place_error, ec};
  140. }
  141. // integral and floating point
  142. template< class T, class Ctx >
  143. result<T>
  144. value_to_impl(
  145. number_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
  146. {
  147. error_code ec;
  148. auto const n = jv.to_number<T>(ec);
  149. if( ec.failed() )
  150. return {boost::system::in_place_error, ec};
  151. return {boost::system::in_place_value, n};
  152. }
  153. // null-like conversion
  154. template< class T, class Ctx >
  155. result<T>
  156. value_to_impl(
  157. null_like_conversion_tag,
  158. try_value_to_tag<T>,
  159. value const& jv,
  160. Ctx const& )
  161. {
  162. if( jv.is_null() )
  163. return {boost::system::in_place_value, T{}};
  164. error_code ec;
  165. BOOST_JSON_FAIL(ec, error::not_null);
  166. return {boost::system::in_place_error, ec};
  167. }
  168. // string-like types
  169. template< class T, class Ctx >
  170. result<T>
  171. value_to_impl(
  172. string_like_conversion_tag,
  173. try_value_to_tag<T>,
  174. value const& jv,
  175. Ctx const& )
  176. {
  177. auto str = jv.if_string();
  178. if( str )
  179. return {boost::system::in_place_value, T(str->subview())};
  180. error_code ec;
  181. BOOST_JSON_FAIL(ec, error::not_string);
  182. return {boost::system::in_place_error, ec};
  183. }
  184. // map-like containers
  185. template< class T, class Ctx >
  186. result<T>
  187. value_to_impl(
  188. map_like_conversion_tag,
  189. try_value_to_tag<T>,
  190. value const& jv,
  191. Ctx const& ctx )
  192. {
  193. object const* obj = jv.if_object();
  194. if( !obj )
  195. {
  196. error_code ec;
  197. BOOST_JSON_FAIL(ec, error::not_object);
  198. return {boost::system::in_place_error, ec};
  199. }
  200. T res;
  201. error const e = detail::try_reserve(
  202. res, obj->size(), reserve_implementation<T>());
  203. if( e != error() )
  204. {
  205. error_code ec;
  206. BOOST_JSON_FAIL( ec, e );
  207. return {boost::system::in_place_error, ec};
  208. }
  209. auto ins = detail::inserter(res, inserter_implementation<T>());
  210. for( key_value_pair const& kv: *obj )
  211. {
  212. auto elem_res = try_value_to<mapped_type<T>>( kv.value(), ctx );
  213. if( elem_res.has_error() )
  214. return {boost::system::in_place_error, elem_res.error()};
  215. *ins++ = value_type<T>{
  216. key_type<T>(kv.key()),
  217. std::move(*elem_res)};
  218. }
  219. return res;
  220. }
  221. // all other containers
  222. template< class T, class Ctx >
  223. result<T>
  224. value_to_impl(
  225. sequence_conversion_tag,
  226. try_value_to_tag<T>,
  227. value const& jv,
  228. Ctx const& ctx )
  229. {
  230. array const* arr = jv.if_array();
  231. if( !arr )
  232. {
  233. error_code ec;
  234. BOOST_JSON_FAIL(ec, error::not_array);
  235. return {boost::system::in_place_error, ec};
  236. }
  237. T result;
  238. error const e = detail::try_reserve(
  239. result, arr->size(), reserve_implementation<T>());
  240. if( e != error() )
  241. {
  242. error_code ec;
  243. BOOST_JSON_FAIL( ec, e );
  244. return {boost::system::in_place_error, ec};
  245. }
  246. auto ins = detail::inserter(result, inserter_implementation<T>());
  247. for( value const& val: *arr )
  248. {
  249. auto elem_res = try_value_to<value_type<T>>( val, ctx );
  250. if( elem_res.has_error() )
  251. return {boost::system::in_place_error, elem_res.error()};
  252. *ins++ = std::move(*elem_res);
  253. }
  254. return result;
  255. }
  256. // tuple-like types
  257. template< class T, class Ctx >
  258. result<T>
  259. try_make_tuple_elem(value const& jv, Ctx const& ctx, error_code& ec)
  260. {
  261. if( ec.failed() )
  262. return {boost::system::in_place_error, ec};
  263. auto result = try_value_to<T>( jv, ctx );
  264. ec = result.error();
  265. return result;
  266. }
  267. template <class T, class Ctx, std::size_t... Is>
  268. result<T>
  269. try_make_tuple_like(
  270. array const& arr, Ctx const& ctx, boost::mp11::index_sequence<Is...>)
  271. {
  272. error_code ec;
  273. auto items = std::make_tuple(
  274. try_make_tuple_elem<
  275. typename std::decay<tuple_element_t<Is, T>>::type >(
  276. arr[Is], ctx, ec)
  277. ...);
  278. if( ec.failed() )
  279. return {boost::system::in_place_error, ec};
  280. return {
  281. boost::system::in_place_value, T(std::move(*std::get<Is>(items))...)};
  282. }
  283. template< class T, class Ctx >
  284. result<T>
  285. value_to_impl(
  286. tuple_conversion_tag,
  287. try_value_to_tag<T>,
  288. value const& jv,
  289. Ctx const& ctx )
  290. {
  291. error_code ec;
  292. array const* arr = jv.if_array();
  293. if( !arr )
  294. {
  295. BOOST_JSON_FAIL(ec, error::not_array);
  296. return {boost::system::in_place_error, ec};
  297. }
  298. constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
  299. if( N != arr->size() )
  300. {
  301. BOOST_JSON_FAIL(ec, error::size_mismatch);
  302. return {boost::system::in_place_error, ec};
  303. }
  304. return try_make_tuple_like<T>(
  305. *arr, ctx, boost::mp11::make_index_sequence<N>());
  306. }
  307. template< class Ctx, class T, bool non_throwing = true >
  308. struct to_described_member
  309. {
  310. using Ds = describe::describe_members<
  311. T, describe::mod_public | describe::mod_inherited>;
  312. using result_type = mp11::mp_eval_if_c< !non_throwing, T, result, T >;
  313. result_type& res;
  314. object const& obj;
  315. std::size_t count;
  316. Ctx const& ctx;
  317. template< class I >
  318. void
  319. operator()(I)
  320. {
  321. if( !res )
  322. return;
  323. using D = mp11::mp_at<Ds, I>;
  324. using M = described_member_t<T, D>;
  325. auto const found = obj.find(D::name);
  326. if( found == obj.end() )
  327. {
  328. BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
  329. {
  330. error_code ec;
  331. BOOST_JSON_FAIL(ec, error::unknown_name);
  332. res = {boost::system::in_place_error, ec};
  333. }
  334. return;
  335. }
  336. #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
  337. # pragma GCC diagnostic push
  338. # pragma GCC diagnostic ignored "-Wunused"
  339. # pragma GCC diagnostic ignored "-Wunused-variable"
  340. #endif
  341. auto member_res = try_value_to<M>( found->value(), ctx );
  342. #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
  343. # pragma GCC diagnostic pop
  344. #endif
  345. if( member_res )
  346. {
  347. (*res).* D::pointer = std::move(*member_res);
  348. ++count;
  349. }
  350. else
  351. res = {boost::system::in_place_error, member_res.error()};
  352. }
  353. };
  354. // described classes
  355. template< class T, class Ctx >
  356. result<T>
  357. value_to_impl(
  358. described_class_conversion_tag,
  359. try_value_to_tag<T>,
  360. value const& jv,
  361. Ctx const& ctx )
  362. {
  363. result<T> res;
  364. auto* obj = jv.if_object();
  365. if( !obj )
  366. {
  367. error_code ec;
  368. BOOST_JSON_FAIL(ec, error::not_object);
  369. res = {boost::system::in_place_error, ec};
  370. return res;
  371. }
  372. to_described_member< Ctx, T > member_converter{ res, *obj, 0u, ctx };
  373. using Ds = typename decltype(member_converter)::Ds;
  374. constexpr std::size_t N = mp11::mp_size<Ds>::value;
  375. mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
  376. if( !res )
  377. return res;
  378. if( member_converter.count != obj->size() )
  379. {
  380. error_code ec;
  381. BOOST_JSON_FAIL(ec, error::size_mismatch);
  382. res = {boost::system::in_place_error, ec};
  383. return res;
  384. }
  385. return res;
  386. }
  387. // described enums
  388. template< class T, class Ctx >
  389. result<T>
  390. value_to_impl(
  391. described_enum_conversion_tag,
  392. try_value_to_tag<T>,
  393. value const& jv,
  394. Ctx const& )
  395. {
  396. T val = {};
  397. (void)jv;
  398. #ifdef BOOST_DESCRIBE_CXX14
  399. error_code ec;
  400. auto str = jv.if_string();
  401. if( !str )
  402. {
  403. BOOST_JSON_FAIL(ec, error::not_string);
  404. return {system::in_place_error, ec};
  405. }
  406. if( !describe::enum_from_string(str->data(), val) )
  407. {
  408. BOOST_JSON_FAIL(ec, error::unknown_name);
  409. return {system::in_place_error, ec};
  410. }
  411. #endif
  412. return {system::in_place_value, val};
  413. }
  414. // optionals
  415. template< class T, class Ctx >
  416. result<T>
  417. value_to_impl(
  418. optional_conversion_tag,
  419. try_value_to_tag<T>,
  420. value const& jv,
  421. Ctx const& ctx)
  422. {
  423. using Inner = value_result_type<T>;
  424. if( jv.is_null() )
  425. return {};
  426. else
  427. return try_value_to<Inner>(jv, ctx);
  428. }
  429. // variants
  430. template< class T, class V, class I >
  431. using variant_construction_category = mp11::mp_cond<
  432. std::is_constructible< T, variant2::in_place_index_t<I::value>, V >,
  433. mp11::mp_int<2>,
  434. #ifndef BOOST_NO_CXX17_HDR_VARIANT
  435. std::is_constructible< T, std::in_place_index_t<I::value>, V >,
  436. mp11::mp_int<1>,
  437. #endif // BOOST_NO_CXX17_HDR_VARIANT
  438. mp11::mp_true,
  439. mp11::mp_int<0> >;
  440. template< class T, class I, class V >
  441. T
  442. initialize_variant( V&& v, mp11::mp_int<0> )
  443. {
  444. T t;
  445. t.template emplace<I::value>( std::move(v) );
  446. return t;
  447. }
  448. template< class T, class I, class V >
  449. T
  450. initialize_variant( V&& v, mp11::mp_int<2> )
  451. {
  452. return T( variant2::in_place_index_t<I::value>(), std::move(v) );
  453. }
  454. #ifndef BOOST_NO_CXX17_HDR_VARIANT
  455. template< class T, class I, class V >
  456. T
  457. initialize_variant( V&& v, mp11::mp_int<1> )
  458. {
  459. return T( std::in_place_index_t<I::value>(), std::move(v) );
  460. }
  461. #endif // BOOST_NO_CXX17_HDR_VARIANT
  462. template< class T, class Ctx >
  463. struct alternative_converter
  464. {
  465. result<T>& res;
  466. value const& jv;
  467. Ctx const& ctx;
  468. template< class I >
  469. void operator()( I ) const
  470. {
  471. if( res )
  472. return;
  473. using V = mp11::mp_at<T, I>;
  474. auto attempt = try_value_to<V>(jv, ctx);
  475. if( attempt )
  476. {
  477. using cat = variant_construction_category<T, V, I>;
  478. res = initialize_variant<T, I>( std::move(*attempt), cat() );
  479. }
  480. }
  481. };
  482. template< class T, class Ctx >
  483. result<T>
  484. value_to_impl(
  485. variant_conversion_tag,
  486. try_value_to_tag<T>,
  487. value const& jv,
  488. Ctx const& ctx)
  489. {
  490. error_code ec;
  491. BOOST_JSON_FAIL(ec, error::exhausted_variants);
  492. using Is = mp11::mp_iota< mp11::mp_size<T> >;
  493. result<T> res = {system::in_place_error, ec};
  494. mp11::mp_for_each<Is>( alternative_converter<T, Ctx>{res, jv, ctx} );
  495. return res;
  496. }
  497. //----------------------------------------------------------
  498. // User-provided conversions; throwing -> throwing
  499. template< class T, class Ctx >
  500. mp11::mp_if< mp11::mp_valid<has_user_conversion_to_impl, T>, T >
  501. value_to_impl(
  502. user_conversion_tag, value_to_tag<T> tag, value const& jv, Ctx const&)
  503. {
  504. return tag_invoke(tag, jv);
  505. }
  506. template<
  507. class T,
  508. class Ctx,
  509. class Sup = supported_context<Ctx, T, value_to_conversion>
  510. >
  511. mp11::mp_if<
  512. mp11::mp_valid< has_context_conversion_to_impl, typename Sup::type, T>, T >
  513. value_to_impl(
  514. context_conversion_tag,
  515. value_to_tag<T> tag,
  516. value const& jv,
  517. Ctx const& ctx )
  518. {
  519. return tag_invoke( tag, jv, Sup::get(ctx) );
  520. }
  521. template<
  522. class T,
  523. class Ctx,
  524. class Sup = supported_context<Ctx, T, value_to_conversion>
  525. >
  526. mp11::mp_if<
  527. mp11::mp_valid<
  528. has_full_context_conversion_to_impl, typename Sup::type, T>,
  529. T>
  530. value_to_impl(
  531. full_context_conversion_tag,
  532. value_to_tag<T> tag,
  533. value const& jv,
  534. Ctx const& ctx )
  535. {
  536. return tag_invoke( tag, jv, Sup::get(ctx), ctx );
  537. }
  538. //----------------------------------------------------------
  539. // User-provided conversions; throwing -> nonthrowing
  540. template< class T, class Ctx >
  541. mp11::mp_if_c< !mp11::mp_valid<has_user_conversion_to_impl, T>::value, T>
  542. value_to_impl(
  543. user_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& )
  544. {
  545. auto res = tag_invoke(try_value_to_tag<T>(), jv);
  546. if( res.has_error() )
  547. throw_system_error( res.error() );
  548. return std::move(*res);
  549. }
  550. template<
  551. class T,
  552. class Ctx,
  553. class Sup = supported_context<Ctx, T, value_to_conversion>
  554. >
  555. mp11::mp_if_c<
  556. !mp11::mp_valid<
  557. has_context_conversion_to_impl, typename Sup::type, T>::value,
  558. T>
  559. value_to_impl(
  560. context_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& ctx )
  561. {
  562. auto res = tag_invoke( try_value_to_tag<T>(), jv, Sup::get(ctx) );
  563. if( res.has_error() )
  564. throw_system_error( res.error() );
  565. return std::move(*res);
  566. }
  567. template< class Ctx >
  568. std::tuple<allow_exceptions, Ctx>
  569. make_throwing_context(Ctx const& ctx)
  570. {
  571. return std::tuple<allow_exceptions, Ctx>(allow_exceptions(), ctx);
  572. }
  573. template< class... Ctxes >
  574. std::tuple<allow_exceptions, Ctxes...>
  575. make_throwing_context(std::tuple<Ctxes...> const& ctx)
  576. {
  577. return std::tuple_cat(std::make_tuple( allow_exceptions() ), ctx);
  578. }
  579. template< class... Ctxes >
  580. std::tuple<allow_exceptions, Ctxes...> const&
  581. make_throwing_context(std::tuple<allow_exceptions, Ctxes...> const& ctx)
  582. noexcept
  583. {
  584. return ctx;
  585. }
  586. template<
  587. class T,
  588. class Ctx,
  589. class Sup = supported_context<Ctx, T, value_to_conversion>
  590. >
  591. mp11::mp_if_c<
  592. !mp11::mp_valid<
  593. has_full_context_conversion_to_impl, typename Sup::type, T>::value,
  594. T>
  595. value_to_impl(
  596. full_context_conversion_tag,
  597. value_to_tag<T>,
  598. value const& jv,
  599. Ctx const& ctx )
  600. {
  601. auto res = tag_invoke(
  602. try_value_to_tag<T>(),
  603. jv,
  604. Sup::get(ctx),
  605. make_throwing_context(ctx));
  606. if( res.has_error() )
  607. throw_system_error( res.error() );
  608. return std::move(*res);
  609. }
  610. //----------------------------------------------------------
  611. // User-provided conversions; nonthrowing -> nonthrowing
  612. template< class T, class Ctx >
  613. mp11::mp_if<
  614. mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>, result<T> >
  615. value_to_impl(
  616. user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
  617. {
  618. return tag_invoke(try_value_to_tag<T>(), jv);
  619. }
  620. template<
  621. class T,
  622. class Ctx,
  623. class Sup = supported_context<Ctx, T, value_to_conversion>
  624. >
  625. mp11::mp_if<
  626. mp11::mp_valid<
  627. has_nonthrowing_context_conversion_to_impl, typename Sup::type, T>,
  628. result<T> >
  629. value_to_impl(
  630. context_conversion_tag,
  631. try_value_to_tag<T> tag,
  632. value const& jv,
  633. Ctx const& ctx )
  634. {
  635. return tag_invoke( tag, jv, Sup::get(ctx) );
  636. }
  637. template<
  638. class T,
  639. class Ctx,
  640. class Sup = supported_context<Ctx, T, value_to_conversion>
  641. >
  642. mp11::mp_if<
  643. mp11::mp_valid<
  644. has_nonthrowing_full_context_conversion_to_impl,
  645. typename Sup::type,
  646. T>,
  647. result<T> >
  648. value_to_impl(
  649. full_context_conversion_tag,
  650. try_value_to_tag<T> tag,
  651. value const& jv,
  652. Ctx const& ctx )
  653. {
  654. return tag_invoke( tag, jv, Sup::get(ctx), ctx );
  655. }
  656. //----------------------------------------------------------
  657. // User-provided conversions; nonthrowing -> throwing
  658. template< class Ctx >
  659. struct does_allow_exceptions : std::false_type
  660. { };
  661. template< class... Ctxes >
  662. struct does_allow_exceptions< std::tuple<allow_exceptions, Ctxes...> >
  663. : std::true_type
  664. { };
  665. template< class T, class... Args >
  666. result<T>
  667. wrap_conversion_exceptions( std::true_type, value_to_tag<T>, Args&& ... args )
  668. {
  669. return {
  670. boost::system::in_place_value,
  671. tag_invoke( value_to_tag<T>(), static_cast<Args&&>(args)... )};
  672. }
  673. template< class T, class... Args >
  674. result<T>
  675. wrap_conversion_exceptions( std::false_type, value_to_tag<T>, Args&& ... args )
  676. {
  677. #ifndef BOOST_NO_EXCEPTIONS
  678. try
  679. {
  680. #endif
  681. return wrap_conversion_exceptions(
  682. std::true_type(),
  683. value_to_tag<T>(),
  684. static_cast<Args&&>(args)... );
  685. #ifndef BOOST_NO_EXCEPTIONS
  686. }
  687. catch( std::bad_alloc const&)
  688. {
  689. throw;
  690. }
  691. catch( system_error const& e)
  692. {
  693. return {boost::system::in_place_error, e.code()};
  694. }
  695. catch( ... )
  696. {
  697. error_code ec;
  698. BOOST_JSON_FAIL(ec, error::exception);
  699. return {boost::system::in_place_error, ec};
  700. }
  701. #endif
  702. }
  703. template< class T, class Ctx >
  704. mp11::mp_if_c<
  705. !mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
  706. result<T> >
  707. value_to_impl(
  708. user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
  709. {
  710. return wrap_conversion_exceptions(
  711. does_allow_exceptions<Ctx>(), value_to_tag<T>(), jv);
  712. }
  713. template<
  714. class T,
  715. class Ctx,
  716. class Sup = supported_context<Ctx, T, value_to_conversion>
  717. >
  718. mp11::mp_if_c<
  719. !mp11::mp_valid<
  720. has_nonthrowing_context_conversion_to_impl,
  721. typename Sup::type,
  722. T>::value,
  723. result<T> >
  724. value_to_impl(
  725. context_conversion_tag,
  726. try_value_to_tag<T>,
  727. value const& jv,
  728. Ctx const& ctx )
  729. {
  730. return wrap_conversion_exceptions(
  731. does_allow_exceptions<Ctx>(), value_to_tag<T>(), jv, Sup::get(ctx) );
  732. }
  733. template<
  734. class T,
  735. class Ctx,
  736. class Sup = supported_context<Ctx, T, value_to_conversion>
  737. >
  738. mp11::mp_if_c<
  739. !mp11::mp_valid<
  740. has_nonthrowing_full_context_conversion_to_impl,
  741. typename Sup::type,
  742. T>::value,
  743. result<T> >
  744. value_to_impl(
  745. full_context_conversion_tag,
  746. try_value_to_tag<T>,
  747. value const& jv,
  748. Ctx const& ctx )
  749. {
  750. return wrap_conversion_exceptions(
  751. does_allow_exceptions<Ctx>(),
  752. value_to_tag<T>(),
  753. jv,
  754. Sup::get(ctx),
  755. ctx);
  756. }
  757. // no suitable conversion implementation
  758. template< class T, class Ctx >
  759. T
  760. value_to_impl( no_conversion_tag, value_to_tag<T>, value const&, Ctx const& )
  761. {
  762. static_assert(
  763. !std::is_same<T, T>::value,
  764. "No suitable tag_invoke overload found for the type");
  765. }
  766. // generic wrapper over non-throwing implementations
  767. template< class Impl, class T, class Ctx >
  768. T
  769. value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
  770. {
  771. return value_to_impl(
  772. impl, try_value_to_tag<T>(), jv, make_throwing_context(ctx) ).value();
  773. }
  774. template< class Ctx, class T >
  775. using value_to_category = conversion_category<
  776. Ctx, T, value_to_conversion >;
  777. } // detail
  778. #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
  779. inline
  780. result<std::nullopt_t>
  781. tag_invoke(
  782. try_value_to_tag<std::nullopt_t>,
  783. value const& jv)
  784. {
  785. if( jv.is_null() )
  786. return std::nullopt;
  787. error_code ec;
  788. BOOST_JSON_FAIL(ec, error::not_null);
  789. return ec;
  790. }
  791. #endif
  792. } // namespace json
  793. } // namespace boost
  794. #endif