Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ temp

# Python
__pycache__
build_dir/
80 changes: 74 additions & 6 deletions include/boost/json/impl/value_ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,100 @@ from_builtin(
std::move(sp));
}

namespace detail {

// Native type implementation: use value constructor
template<class T>
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<
T const*>(p),
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<class T>
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<class T>
value
from_rvalue_impl(
void* p,
storage_ptr sp)
storage_ptr sp,
std::true_type /* is_native */)
{
return value(
std::move(
*reinterpret_cast<T*>(p)),
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<class T>
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<T*>(p)),
jv);
return jv;
}

} // namespace detail

template<class T>
value
value_ref::
from_const(
void const* p,
storage_ptr sp)
{
return detail::from_const_impl<T>(
p,
std::move(sp),
detail::is_native_value_type<T>());
}

template<class T>
value
value_ref::
from_rvalue(
void* p,
storage_ptr sp)
{
return detail::from_rvalue_impl<T>(
p,
std::move(sp),
detail::is_native_value_type<T>());
}

} // namespace json
} // namespace boost

Expand Down
35 changes: 35 additions & 0 deletions include/boost/json/value_ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<class T>
struct is_native_value_type : std::integral_constant<bool,
std::is_same<T, value>::value ||
std::is_same<T, array>::value ||
std::is_same<T, object>::value ||
std::is_same<T, string>::value ||
std::is_same<T, bool>::value ||
std::is_same<T, std::nullptr_t>::value ||
std::is_same<T, signed char>::value ||
std::is_same<T, short>::value ||
std::is_same<T, int>::value ||
std::is_same<T, long>::value ||
std::is_same<T, long long>::value ||
std::is_same<T, unsigned char>::value ||
std::is_same<T, unsigned short>::value ||
std::is_same<T, unsigned int>::value ||
std::is_same<T, unsigned long>::value ||
std::is_same<T, unsigned long long>::value ||
std::is_same<T, float>::value ||
std::is_same<T, double>::value ||
std::is_same<T, string_view>::value ||
std::is_same<T, char const*>::value
> {};

} // namespace detail

//----------------------------------------------------------

/** The type used in initializer lists.
Expand Down
95 changes: 95 additions & 0 deletions test/value_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,37 @@

#include <boost/json/value.hpp>
#include <boost/json/serialize.hpp>
#include <boost/json/value_from.hpp>

#include "test_suite.hpp"

#include <vector>

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
Expand Down Expand Up @@ -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<test_udt> 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()
{
Expand All @@ -397,6 +491,7 @@ class value_ref_test
testMakeValue();
testObjects();
testMoveFrom();
testUDT();
}
};

Expand Down
Loading