Skip to content

Commit 261cc4e

Browse files
Fix constraints on from_json() for strings (#3427)
Constrain from_json() overload for StringType to not accept json_ref and require it to be assignable, instead of constructible, from basic_json::string_t. Re-enable C++14 tests on Clang <4.0. Fixes #3171. Fixes #3267. Fixes #3312. Fixes #3384.
1 parent 15fa6a3 commit 261cc4e

File tree

4 files changed

+79
-17
lines changed

4 files changed

+79
-17
lines changed

include/nlohmann/detail/conversions/from_json.hpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,12 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
105105
}
106106

107107
template <
108-
typename BasicJsonType, typename ConstructibleStringType,
108+
typename BasicJsonType, typename StringType,
109109
enable_if_t <
110-
is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&
111-
!std::is_same<typename BasicJsonType::string_t,
112-
ConstructibleStringType>::value,
113-
int > = 0 >
114-
void from_json(const BasicJsonType& j, ConstructibleStringType& s)
110+
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
111+
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value
112+
&& !is_json_ref<StringType>::value, int > = 0 >
113+
void from_json(const BasicJsonType& j, StringType& s)
115114
{
116115
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
117116
{

single_include/nlohmann/json.hpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -3928,13 +3928,12 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
39283928
}
39293929

39303930
template <
3931-
typename BasicJsonType, typename ConstructibleStringType,
3931+
typename BasicJsonType, typename StringType,
39323932
enable_if_t <
3933-
is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&
3934-
!std::is_same<typename BasicJsonType::string_t,
3935-
ConstructibleStringType>::value,
3936-
int > = 0 >
3937-
void from_json(const BasicJsonType& j, ConstructibleStringType& s)
3933+
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
3934+
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value
3935+
&& !is_json_ref<StringType>::value, int > = 0 >
3936+
void from_json(const BasicJsonType& j, StringType& s)
39383937
{
39393938
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
39403939
{

test/CMakeLists.txt

-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ include(test)
1111
# override standard support
1212
#############################################################################
1313

14-
# compiling json.hpp in C++14 mode fails with Clang <4.0
15-
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0)
16-
unset(compiler_supports_cpp_14)
17-
endif()
18-
1914
# Clang only supports C++17 starting from Clang 5.0
2015
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
2116
unset(compiler_supports_cpp_17)

test/src/unit-regression2.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,50 @@ inline void from_json(const nlohmann::json& j, FooBar& fb)
240240
j.at("value").get_to(fb.foo.value);
241241
}
242242

243+
/////////////////////////////////////////////////////////////////////
244+
// for #3171
245+
/////////////////////////////////////////////////////////////////////
246+
247+
struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions)
248+
{
249+
for_3171_base(const std::string& /*unused*/ = {}) {}
250+
virtual ~for_3171_base() = default;
251+
252+
virtual void _from_json(const json& j)
253+
{
254+
j.at("str").get_to(str);
255+
}
256+
257+
std::string str{};
258+
};
259+
260+
struct for_3171_derived : public for_3171_base
261+
{
262+
for_3171_derived() = default;
263+
explicit for_3171_derived(const std::string& /*unused*/) { }
264+
};
265+
266+
inline void from_json(const json& j, for_3171_base& tb)
267+
{
268+
tb._from_json(j);
269+
}
270+
271+
/////////////////////////////////////////////////////////////////////
272+
// for #3312
273+
/////////////////////////////////////////////////////////////////////
274+
275+
#ifdef JSON_HAS_CPP_20
276+
struct for_3312
277+
{
278+
std::string name;
279+
};
280+
281+
inline void from_json(const json& j, for_3312& obj)
282+
{
283+
j.at("name").get_to(obj.name);
284+
}
285+
#endif
286+
243287
TEST_CASE("regression tests 2")
244288
{
245289
SECTION("issue #1001 - Fix memory leak during parser callback")
@@ -791,6 +835,31 @@ TEST_CASE("regression tests 2")
791835
CHECK(jit->first == ojit->first);
792836
CHECK(jit->second.get<std::string>() == ojit->second.get<std::string>());
793837
}
838+
839+
SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed")
840+
{
841+
json j{{ "str", "value"}};
842+
843+
// failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’
844+
// {aka ‘const std::__cxx11::basic_string<char>’})
845+
// s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
846+
auto td = j.get<for_3171_derived>();
847+
848+
CHECK(td.str == "value");
849+
}
850+
851+
#ifdef JSON_HAS_CPP_20
852+
SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20")
853+
{
854+
// see test for #3171
855+
ordered_json j = {{"name", "class"}};
856+
for_3312 obj{};
857+
858+
j.get_to(obj);
859+
860+
CHECK(obj.name == "class");
861+
}
862+
#endif
794863
}
795864

796865
DOCTEST_CLANG_SUPPRESS_WARNING_POP

0 commit comments

Comments
 (0)