Skip to content

Commit

Permalink
fix inconsistent choice of init list constructor
Browse files Browse the repository at this point in the history
On some compilers value{x} invokes initializer_list constructor, on
others it is equivalent to value(x). This is very problematic, but this
isn't something we can fix. On the other hand, we CAN make init list
construction to be equivalent to value(x), if the size of init list is
one. This commit does exactly that.
  • Loading branch information
grisumbras committed Aug 25, 2023
1 parent 2acdb29 commit 25e740d
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 8 deletions.
21 changes: 18 additions & 3 deletions include/boost/json/impl/value.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,28 @@ value(
storage_ptr sp)
{
if(value_ref::maybe_object(init))
{
::new(&obj_) object(
value_ref::make_object(
init, std::move(sp)));
}
else
::new(&arr_) array(
value_ref::make_array(
init, std::move(sp)));
{
#ifndef BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR
if( init.size() == 1 )
{
::new(&sca_) scalar();
value temp = init.begin()->make_value( std::move(sp) );
swap(temp);
}
else
#endif
{
::new(&arr_) array(
value_ref::make_array(
init, std::move(sp)));
}
}
}

//----------------------------------------------------------
Expand Down
29 changes: 24 additions & 5 deletions include/boost/json/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1014,11 +1014,16 @@ class value

/** Construct from an initializer-list
If the initializer list consists of key/value
pairs, an @ref object is created. Otherwise
an @ref array is created. The contents of the
initializer list are copied to the newly constructed
value using the specified memory resource.
@li If the initializer list consists of key/value
pairs, an @ref object is created; otherwise,
@li if the size of the initializer list is exactly 1, the object is
constructed directly from that sole element; otherwise,
@li an @ref array is created.
The contents of the initializer list are copied to the newly
constructed value using the specified memory resource.
@par Complexity
Linear in `init.size()`.
Expand All @@ -1032,6 +1037,20 @@ class value
@param sp A pointer to the @ref memory_resource
to use. The container will acquire shared
ownership of the memory resource.
@par Note
The previous behavior of this constructor was to always
construct either an @ref object or an @ref array. In practice though,
several C++ implementations did not treat `value{x}` as a constructor
from initializer list. This effectively resulted in different behavior
on different implementations. <br>
If you need the legacy behavior define macro
`BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR` when you are building the
library. The macro and the functionality will be deprecated in the
future and then removed, so we urge you to change your code for the new
behavior as soon as possible. The simplest way to create an @ref array
with 1 element using an initializer list is via `array{x}`.
*/
BOOST_JSON_DECL
value(
Expand Down
11 changes: 11 additions & 0 deletions test/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2131,6 +2131,17 @@ class value_test
void
testInitList()
{
{
value jv{};
BOOST_TEST( jv.is_null() );
}
#ifndef BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR
{
value jv{0};
BOOST_TEST( jv == 0 );
}
#endif

check_array(value{0,0,0}, 0, 0, 0);
check_array(value{false,false,false}, false, false, false);
check_array(value{false,2,false}, false, 2, false);
Expand Down

0 comments on commit 25e740d

Please sign in to comment.