diff --git a/.gitignore b/.gitignore index a17f7cad7..15ed8d190 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ temp # Python __pycache__ +build_dir/ diff --git a/include/boost/json/impl/value_ref.hpp b/include/boost/json/impl/value_ref.hpp index 20634cfd1..34c117f99 100644 --- a/include/boost/json/impl/value_ref.hpp +++ b/include/boost/json/impl/value_ref.hpp @@ -26,12 +26,15 @@ from_builtin( std::move(sp)); } +namespace detail { + +// Native type implementation: use value constructor template value -value_ref:: -from_const( +from_const_impl( void const* p, - storage_ptr sp) + storage_ptr sp, + std::true_type /* is_native */) { return value( *reinterpret_cast< @@ -39,12 +42,31 @@ from_const( std::move(sp)); } +// UDT implementation: use value_from +// Note: A suitable tag_invoke overload for value_from_tag must be +// visible at the point of instantiation for the type T. template value -value_ref:: -from_rvalue( +from_const_impl( + void const* p, + storage_ptr sp, + std::false_type /* is_native */) +{ + value jv(std::move(sp)); + value_from( + *reinterpret_cast< + T const*>(p), + jv); + return jv; +} + +// Native type implementation: use value constructor +template +value +from_rvalue_impl( void* p, - storage_ptr sp) + storage_ptr sp, + std::true_type /* is_native */) { return value( std::move( @@ -52,6 +74,52 @@ from_rvalue( std::move(sp)); } +// UDT implementation: use value_from +// Note: A suitable tag_invoke overload for value_from_tag must be +// visible at the point of instantiation for the type T. +template +value +from_rvalue_impl( + void* p, + storage_ptr sp, + std::false_type /* is_native */) +{ + value jv(std::move(sp)); + value_from( + std::move( + *reinterpret_cast(p)), + jv); + return jv; +} + +} // namespace detail + +template +value +value_ref:: +from_const( + void const* p, + storage_ptr sp) +{ + return detail::from_const_impl( + p, + std::move(sp), + detail::is_native_value_type()); +} + +template +value +value_ref:: +from_rvalue( + void* p, + storage_ptr sp) +{ + return detail::from_rvalue_impl( + p, + std::move(sp), + detail::is_native_value_type()); +} + } // namespace json } // namespace boost diff --git a/include/boost/json/value_ref.hpp b/include/boost/json/value_ref.hpp index e3137a308..2ed66c6fa 100644 --- a/include/boost/json/value_ref.hpp +++ b/include/boost/json/value_ref.hpp @@ -27,6 +27,41 @@ class array; class string; #endif +namespace detail { + +// Trait to detect types that have native value constructors. +// These are types for which value(T, storage_ptr) is well-formed: +// - JSON types: value, array, object, string +// - Numeric types: bool, signed/unsigned integers, float, double +// - String-like: string_view, char const* +// - Null: std::nullptr_t +// Types NOT in this list (user-defined types) require value_from conversion. +template +struct is_native_value_type : std::integral_constant::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value +> {}; + +} // namespace detail + //---------------------------------------------------------- /** The type used in initializer lists. diff --git a/test/value_ref.cpp b/test/value_ref.cpp index bb23eed31..27785cb16 100644 --- a/test/value_ref.cpp +++ b/test/value_ref.cpp @@ -12,14 +12,37 @@ #include #include +#include #include "test_suite.hpp" +#include + namespace boost { namespace json { //---------------------------------------------------------- +// UDT for testing value_ref with user-defined types +struct test_udt +{ + int value; + std::string name; +}; + +// tag_invoke for value_from +inline +void +tag_invoke( + value_from_tag, + value& jv, + test_udt const& t) +{ + jv = { {"value", t.value}, {"name", t.name} }; +} + +//---------------------------------------------------------- + // From the javadoc class my_type @@ -389,6 +412,77 @@ class value_ref_test } } + void + testUDT() + { + // Test UDT in value_ref constructor (lvalue) + { + test_udt udt{42, "test"}; + value_ref ref(udt); + value jv = ref; + BOOST_TEST(jv.is_object()); + BOOST_TEST(jv.at("value").as_int64() == 42); + BOOST_TEST(jv.at("name").as_string() == "test"); + } + + // Test UDT in array initializer list (rvalue) + { + array arr{test_udt{100, "rvalue"}}; + BOOST_TEST(arr.size() == 1); + BOOST_TEST(arr[0].is_object()); + BOOST_TEST(arr[0].at("value").as_int64() == 100); + BOOST_TEST(arr[0].at("name").as_string() == "rvalue"); + } + + // Test UDT in array initializer list (lvalue) + { + test_udt udt1{1, "first"}; + test_udt udt2{2, "second"}; + array arr{udt1, udt2, 123}; + BOOST_TEST(arr.size() == 3); + BOOST_TEST(arr[0].is_object()); + BOOST_TEST(arr[0].at("value").as_int64() == 1); + BOOST_TEST(arr[1].at("value").as_int64() == 2); + BOOST_TEST(arr[2].as_int64() == 123); + } + + // Test UDT in value initializer list + { + test_udt udt{42, "test"}; + value jv = {udt, 123}; + BOOST_TEST(jv.is_array()); + BOOST_TEST(jv.at(0).is_object()); + BOOST_TEST(jv.at(1).as_int64() == 123); + } + + // Test UDT as object value + { + test_udt udt{42, "test"}; + object obj{{"udt", udt}, {"num", 123}}; + BOOST_TEST(obj.at("udt").is_object()); + BOOST_TEST(obj.at("udt").at("value").as_int64() == 42); + BOOST_TEST(obj.at("num").as_int64() == 123); + } + + // Test nested initialization with UDT + { + test_udt udt{42, "test"}; + value jv = {{"data", {udt}}, {"count", 1}}; + BOOST_TEST(jv.is_object()); + BOOST_TEST(jv.at("data").is_array()); + BOOST_TEST(jv.at("data").at(0).at("value").as_int64() == 42); + } + + // Test vector of UDTs + { + std::vector udts{{1, "a"}, {2, "b"}}; + value jv = value_from(udts); + BOOST_TEST(jv.is_array()); + BOOST_TEST(jv.at(0).at("value").as_int64() == 1); + BOOST_TEST(jv.at(1).at("value").as_int64() == 2); + } + } + void run() { @@ -397,6 +491,7 @@ class value_ref_test testMakeValue(); testObjects(); testMoveFrom(); + testUDT(); } };