Skip to content

Commit 11aa5f9

Browse files
risa2000Richard Musil
andauthored
Make std::filesystem::path conversion to/from UTF-8 encoded string explicit (#4631)
* Make std::filesystem::path conversion to/from UTF-8 encoded JSON string explicit. Signed-off-by: Richard Musil <[email protected]> * Experimental: Changing C++ standard detection logic to accommodate potential corner cases. Signed-off-by: Richard Musil <[email protected]> * Drop C++ standard tests for compilers which do not implement required features. Signed-off-by: Richard Musil <[email protected]> * Drop C++ standard tests for MSVC versions which do not implement required features. Signed-off-by: Richard Musil <[email protected]> --------- Signed-off-by: Richard Musil <[email protected]> Co-authored-by: Richard Musil <[email protected]>
1 parent 79587f8 commit 11aa5f9

File tree

8 files changed

+85
-15
lines changed

8 files changed

+85
-15
lines changed

.github/external_ci/appveyor.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ environment:
4141
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
4242
configuration: Release
4343
platform: x86
44-
CXX_FLAGS: "/permissive- /std:c++latest /utf-8 /W4 /WX"
44+
CXX_FLAGS: "/permissive- /std:c++17 /utf-8 /W4 /WX"
4545
CMAKE_OPTIONS: ""
4646
GENERATOR: Visual Studio 15 2017
4747

@@ -62,7 +62,7 @@ environment:
6262
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
6363
configuration: Release
6464
platform: x64
65-
CXX_FLAGS: "/permissive- /std:c++latest /Zc:__cplusplus /utf-8 /W4 /WX"
65+
CXX_FLAGS: "/permissive- /std:c++17 /Zc:__cplusplus /utf-8 /W4 /WX"
6666
CMAKE_OPTIONS: ""
6767
GENERATOR: Visual Studio 15 2017
6868

include/nlohmann/detail/conversions/from_json.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,12 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
539539
{
540540
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
541541
}
542-
p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
542+
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
543+
#ifdef JSON_HAS_CPP_20
544+
p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));
545+
#else
546+
p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20
547+
#endif
543548
}
544549
#endif
545550

include/nlohmann/detail/conversions/to_json.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,12 @@ inline void to_json(BasicJsonType& j, const T& t)
443443
template<typename BasicJsonType>
444444
inline void to_json(BasicJsonType& j, const std_fs::path& p)
445445
{
446-
j = p.string();
446+
#ifdef JSON_HAS_CPP_20
447+
const std::u8string s = p.u8string();
448+
j = std::string(s.begin(), s.end());
449+
#else
450+
j = p.u8string(); // returns std::string in C++17
451+
#endif
447452
}
448453
#endif
449454

include/nlohmann/detail/macro_scope.hpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,20 @@
3232

3333
// C++ language standard detection
3434
// if the user manually specified the used c++ version this is skipped
35-
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
36-
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
35+
#if !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
36+
#if (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
37+
#define JSON_HAS_CPP_23
3738
#define JSON_HAS_CPP_20
3839
#define JSON_HAS_CPP_17
3940
#define JSON_HAS_CPP_14
40-
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
41+
#elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
42+
#define JSON_HAS_CPP_20
43+
#define JSON_HAS_CPP_17
44+
#define JSON_HAS_CPP_14
45+
#elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
4146
#define JSON_HAS_CPP_17
4247
#define JSON_HAS_CPP_14
43-
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
48+
#elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
4449
#define JSON_HAS_CPP_14
4550
#endif
4651
// the cpp 11 flag is always specified because it is the minimal required version

include/nlohmann/detail/macro_unscope.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#undef JSON_HAS_CPP_14
3535
#undef JSON_HAS_CPP_17
3636
#undef JSON_HAS_CPP_20
37+
#undef JSON_HAS_CPP_23
3738
#undef JSON_HAS_FILESYSTEM
3839
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
3940
#undef JSON_HAS_THREE_WAY_COMPARISON

single_include/nlohmann/json.hpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,15 +2398,20 @@ JSON_HEDLEY_DIAGNOSTIC_POP
23982398

23992399
// C++ language standard detection
24002400
// if the user manually specified the used c++ version this is skipped
2401-
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
2402-
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
2401+
#if !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
2402+
#if (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
2403+
#define JSON_HAS_CPP_23
24032404
#define JSON_HAS_CPP_20
24042405
#define JSON_HAS_CPP_17
24052406
#define JSON_HAS_CPP_14
2406-
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
2407+
#elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
2408+
#define JSON_HAS_CPP_20
2409+
#define JSON_HAS_CPP_17
2410+
#define JSON_HAS_CPP_14
2411+
#elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
24072412
#define JSON_HAS_CPP_17
24082413
#define JSON_HAS_CPP_14
2409-
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
2414+
#elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
24102415
#define JSON_HAS_CPP_14
24112416
#endif
24122417
// the cpp 11 flag is always specified because it is the minimal required version
@@ -5319,7 +5324,12 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
53195324
{
53205325
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
53215326
}
5322-
p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
5327+
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
5328+
#ifdef JSON_HAS_CPP_20
5329+
p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));
5330+
#else
5331+
p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20
5332+
#endif
53235333
}
53245334
#endif
53255335

@@ -6080,7 +6090,12 @@ inline void to_json(BasicJsonType& j, const T& t)
60806090
template<typename BasicJsonType>
60816091
inline void to_json(BasicJsonType& j, const std_fs::path& p)
60826092
{
6083-
j = p.string();
6093+
#ifdef JSON_HAS_CPP_20
6094+
const std::u8string s = p.u8string();
6095+
j = std::string(s.begin(), s.end());
6096+
#else
6097+
j = p.u8string(); // returns std::string in C++17
6098+
#endif
60846099
}
60856100
#endif
60866101

@@ -25337,6 +25352,7 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
2533725352
#undef JSON_HAS_CPP_14
2533825353
#undef JSON_HAS_CPP_17
2533925354
#undef JSON_HAS_CPP_20
25355+
#undef JSON_HAS_CPP_23
2534025356
#undef JSON_HAS_FILESYSTEM
2534125357
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
2534225358
#undef JSON_HAS_THREE_WAY_COMPARISON

tests/CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,32 @@ include(test)
2121
# override standard support
2222
#############################################################################
2323

24+
# Clang only supports C++14 starting from Clang 3.5 (lesser versions miss std::enable_if_t)
25+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
26+
unset(compiler_supports_cpp_14)
27+
endif()
28+
2429
# Clang only supports C++17 starting from Clang 5.0
2530
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
2631
unset(compiler_supports_cpp_17)
2732
endif()
2833
# MSVC 2015 (14.0) does not support C++17
29-
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.1)
34+
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)
35+
unset(compiler_supports_cpp_17)
36+
endif()
37+
# GCC 5 and 6 do claim experimental support for C++17, but do not implement <optional>
38+
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
3039
unset(compiler_supports_cpp_17)
3140
endif()
3241

3342
# Clang C++20 support appears insufficient prior to Clang 9.0 (based on CI build failure)
3443
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
3544
unset(compiler_supports_cpp_20)
3645
endif()
46+
# MSVC 2017 (15.x) does not support C++20
47+
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
48+
unset(compiler_supports_cpp_20)
49+
endif()
3750
# GCC started supporting C++20 features in 8.0 but a test for #3070 segfaults prior to 9.0
3851
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
3952
unset(compiler_supports_cpp_20)

tests/src/unit-conversions.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,31 @@ TEST_CASE("JSON to enum mapping")
16581658
}
16591659

16601660
#ifdef JSON_HAS_CPP_17
1661+
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
1662+
TEST_CASE("std::filesystem::path")
1663+
{
1664+
SECTION("ascii")
1665+
{
1666+
json const j_string = "Path";
1667+
auto p = j_string.template get<nlohmann::detail::std_fs::path>();
1668+
json const j_path = p;
1669+
1670+
CHECK(j_path.template get<std::string>() ==
1671+
j_string.template get<std::string>());
1672+
}
1673+
1674+
SECTION("utf-8")
1675+
{
1676+
json const j_string = "P\xc4\x9b\xc5\xa1ina";
1677+
auto p = j_string.template get<nlohmann::detail::std_fs::path>();
1678+
json const j_path = p;
1679+
1680+
CHECK(j_path.template get<std::string>() ==
1681+
j_string.template get<std::string>());
1682+
}
1683+
}
1684+
#endif
1685+
16611686
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
16621687
TEST_CASE("std::optional")
16631688
{

0 commit comments

Comments
 (0)