Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compilation error when trying to use same type for number_integer_t and number_unsigned_t in basic_json template specification. #2573

Closed
2 of 5 tasks
slovak194 opened this issue Jan 7, 2021 · 4 comments
Labels
solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope)

Comments

@slovak194
Copy link

slovak194 commented Jan 7, 2021

I'm trying to set the same storage type (std::int64_t) for both number_integer_t and number_unsigned_t for basic_json without success so far. It looks like they by design have to be different as setting same types lead to duplicated declatations in code.

What is the issue you have?

This line of code does not compile:
nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::int64_t, double> j4; // Fail

Please describe the steps to reproduce the issue.

Here is the small piece of code which shows the issue:

`
#include "nlohmann/json.hpp"

int main(int argc, char **argv) {

nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double> j1; // Ok
nlohmann::basic_json<std::map, std::vector, std::string, bool, std::uint64_t, std::int64_t, double> j2; // Ok
nlohmann::basic_json<std::map, std::vector, std::string, bool, int, long int, double> j3; // Ok
nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::int64_t, double> j4; // Fail
nlohmann::basic_json<std::map, std::vector, std::string, bool, float, float, float> j5; // Fail

return 0;
}
`

What is the expected behavior?

nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::int64_t, double> should be compiled without errors and use same storage type for number_unsigned_t and number_integer_t

And what is the actual behavior instead?

Here is the output from compiler:
/usr/local/include/nlohmann/json.hpp: In instantiation of ‘class nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char>, bool, long int, long int, double>’: /home/slovak/remote-config/test/test_types.cpp:11:102: required from here /usr/local/include/nlohmann/json.hpp:19337:24: error: ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_unsigned_t* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::get_impl_ptr(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_unsigned_t*) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_unsigned_t = long int]’ cannot be overloaded with ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::get_impl_ptr(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t*) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t = long int]’ 19337 | number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept | ^~~~~~~~~~~~ /usr/local/include/nlohmann/json.hpp:19325:23: note: previous declaration ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::get_impl_ptr(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t*) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t = long int]’ 19325 | number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept | ^~~~~~~~~~~~ /usr/local/include/nlohmann/json.hpp:19343:40: error: ‘constexpr const number_unsigned_t* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::get_impl_ptr(const number_unsigned_t*) const [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_unsigned_t = long int]’ cannot be overloaded with ‘constexpr const number_integer_t* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::get_impl_ptr(const number_integer_t*) const [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t = long int]’ 19343 | constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept | ^~~~~~~~~~~~ /usr/local/include/nlohmann/json.hpp:19331:39: note: previous declaration ‘constexpr const number_integer_t* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::get_impl_ptr(const number_integer_t*) const [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t = long int]’ 19331 | constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept | ^~~~~~~~~~~~ /usr/local/include/nlohmann/json.hpp: In instantiation of ‘union nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char>, bool, long int, long int, double>::json_value’: /usr/local/include/nlohmann/json.hpp:23530:16: required from ‘class nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char>, bool, long int, long int, double>’ /home/slovak/remote-config/test/test_types.cpp:11:102: required from here /usr/local/include/nlohmann/json.hpp:17558:9: error: ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::json_value::json_value(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_unsigned_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_unsigned_t = long int]’ cannot be overloaded with ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::json_value::json_value(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t = long int]’ 17558 | json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} | ^~~~~~~~~~ /usr/local/include/nlohmann/json.hpp:17556:9: note: previous declaration ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::json_value::json_value(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::number_integer_t = long int]’ 17556 | json_value(number_integer_t v) noexcept : number_integer(v) {} | ^~~~~~~~~~

Which compiler and operating system are you using?

  • Compiler: gcc version 9.3.0
  • Operating system: Ubuntu 9.3.0-17ubuntu1~20.04

Which version of the library did you use?

  • latest release version 3.9.1
  • other release - please state the version: ___
  • the develop branch

If you experience a compilation error: can you compile and run the unit tests?

  • yes
  • no - please copy/paste the error message below
@gregmarr
Copy link
Contributor

gregmarr commented Jan 7, 2021

What are you trying to achieve by doing this? It is not possible to do this because that makes these two functions (and probably other pairs) identical:

template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
{
    get_arithmetic_value(j, val);
}

template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
{
    get_arithmetic_value(j, val);
}

I'm sure that there's also code that assumes that number_unsigned_t is actually an unsigned type. (@nlohmann it may be worth adding a static_assert for that if that is an accurate assumption.)

@slovak194
Copy link
Author

slovak194 commented Jan 7, 2021

What are you trying to achieve by doing this? It is not possible to do this because that makes these two functions (and probably other pairs) identical:

I'm trying to add some kind of type safety and numerical array homogeneity guaranty on top of this library. I plan to extensively use direct access to the underlying memory (json.get_ptr()) to avoid copies and I'd like to have some guarantee that the underlying type will not change accidentally.
For example, if I have very big json with the vector of floating-point numbers - I'd like to have the possibility to keep this vector homogenous - all members float. Further, I can use Eigen::Map for fast direct access to the data. Same for integer types.

RFC 7159 describes numbers as follows:
The representation of numbers is similar to that used in most programming languages. A number is represented in base 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted.

This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, number_integer_t, number_unsigned_t and number_float_t are used.

As JSON standard does not differentiate between float, signed, and unsigned - this is quite natural to have the possibility to use the same storage type for any incoming number. Or, some other better control over how types are deducted from values.

@nlohmann
Copy link
Owner

nlohmann commented Jan 7, 2021

It is currently neither possible nor planned to use the same type for signed and unsigned integers used by the library. @gregmarr is right we should add static assertions for this. Sorry for the inconvenience.

@nlohmann nlohmann added solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope) and removed kind: bug labels Jan 7, 2021
@eric-wieser
Copy link

Presumably it would be possible to just eliminate these duplicate functions with std::enable_if for the case when the two types are equal?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
solution: wontfix the issue will not be fixed (either it is impossible or deemed out of scope)
Projects
None yet
Development

No branches or pull requests

5 participants
@nlohmann @eric-wieser @slovak194 @gregmarr and others