Skip to content

Commit a048b72

Browse files
authored
Merge pull request #1559 from theodelrieu/feat/explicit_conversion_operator
Feat/explicit conversion operator
2 parents 484029b + 7973293 commit a048b72

17 files changed

Lines changed: 191 additions & 66 deletions

.travis.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ matrix:
145145
- os: osx
146146
osx_image: xcode12
147147

148+
- os: osx
149+
osx_image: xcode12
150+
env:
151+
- IMPLICIT_CONVERSIONS=OFF
152+
148153
# Linux / GCC
149154

150155
- os: linux
@@ -203,6 +208,16 @@ matrix:
203208
sources: ['ubuntu-toolchain-r-test']
204209
packages: ['g++-9', 'ninja-build']
205210

211+
- os: linux
212+
compiler: gcc
213+
env:
214+
- COMPILER=g++-9
215+
- IMPLICIT_CONVERSIONS=OFF
216+
addons:
217+
apt:
218+
sources: ['ubuntu-toolchain-r-test']
219+
packages: ['g++-9', 'ninja-build']
220+
206221
- os: linux
207222
compiler: gcc
208223
env:
@@ -315,10 +330,12 @@ script:
315330
- if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi
316331
# by default, use the single-header version
317332
- if [[ "${MULTIPLE_HEADERS}" == "" ]]; then export MULTIPLE_HEADERS=OFF; fi
333+
# by default, use implicit conversions
334+
- if [[ "${IMPLICIT_CONVERSIONS}" == "" ]]; then export IMPLICIT_CONVERSIONS=ON; fi
318335

319336
# compile and execute unit tests
320337
- mkdir -p build && cd build
321-
- cmake .. ${CMAKE_OPTIONS} -DJSON_MultipleHeaders=${MULTIPLE_HEADERS} -DJSON_BuildTests=On -GNinja && cmake --build . --config Release
338+
- cmake .. ${CMAKE_OPTIONS} -DJSON_MultipleHeaders=${MULTIPLE_HEADERS} -DJSON_ImplicitConversions=${IMPLICIT_CONVERSIONS} -DJSON_BuildTests=On -GNinja && cmake --build . --config Release
322339
- ctest -C Release --timeout 2700 -V -j
323340
- cd ..
324341

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

appveyor.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,31 @@ environment:
77
platform: x86
88
CXX_FLAGS: ""
99
LINKER_FLAGS: ""
10+
CMAKE_OPTIONS: ""
1011
GENERATOR: Visual Studio 14 2015
1112

1213
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
1314
configuration: Debug
1415
platform: x86
1516
CXX_FLAGS: ""
1617
LINKER_FLAGS: ""
18+
CMAKE_OPTIONS: ""
1719
GENERATOR: Visual Studio 15 2017
1820

1921
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
2022
configuration: Debug
2123
platform: x86
2224
CXX_FLAGS: ""
2325
LINKER_FLAGS: ""
26+
CMAKE_OPTIONS: ""
2427
GENERATOR: Visual Studio 16 2019
2528

2629
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
2730
configuration: Debug
2831
platform: x64
2932
CXX_FLAGS: ""
3033
LINKER_FLAGS: ""
34+
CMAKE_OPTIONS: ""
3135
GENERATOR: Visual Studio 16 2019
3236

3337
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
@@ -36,6 +40,7 @@ environment:
3640
platform: x86
3741
CXX_FLAGS: ""
3842
LINKER_FLAGS: ""
43+
CMAKE_OPTIONS: ""
3944
GENERATOR: Ninja
4045

4146
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
@@ -44,13 +49,15 @@ environment:
4449
platform: x86
4550
CXX_FLAGS: ""
4651
LINKER_FLAGS: ""
52+
CMAKE_OPTIONS: ""
4753
GENERATOR: Ninja
4854

4955
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
5056
configuration: Release
5157
platform: x86
5258
CXX_FLAGS: ""
5359
LINKER_FLAGS: ""
60+
CMAKE_OPTIONS: ""
5461
GENERATOR: Visual Studio 14 2015
5562

5663
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
@@ -59,41 +66,55 @@ environment:
5966
name: with_win_header
6067
CXX_FLAGS: ""
6168
LINKER_FLAGS: ""
69+
CMAKE_OPTIONS: ""
6270
GENERATOR: Visual Studio 14 2015
6371

6472
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
6573
configuration: Release
6674
platform: x86
6775
CXX_FLAGS: "/permissive- /std:c++latest /utf-8"
6876
LINKER_FLAGS: ""
77+
CMAKE_OPTIONS: ""
6978
GENERATOR: Visual Studio 15 2017
7079

7180
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
7281
configuration: Release
7382
platform: x86
7483
CXX_FLAGS: ""
7584
LINKER_FLAGS: ""
85+
CMAKE_OPTIONS: "-DJSON_ImplicitConversions=OFF"
86+
GENERATOR: Visual Studio 16 2019
87+
88+
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
89+
configuration: Release
90+
platform: x64
91+
CXX_FLAGS: ""
92+
LINKER_FLAGS: ""
93+
CMAKE_OPTIONS: ""
7694
GENERATOR: Visual Studio 16 2019
7795

7896
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
7997
configuration: Release
8098
platform: x64
8199
CXX_FLAGS: ""
82100
LINKER_FLAGS: ""
101+
CMAKE_OPTIONS: ""
83102
GENERATOR: Visual Studio 14 2015
84103

85104
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
86105
configuration: Release
87106
platform: x64
88107
CXX_FLAGS: "/permissive- /std:c++latest /Zc:__cplusplus /utf-8 /F4000000"
89108
LINKER_FLAGS: "/STACK:4000000"
109+
CMAKE_OPTIONS: ""
90110
GENERATOR: Visual Studio 15 2017
91111

92112
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
93113
configuration: Release
94114
platform: x64
95115
CXX_FLAGS: ""
96116
LINKER_FLAGS: ""
117+
CMAKE_OPTIONS: ""
97118
GENERATOR: Visual Studio 16 2019
98119

99120
init:
@@ -112,7 +133,7 @@ before_build:
112133
# for with_win_header build, inject the inclusion of Windows.h to the single-header library
113134
- ps: if ($env:name -Eq "with_win_header") { $header_path = "single_include\nlohmann\json.hpp" }
114135
- ps: if ($env:name -Eq "with_win_header") { "#include <Windows.h>`n" + (Get-Content $header_path | Out-String) | Set-Content $header_path }
115-
- if "%GENERATOR%"=="Ninja" (cmake . -G "%GENERATOR%" -DCMAKE_BUILD_TYPE="%configuration%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_EXE_LINKER_FLAGS="%LINKER_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On) else (cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_EXE_LINKER_FLAGS="%LINKER_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On)
136+
- if "%GENERATOR%"=="Ninja" (cmake . -G "%GENERATOR%" -DCMAKE_BUILD_TYPE="%configuration%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_EXE_LINKER_FLAGS="%LINKER_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%") else (cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_EXE_LINKER_FLAGS="%LINKER_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%")
116137

117138
build_script:
118139
- cmake --build . --config "%configuration%"

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()

0 commit comments

Comments
 (0)