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

different behaviour between clang and gcc with braced initialization #1059

Closed
scontini76 opened this issue Apr 17, 2018 · 4 comments
Closed
Labels
solution: duplicate the issue is a duplicate; refer to the linked issue instead

Comments

@scontini76
Copy link

#include "json.hpp"
#include <iostream>

using json = nlohmann::json;

int main()
{
    auto j{json::parse(R"({ "result": [1, 2, 3] })")};

    std::cout << j["result"];

    return 0;
}

compiled with g++5, g++6, gcc+7 bring to the following result:

terminate called after throwing an instance of 'nlohmann::detail::type_error'
what(): [json.exception.type_error.305] cannot use operator[] with array

with clang++ the output is the excepted:

[1, 2, 3]

I'm using json 3.1.2, is there an error in my code? what compiler is wrong? can we obtain the same behaviour on both the family complier?

The code works in the on either g++ and clang++ if I use the assignment instead of braced initialisation.

so:

auto j{json::parse(R"({ "result": [1, 2, 3] })")};
std::cout << j["result"];

works on clang

auto j = json::parse(R"({ "result": [1, 2, 3] })");
std::cout << j["result"];

works on both family compilers

auto j{json::parse(R"({ "result": [1, 2, 3] })")};
std::cout << j[0]["result"];

works on g++

thank you for the support!

@nlohmann
Copy link
Owner

There have been previous issues regarding brace initialization, but I can't find them right now. I am not a language lawyer, but maybe someone has an idea on this.

@nlohmann nlohmann added the state: help needed the issue needs help to proceed label Apr 17, 2018
@whackashoe
Copy link
Contributor

auto a{json::parse(R"({ "result": [1, 2, 3] })")};                                                                              
auto b{a};
auto c{b};
auto d{c};
auto j(d);
std::cout << j << std::endl;                                                                                               

Results in: [[[[{"result":[1,2,3]}]]]] with g++.

What is happening is this constructor:

      basic_json(initializer_list_t init, 
                 bool type_deduction = true, 
                 value_t manual_type = value_t::array)

Is being called when using g++.

The initializer_list constructor docs state:

  1. If the list is empty, an empty JSON object value `{}` is created.
  2. If the list consists of pairs whose first element is a string, a JSON
     object value is created where the first elements of the pairs are
     treated as keys and the second elements are as values.
  3. In all other cases, an array is created.

So the issue is coming down to, why is this being selected in the first place?

auto j0  {json::parse(R"({ "result": [1, 2, 3] })")};                                                                           
auto j1 = json({json::parse(R"({ "result": [1, 2, 3] })")});
auto j2 = json{json::parse(R"({ "result": [1, 2, 3] })")};
auto j3 = json{{json::parse(R"({ "result": [1, 2, 3] })")}};

std::cout << j0 << std::endl;
std::cout << j1 << std::endl;                                                                                                   
std::cout << j2 << std::endl;
std::cout << j3 << std::endl;

g++:

[{"result":[1,2,3]}]
[{"result":[1,2,3]}]
[{"result":[1,2,3]}]
[[{"result":[1,2,3]}]]

clang:

{"result":[1,2,3]}
{"result":[1,2,3]}
{"result":[1,2,3]}
[[{"result":[1,2,3]}]]

So, we manage to do... something? On clang the most to me that would make sense (I think) would be a single set of square brackets around it, not two (for the j3 case).

My intuition is saying g++ is interpreting {(result_of_json_parse)} as an initializer list prior to deducing the type of auto, then it calls the initializer_list constructor with this value to result in a json type rather than a list, allowing for auto deduction. Why this would happen, I'm not entirely sure.

I sprinkled some print statements into the constructors to see what is happening like so:

      basic_json(const value_t v)
          : m_type(v), m_value(v)
      {                  
         std::cout << "VALUE_T"<< std::endl;                                                                                   
          assert_invariant();
      }                  

Clang says:

VALUE_T
VALUE_T
VALUE_T

g++-4.9 says:

VALUE_T
VALUE_T
VALUE_T
COMPAT_TYPE

Where COMPAT_TYPE is referring to:

      template <typename CompatibleType,
                typename U = detail::uncvref_t<CompatibleType>,
                detail::enable_if_t<
                    detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
      basic_json(CompatibleType && val) noexcept(noexcept(
                  JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
                                             std::forward<CompatibleType>(val))))
      {                  
         std::cout << "COMPAT_TYPE" << std::endl;                                                                              
          JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
          assert_invariant();
      }                  

Whereas g++ 5.4 says:


VALUE_T
VALUE_T
VALUE_T
INIT_LIST

(Remember at the top, the initializer_list constructor).


If we switch the code around to this:

json test() {
    return {json::parse(R"({ "result": [1, 2, 3] })")};
}
auto j0  = test();

And add explicit qualifier like:


     explicit basic_json(initializer_list_t init,                                                                              
                 bool type_deduction = true,
                 value_t manual_type = value_t::array)

We now get a compile error with g++ and not clang!

1.cpp: In function ‘json test()’:
1.cpp:7:54: error: converting to ‘json {aka nlohmann::basic_json<>}’ from initializer list would use explicit constructor ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::initializer_list_t, bool, nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::value_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::initializer_list_t = std::initializer_list<nlohmann::detail::json_ref<nlohmann::basic_json<> > >; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::value_t = nlohmann::detail::value_t]’

So we have something at least. But this compile error doesn't occur unless we do this... so... hmph.


On the side, I recommend not using brace initialization with auto... it is somewhat ambiguous and I've encountered multiple confusions from it. I

I think this is relevant:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3681.html

@stale
Copy link

stale bot commented May 19, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated label May 19, 2018
@stale stale bot closed this as completed May 26, 2018
@nlohmann
Copy link
Owner

Duplicate of #1359.

@nlohmann nlohmann added solution: duplicate the issue is a duplicate; refer to the linked issue instead and removed state: help needed the issue needs help to proceed state: stale the issue has not been updated in a while and will be closed automatically soon unless it is updated labels Dec 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
solution: duplicate the issue is a duplicate; refer to the linked issue instead
Projects
None yet
Development

No branches or pull requests

3 participants