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

Unexpected conversion from 'true'/'false' to some integer types #4358

Open
2 tasks
jboelterintc opened this issue Apr 24, 2024 · 1 comment
Open
2 tasks

Comments

@jboelterintc
Copy link

jboelterintc commented Apr 24, 2024

Description

Converting from "value": true or "value": false behaves unexpectedly and differently for certain integer types.

I was expecting all conversions from a boolean true/false to integer value to fail, however it is stored as a 1 or 0.

Reproduction steps

Given:

template<typename T>
class Value {
public:
    T value;
private:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Value<T>, value);
};

This will result in a 1 in value.

auto v = R"({
    "value" : true
})"_json.get<Value<int>>();

Using uint64_t will throw a type_error.

auto v = R"({
    "value" : true
})"_json.get<Value<uint64_t>>();

Expected vs. actual results

I expected all boolean to integer conversions to fail. Instead a true -> 1 and false -> 0 for certain integer values.

Various integer conversions - https://godbolt.org/z/7Wrh6EanW

The uint64_t case is taking the path through get_arithmetic_value

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

The int case is taking the path through

struct from_json_fn
{
    template<typename BasicJsonType, typename T>
    auto operator()(const BasicJsonType& j, T&& val) const
    noexcept(noexcept(from_json(j, std::forward<T>(val))))
    -> decltype(from_json(j, std::forward<T>(val)))
    {
        return from_json(j, std::forward<T>(val));
    }
};

// which calls

template < typename BasicJsonType, typename ArithmeticType,
           enable_if_t <
               std::is_arithmetic<ArithmeticType>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
               int > = 0 >
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
//...

        case value_t::boolean:
        {
            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
            break;
        }
//...
}

Minimal code example

https://godbolt.org/z/fs4frqz7G

#include <cassert>
#include <iostream>

#include <nlohmann/json.hpp>

template<typename T>
class Value {
public:
    T value;
private:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Value<T>, value);
};

int main() {
    try {
        std::cout << "uint64_t" << std::endl;
        auto v = R"({
            "value" : true
        })"_json.get<Value<uint64_t>>();

        assert(false);
    } catch (const nlohmann::detail::type_error& e) {
        std::cout << e.what() << std::endl;
        std::cout << "ok - exception expected" << std::endl;
    }

    try {
        std::cout << "int" << std::endl;
        auto v = R"({
            "value" : true
        })"_json.get<Value<int>>();

        assert(false);
    } catch (const nlohmann::detail::type_error& e) {
        std::cout << e.what() << std::endl;
        std::cout << "ok - exception expected" << std::endl;
    }
}

Error messages

No response

Compiler and operating system

Latest MSVC & Clang

Library version

3.11.3

Validation

@jshastid
Copy link

I am having the same issue and apologies if I am not understanding this correctly as I am new to C++ and have only just started using this library, but it looks like the integer value is not being considered a basic_json and so is calling the explicit cast from_json. At least the comment above the function leads me to believe this.
// overload for arithmetic types, not chosen for basic_json template arguments
// (BooleanType, etc..); note: Is it really necessary to provide explicit
// overloads for boolean_t etc. in case of a custom BooleanType which is not
// an arithmetic type?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants