Skip to content

Commit 74b446f

Browse files
committed
add a switch to enable implicit conversions (defaults to true)
wrap implicit conversions tests around the JSON_USE_IMPLICIT_CONVERSIONS macro
1 parent 2cd10a7 commit 74b446f

File tree

15 files changed

+151
-64
lines changed

15 files changed

+151
-64
lines changed

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ endif ()
2424
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON)
2525
option(JSON_Install "Install CMake targets during install step." ON)
2626
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
27+
option(JSON_ImplicitConversions "Enable implicit conversions" ON)
2728

2829
##
2930
## CONFIGURATION
@@ -60,6 +61,12 @@ else()
6061
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)
6162
endif()
6263

64+
target_compile_definitions(
65+
${NLOHMANN_JSON_TARGET_NAME}
66+
INTERFACE
67+
JSON_USE_IMPLICIT_CONVERSIONS=$<BOOL:${JSON_ImplicitConversions}>
68+
)
69+
6370
target_include_directories(
6471
${NLOHMANN_JSON_TARGET_NAME}
6572
INTERFACE

include/nlohmann/detail/conversions/from_json.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ void from_json(const BasicJsonType& j, EnumType& e)
128128

129129
// forward_list doesn't have an insert method
130130
template<typename BasicJsonType, typename T, typename Allocator,
131-
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
131+
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
132132
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
133133
{
134134
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -145,15 +145,19 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
145145

146146
// valarray doesn't have an insert method
147147
template<typename BasicJsonType, typename T,
148-
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
148+
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
149149
void from_json(const BasicJsonType& j, std::valarray<T>& l)
150150
{
151151
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
152152
{
153153
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
154154
}
155155
l.resize(j.size());
156-
std::copy(j.begin(), j.end(), std::begin(l));
156+
std::transform(j.begin(), j.end(), std::begin(l),
157+
[](const BasicJsonType & elem)
158+
{
159+
return elem.template get<T>();
160+
});
157161
}
158162

159163
template<typename BasicJsonType, typename T, std::size_t N>

include/nlohmann/detail/macro_scope.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,13 @@
284284
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
285285
void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
286286
void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
287+
288+
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
289+
#define JSON_USE_IMPLICIT_CONVERSIONS 1
290+
#endif
291+
292+
#if JSON_USE_IMPLICIT_CONVERSIONS
293+
#define JSON_EXPLICIT
294+
#else
295+
#define JSON_EXPLICIT explicit
296+
#endif

include/nlohmann/detail/macro_unscope.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818
#undef JSON_HAS_CPP_17
1919
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
2020
#undef NLOHMANN_BASIC_JSON_TPL
21+
#undef JSON_EXPLICIT
2122

2223
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

include/nlohmann/detail/meta/type_traits.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ using get_template_function = decltype(std::declval<T>().template get<U>());
9494
template<typename BasicJsonType, typename T, typename = void>
9595
struct has_from_json : std::false_type {};
9696

97+
// trait checking if j.get<T> is valid
98+
// use this trait instead of std::is_constructible or std::is_convertible,
99+
// both rely on, or make use of implicit conversions, and thus fail when T
100+
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
101+
template <typename BasicJsonType, typename T>
102+
struct is_getable
103+
{
104+
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
105+
};
106+
97107
template<typename BasicJsonType, typename T>
98108
struct has_from_json < BasicJsonType, T,
99109
enable_if_t < !is_basic_json<T>::value >>

include/nlohmann/json.hpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,7 +3229,7 @@ class basic_json
32293229
#endif
32303230
&& detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
32313231
, int >::type = 0 >
3232-
operator ValueType() const
3232+
JSON_EXPLICIT operator ValueType() const
32333233
{
32343234
// delegate the call to get<>() const
32353235
return get<ValueType>();
@@ -3784,8 +3784,9 @@ class basic_json
37843784
37853785
@since version 1.0.0
37863786
*/
3787+
// using std::is_convertible in a std::enable_if will fail when using explicit conversions
37873788
template < class ValueType, typename std::enable_if <
3788-
std::is_convertible<basic_json_t, ValueType>::value
3789+
detail::is_getable<basic_json_t, ValueType>::value
37893790
&& !std::is_same<value_t, ValueType>::value, int >::type = 0 >
37903791
ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
37913792
{
@@ -3796,7 +3797,7 @@ class basic_json
37963797
const auto it = find(key);
37973798
if (it != end())
37983799
{
3799-
return *it;
3800+
return it->template get<ValueType>();
38003801
}
38013802

38023803
return default_value;
@@ -3858,7 +3859,7 @@ class basic_json
38583859
@since version 2.0.2
38593860
*/
38603861
template<class ValueType, typename std::enable_if<
3861-
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
3862+
detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
38623863
ValueType value(const json_pointer& ptr, const ValueType& default_value) const
38633864
{
38643865
// at only works for objects
@@ -3867,7 +3868,7 @@ class basic_json
38673868
// if pointer resolves a value, return it or use default value
38683869
JSON_TRY
38693870
{
3870-
return ptr.get_checked(this);
3871+
return ptr.get_checked(this).template get<ValueType>();
38713872
}
38723873
JSON_INTERNAL_CATCH (out_of_range&)
38733874
{
@@ -8341,8 +8342,8 @@ class basic_json
83418342
}
83428343

83438344
// collect mandatory members
8344-
const std::string op = get_value("op", "op", true);
8345-
const std::string path = get_value(op, "path", true);
8345+
const auto op = get_value("op", "op", true).template get<std::string>();
8346+
const auto path = get_value(op, "path", true).template get<std::string>();
83468347
json_pointer ptr(path);
83478348

83488349
switch (get_op(op))
@@ -8368,7 +8369,7 @@ class basic_json
83688369

83698370
case patch_operations::move:
83708371
{
8371-
const std::string from_path = get_value("move", "from", true);
8372+
const auto from_path = get_value("move", "from", true).template get<std::string>();
83728373
json_pointer from_ptr(from_path);
83738374

83748375
// the "from" location must exist - use at()
@@ -8385,7 +8386,7 @@ class basic_json
83858386

83868387
case patch_operations::copy:
83878388
{
8388-
const std::string from_path = get_value("copy", "from", true);
8389+
const auto from_path = get_value("copy", "from", true).template get<std::string>();
83898390
const json_pointer from_ptr(from_path);
83908391

83918392
// the "from" location must exist - use at()

single_include/nlohmann/json.hpp

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,6 +2302,16 @@ JSON_HEDLEY_DIAGNOSTIC_POP
23022302
void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
23032303
void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
23042304

2305+
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
2306+
#define JSON_USE_IMPLICIT_CONVERSIONS 1
2307+
#endif
2308+
2309+
#if JSON_USE_IMPLICIT_CONVERSIONS
2310+
#define JSON_EXPLICIT
2311+
#else
2312+
#define JSON_EXPLICIT explicit
2313+
#endif
2314+
23052315

23062316
namespace nlohmann
23072317
{
@@ -3022,6 +3032,16 @@ using get_template_function = decltype(std::declval<T>().template get<U>());
30223032
template<typename BasicJsonType, typename T, typename = void>
30233033
struct has_from_json : std::false_type {};
30243034

3035+
// trait checking if j.get<T> is valid
3036+
// use this trait instead of std::is_constructible or std::is_convertible,
3037+
// both rely on, or make use of implicit conversions, and thus fail when T
3038+
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
3039+
template <typename BasicJsonType, typename T>
3040+
struct is_getable
3041+
{
3042+
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
3043+
};
3044+
30253045
template<typename BasicJsonType, typename T>
30263046
struct has_from_json < BasicJsonType, T,
30273047
enable_if_t < !is_basic_json<T>::value >>
@@ -3507,7 +3527,7 @@ void from_json(const BasicJsonType& j, EnumType& e)
35073527

35083528
// forward_list doesn't have an insert method
35093529
template<typename BasicJsonType, typename T, typename Allocator,
3510-
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
3530+
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
35113531
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
35123532
{
35133533
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -3524,15 +3544,19 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
35243544

35253545
// valarray doesn't have an insert method
35263546
template<typename BasicJsonType, typename T,
3527-
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
3547+
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
35283548
void from_json(const BasicJsonType& j, std::valarray<T>& l)
35293549
{
35303550
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
35313551
{
35323552
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
35333553
}
35343554
l.resize(j.size());
3535-
std::copy(j.begin(), j.end(), std::begin(l));
3555+
std::transform(j.begin(), j.end(), std::begin(l),
3556+
[](const BasicJsonType & elem)
3557+
{
3558+
return elem.template get<T>();
3559+
});
35363560
}
35373561

35383562
template<typename BasicJsonType, typename T, std::size_t N>
@@ -19508,7 +19532,7 @@ class basic_json
1950819532
#endif
1950919533
&& detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
1951019534
, int >::type = 0 >
19511-
operator ValueType() const
19535+
JSON_EXPLICIT operator ValueType() const
1951219536
{
1951319537
// delegate the call to get<>() const
1951419538
return get<ValueType>();
@@ -20063,8 +20087,9 @@ class basic_json
2006320087

2006420088
@since version 1.0.0
2006520089
*/
20090+
// using std::is_convertible in a std::enable_if will fail when using explicit conversions
2006620091
template < class ValueType, typename std::enable_if <
20067-
std::is_convertible<basic_json_t, ValueType>::value
20092+
detail::is_getable<basic_json_t, ValueType>::value
2006820093
&& !std::is_same<value_t, ValueType>::value, int >::type = 0 >
2006920094
ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
2007020095
{
@@ -20075,7 +20100,7 @@ class basic_json
2007520100
const auto it = find(key);
2007620101
if (it != end())
2007720102
{
20078-
return *it;
20103+
return it->template get<ValueType>();
2007920104
}
2008020105

2008120106
return default_value;
@@ -20137,7 +20162,7 @@ class basic_json
2013720162
@since version 2.0.2
2013820163
*/
2013920164
template<class ValueType, typename std::enable_if<
20140-
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
20165+
detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
2014120166
ValueType value(const json_pointer& ptr, const ValueType& default_value) const
2014220167
{
2014320168
// at only works for objects
@@ -20146,7 +20171,7 @@ class basic_json
2014620171
// if pointer resolves a value, return it or use default value
2014720172
JSON_TRY
2014820173
{
20149-
return ptr.get_checked(this);
20174+
return ptr.get_checked(this).template get<ValueType>();
2015020175
}
2015120176
JSON_INTERNAL_CATCH (out_of_range&)
2015220177
{
@@ -24620,8 +24645,8 @@ class basic_json
2462024645
}
2462124646

2462224647
// collect mandatory members
24623-
const std::string op = get_value("op", "op", true);
24624-
const std::string path = get_value(op, "path", true);
24648+
const auto op = get_value("op", "op", true).template get<std::string>();
24649+
const auto path = get_value(op, "path", true).template get<std::string>();
2462524650
json_pointer ptr(path);
2462624651

2462724652
switch (get_op(op))
@@ -24647,7 +24672,7 @@ class basic_json
2464724672

2464824673
case patch_operations::move:
2464924674
{
24650-
const std::string from_path = get_value("move", "from", true);
24675+
const auto from_path = get_value("move", "from", true).template get<std::string>();
2465124676
json_pointer from_ptr(from_path);
2465224677

2465324678
// the "from" location must exist - use at()
@@ -24664,7 +24689,7 @@ class basic_json
2466424689

2466524690
case patch_operations::copy:
2466624691
{
24667-
const std::string from_path = get_value("copy", "from", true);
24692+
const auto from_path = get_value("copy", "from", true).template get<std::string>();
2466824693
const json_pointer from_ptr(from_path);
2466924694

2467024695
// the "from" location must exist - use at()
@@ -25086,6 +25111,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
2508625111
#undef JSON_HAS_CPP_17
2508725112
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
2508825113
#undef NLOHMANN_BASIC_JSON_TPL
25114+
#undef JSON_EXPLICIT
2508925115

2509025116
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
2509125117
#undef JSON_HEDLEY_ALWAYS_INLINE

0 commit comments

Comments
 (0)