Skip to content

Commit

Permalink
Merge pull request #1559 from theodelrieu/feat/explicit_conversion_op…
Browse files Browse the repository at this point in the history
…erator

Feat/explicit conversion operator
  • Loading branch information
nlohmann committed Jul 23, 2020
2 parents 484029b + 7973293 commit a048b72
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 66 deletions.
19 changes: 18 additions & 1 deletion .travis.yml
Expand Up @@ -145,6 +145,11 @@ matrix:
- os: osx
osx_image: xcode12

- os: osx
osx_image: xcode12
env:
- IMPLICIT_CONVERSIONS=OFF

# Linux / GCC

- os: linux
Expand Down Expand Up @@ -203,6 +208,16 @@ matrix:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-9', 'ninja-build']

- os: linux
compiler: gcc
env:
- COMPILER=g++-9
- IMPLICIT_CONVERSIONS=OFF
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-9', 'ninja-build']

- os: linux
compiler: gcc
env:
Expand Down Expand Up @@ -315,10 +330,12 @@ script:
- if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi
# by default, use the single-header version
- if [[ "${MULTIPLE_HEADERS}" == "" ]]; then export MULTIPLE_HEADERS=OFF; fi
# by default, use implicit conversions
- if [[ "${IMPLICIT_CONVERSIONS}" == "" ]]; then export IMPLICIT_CONVERSIONS=ON; fi

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

Expand Down
7 changes: 7 additions & 0 deletions CMakeLists.txt
Expand Up @@ -24,6 +24,7 @@ endif ()
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON)
option(JSON_Install "Install CMake targets during install step." ON)
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
option(JSON_ImplicitConversions "Enable implicit conversions" ON)

##
## CONFIGURATION
Expand Down Expand Up @@ -60,6 +61,12 @@ else()
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)
endif()

target_compile_definitions(
${NLOHMANN_JSON_TARGET_NAME}
INTERFACE
JSON_USE_IMPLICIT_CONVERSIONS=$<BOOL:${JSON_ImplicitConversions}>
)

target_include_directories(
${NLOHMANN_JSON_TARGET_NAME}
INTERFACE
Expand Down
23 changes: 22 additions & 1 deletion appveyor.yml
Expand Up @@ -7,27 +7,31 @@ environment:
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 14 2015

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
configuration: Debug
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 15 2017

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
configuration: Debug
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 16 2019

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
configuration: Debug
platform: x64
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 16 2019

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
Expand All @@ -36,6 +40,7 @@ environment:
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Ninja

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
Expand All @@ -44,13 +49,15 @@ environment:
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Ninja

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
configuration: Release
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 14 2015

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
Expand All @@ -59,41 +66,55 @@ environment:
name: with_win_header
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 14 2015

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
configuration: Release
platform: x86
CXX_FLAGS: "/permissive- /std:c++latest /utf-8"
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 15 2017

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
configuration: Release
platform: x86
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: "-DJSON_ImplicitConversions=OFF"
GENERATOR: Visual Studio 16 2019

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
configuration: Release
platform: x64
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 16 2019

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
configuration: Release
platform: x64
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 14 2015

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
configuration: Release
platform: x64
CXX_FLAGS: "/permissive- /std:c++latest /Zc:__cplusplus /utf-8 /F4000000"
LINKER_FLAGS: "/STACK:4000000"
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 15 2017

- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
configuration: Release
platform: x64
CXX_FLAGS: ""
LINKER_FLAGS: ""
CMAKE_OPTIONS: ""
GENERATOR: Visual Studio 16 2019

init:
Expand All @@ -112,7 +133,7 @@ before_build:
# for with_win_header build, inject the inclusion of Windows.h to the single-header library
- ps: if ($env:name -Eq "with_win_header") { $header_path = "single_include\nlohmann\json.hpp" }
- ps: if ($env:name -Eq "with_win_header") { "#include <Windows.h>`n" + (Get-Content $header_path | Out-String) | Set-Content $header_path }
- 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)
- 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%")

build_script:
- cmake --build . --config "%configuration%"
Expand Down
10 changes: 7 additions & 3 deletions include/nlohmann/detail/conversions/from_json.hpp
Expand Up @@ -128,7 +128,7 @@ void from_json(const BasicJsonType& j, EnumType& e)

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

// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
l.resize(j.size());
std::copy(j.begin(), j.end(), std::begin(l));
std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
{
return elem.template get<T>();
});
}

template<typename BasicJsonType, typename T, std::size_t N>
Expand Down
10 changes: 10 additions & 0 deletions include/nlohmann/detail/macro_scope.hpp
Expand Up @@ -284,3 +284,13 @@
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }

#ifndef JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_USE_IMPLICIT_CONVERSIONS 1
#endif

#if JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_EXPLICIT
#else
#define JSON_EXPLICIT explicit
#endif
1 change: 1 addition & 0 deletions include/nlohmann/detail/macro_unscope.hpp
Expand Up @@ -18,5 +18,6 @@
#undef JSON_HAS_CPP_17
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT

#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
10 changes: 10 additions & 0 deletions include/nlohmann/detail/meta/type_traits.hpp
Expand Up @@ -94,6 +94,16 @@ using get_template_function = decltype(std::declval<T>().template get<U>());
template<typename BasicJsonType, typename T, typename = void>
struct has_from_json : std::false_type {};

// trait checking if j.get<T> is valid
// use this trait instead of std::is_constructible or std::is_convertible,
// both rely on, or make use of implicit conversions, and thus fail when T
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
template <typename BasicJsonType, typename T>
struct is_getable
{
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
};

template<typename BasicJsonType, typename T>
struct has_from_json < BasicJsonType, T,
enable_if_t < !is_basic_json<T>::value >>
Expand Down
19 changes: 10 additions & 9 deletions include/nlohmann/json.hpp
Expand Up @@ -3229,7 +3229,7 @@ class basic_json
#endif
&& detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 >
operator ValueType() const
JSON_EXPLICIT operator ValueType() const
{
// delegate the call to get<>() const
return get<ValueType>();
Expand Down Expand Up @@ -3784,8 +3784,9 @@ class basic_json
@since version 1.0.0
*/
// using std::is_convertible in a std::enable_if will fail when using explicit conversions
template < class ValueType, typename std::enable_if <
std::is_convertible<basic_json_t, ValueType>::value
detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int >::type = 0 >
ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{
Expand All @@ -3796,7 +3797,7 @@ class basic_json
const auto it = find(key);
if (it != end())
{
return *it;
return it->template get<ValueType>();
}

return default_value;
Expand Down Expand Up @@ -3858,7 +3859,7 @@ class basic_json
@since version 2.0.2
*/
template<class ValueType, typename std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{
// at only works for objects
Expand All @@ -3867,7 +3868,7 @@ class basic_json
// if pointer resolves a value, return it or use default value
JSON_TRY
{
return ptr.get_checked(this);
return ptr.get_checked(this).template get<ValueType>();
}
JSON_INTERNAL_CATCH (out_of_range&)
{
Expand Down Expand Up @@ -8341,8 +8342,8 @@ class basic_json
}

// collect mandatory members
const std::string op = get_value("op", "op", true);
const std::string path = get_value(op, "path", true);
const auto op = get_value("op", "op", true).template get<std::string>();
const auto path = get_value(op, "path", true).template get<std::string>();
json_pointer ptr(path);

switch (get_op(op))
Expand All @@ -8368,7 +8369,7 @@ class basic_json

case patch_operations::move:
{
const std::string from_path = get_value("move", "from", true);
const auto from_path = get_value("move", "from", true).template get<std::string>();
json_pointer from_ptr(from_path);

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

case patch_operations::copy:
{
const std::string from_path = get_value("copy", "from", true);
const auto from_path = get_value("copy", "from", true).template get<std::string>();
const json_pointer from_ptr(from_path);

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

0 comments on commit a048b72

Please sign in to comment.