From fe6fc5ea4a17dc3dd30c3cb913fa8a3638b66ad3 Mon Sep 17 00:00:00 2001 From: Dmitry Arkhipov Date: Fri, 6 Feb 2026 18:25:18 +0300 Subject: [PATCH] allow forward declaration of has_ traits --- doc/pages/conversion/forward.adoc | 15 +++++++++ include/boost/json/fwd.hpp | 6 ++++ include/boost/json/value_from.hpp | 14 ++++---- include/boost/json/value_to.hpp | 19 +++++------ test/doc_forward_conversion.cpp | 53 +++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 19 deletions(-) diff --git a/doc/pages/conversion/forward.adoc b/doc/pages/conversion/forward.adoc index 582300b08..e26158217 100644 --- a/doc/pages/conversion/forward.adoc +++ b/doc/pages/conversion/forward.adoc @@ -41,3 +41,18 @@ Forward declarations of contextual conversions are done very similarly: ---- include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_3,indent=0] ---- + + +== Checking for Conversion Support + +In some cases it might be necessary for you to check if a type is supported by +JSON's conversion functions. For example, you want to provide a fallback +overload for types that do not have such support. The type traits +<> and <> are intended for such checks. +They can also be forward-declared in case a physical dependency on JSON is +not desirable. + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_4,indent=0] +---- diff --git a/include/boost/json/fwd.hpp b/include/boost/json/fwd.hpp index fa6dd7152..f85f26978 100644 --- a/include/boost/json/fwd.hpp +++ b/include/boost/json/fwd.hpp @@ -84,6 +84,12 @@ template typename result_for::type result_from_errno( int e, boost::source_location const* loc ) noexcept; +template +struct has_value_from; + +template +struct has_value_to; + #endif } // namespace json diff --git a/include/boost/json/value_from.hpp b/include/boost/json/value_from.hpp index 984c32a74..aadab3830 100644 --- a/include/boost/json/value_from.hpp +++ b/include/boost/json/value_from.hpp @@ -153,17 +153,15 @@ value_from( If `T` can be converted to @ref value via a call to @ref value_from, the static data member `value` is defined as `true`. Otherwise, `value` is defined as `false`. - - @see @ref value_from. */ -#ifdef BOOST_JSON_DOCS -template -using has_value_from = __see_below__; -#else template -using has_value_from = detail::can_convert< - detail::remove_cvref, detail::value_from_conversion>; +struct has_value_from +#ifndef BOOST_JSON_DOCS + : detail::can_convert< + detail::remove_cvref, detail::value_from_conversion> +{} #endif +; } // namespace json } // namespace boost diff --git a/include/boost/json/value_to.hpp b/include/boost/json/value_to.hpp index 9fe215861..9e0852da1 100644 --- a/include/boost/json/value_to.hpp +++ b/include/boost/json/value_to.hpp @@ -215,21 +215,18 @@ try_value_to(const value& jv) /** Determine if a @ref value can be converted to `T`. - If @ref value can be converted to `T` via a - call to @ref value_to, the static data member `value` - is defined as `true`. Otherwise, `value` is + If @ref value can be converted to `T` via a call to @ref value_to, the + static data member `value` is defined as `true`. Otherwise, `value` is defined as `false`. - - @see @ref value_to */ -#ifdef BOOST_JSON_DOCS -template -using has_value_to = __see_below__; -#else template -using has_value_to = detail::can_convert< - detail::remove_cvref, detail::value_to_conversion>; +struct has_value_to +#ifndef BOOST_JSON_DOCS + : detail::can_convert< + detail::remove_cvref, detail::value_to_conversion> +{} #endif +; } // namespace json } // namespace boost diff --git a/test/doc_forward_conversion.cpp b/test/doc_forward_conversion.cpp index 21d0531de..0c944f8b1 100644 --- a/test/doc_forward_conversion.cpp +++ b/test/doc_forward_conversion.cpp @@ -32,6 +32,20 @@ try_value_to( const value& jv ); } // end::doc_forward_conversion_1[] +// tag::doc_forward_conversion_4[] +namespace boost { +namespace json { + +template< class T > +struct has_value_from; + +template< class T > +struct has_value_to; + +} +} +// end::doc_forward_conversion_4[] + // tag::doc_forward_conversion_3[] namespace boost { namespace json { @@ -156,6 +170,35 @@ tag_invoke( } // end::doc_forward_conversion_3[] +// tag::doc_forward_conversion_4[] + +namespace third_party +{ + +struct json_storage +{ + boost::json::value& jv; + + template< class T > + friend + typename std::enable_if< boost::json::has_value_from::value >::type + process(json_storage& s, T const& t) + { + boost::json::value_from(t, s.jv); + } + + template< class T > + friend + typename std::enable_if< ! boost::json::has_value_from::value >::type + process(json_storage& s, T const&) + { + boost::json::value_from("unknown value", s.jv); + } +}; + +} +// end::doc_forward_conversion_4[] + #include #include @@ -193,6 +236,16 @@ class doc_forward_conversion BOOST_TEST( get<1>(addr2) == 115 ); BOOST_TEST( get<2>(addr2) == 81 ); BOOST_TEST( get<3>(addr2) == 22 ); + + jv.emplace_null(); + third_party::json_storage s{jv}; + process(s, addr2); + BOOST_TEST(( jv == value{ 212, 115, 81, 22 } )); + + struct some_struct {}; + jv.emplace_null(); + process(s, some_struct{}); + BOOST_TEST(( jv == value{ "unknown value" } )); } };