Coverage Report

Created: 2026-03-09 06:23

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