json: String type change breaks C++ type matching
Usually when one of template parameters is changed, reference to new type cannot be implicitly converted to reference to old type. However for some reason this is not true for nlohmann::json. I tried to change std::string to my custom string class, and it turned out that references to both types are compatible for compiler. Here is example code which demonstrates this problem. It uses std::wstring instead of std::string:
#include <json.hpp>
template <
template <typename U, typename V, typename... Args> class ObjectType = std::map,
template <typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::wstring, class BooleanType = bool,
class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double, template <typename U> class AllocatorType = std::allocator,
template <typename T, typename SFINAE = void> class JSONSerializer = nlohmann::adl_serializer>
using MyBasicJson = nlohmann::basic_json<
ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType,
NumberFloatType, AllocatorType, JSONSerializer>;
using MyJson = MyBasicJson<>;
void load(const nlohmann::json& json);
//void load(const MyJson& json);
void test(MyJson* json)
{
load(*json);
}
During compilation I should get error that parameter passed to load() has incorrect type. However this code compiles cleanly. I tried to use gcc 10.2, clang 10.0, both compiles this code without any complain. I use nlohmann::json 3.9.1. I compiled it on CentOS and Ubuntu using following command:
g++ -c -o dupa.o test.cpp -O3 -Wall -Wextra -Werror -std=c++11 -I.
I played a bit with this code trying to create minimum example. I found that when removed include and copied forward declarations from json.hpp to my file, g++ reported error as expected. So it looks that something is wrong in other part of json.hpp.
Initially I thought that this may be some gcc error, but clang also does not complain, so this is unlikely.
Which compiler and operating system are you using?
gcc 10.2 on CentOS7 (installed from RedHat SCL packages), clang 10.0 on Ubuntu Ubuntu 20.04.2 LTS
Which version of the library did you use?
- latest release version 3.9.1
- other release - please state the version: ___
- the
develop
branch
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 17 (7 by maintainers)
The workaround I mentioned before is still the only way to solve this I could think of so far. Any other solutions I considered are breaking changes and would have to wait until 4.0.
The caveat is, that even my workaround requires a minor modification of the library and I haven’t done any significant testing to ensure it doesn’t break anything else.
Here’s the code to disable conversions between
basic_json
types with mismatched string types: (alt_string
is the alternative string type from the library unit test, it’s a thin wrapper aroundstd::string
. Replace that with your string type.)This works by SFINAE-disabling the
from_json
/to_json
functions for mismatched string types.In addition, the following change to
include/nlohmann/json.hpp
is required for the above code to compile.basic_json
:U
indetail::is_compatible_type<basic_json_t, U>::value
withCompatibleType
.Again, I haven’t done any further testing. Once I confirm that this modification doesn’t break anything else, I’ll submit a PR.
@falbrechtskirchinger my custom string class is something like @agauniyal’s
secure_string
. I usedstd::wstring
only as an example, to avoid pasting code of my string class here. It is something like below. With this approach I can securely allocate and clear memory used bystd::string
object too:I’ll try if can get you a mve for
secure_allocator
. Your understanding about it’ssecure
nature is correct as well, I just want to add that it disables SSO by manually allocating bigger strings even in the case of small ones, such that nothing is stored on the stack.This might be related to #3425. I’ll check if any of the provided examples trigger the assertions I’ve added to at least catch some problematic conversions between
basic_json
types.And as an aside,
std::wstring
asStringType
is not well supported. There’re too many places where it’s assumed that the underlying char type is 8bit wide. Parsingstd::wstring
on the other hand is well defined.I am aware of the issue, but have not found the time to look into it.