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

json::value_t can't be a map's key type in VC++ 2015 #486

Closed
Getfree opened this issue Mar 7, 2017 · 11 comments
Closed

json::value_t can't be a map's key type in VC++ 2015 #486

Getfree opened this issue Mar 7, 2017 · 11 comments
Assignees
Labels
Milestone

Comments

@Getfree
Copy link

Getfree commented Mar 7, 2017

The following code does not compile in Visual Studio 2015, but compiles fine in GCC.

#include <string>
#include <map>
#include <iostream>

#include "json.hpp"
using JSON = nlohmann::json ;

int main (){
	using namespace std ;
	
	map<JSON::value_t,string> jsonTypes ;

	jsonTypes[JSON::value_t::array] = "array" ;

	cout << jsonTypes[ JSON({"val1", 123, false}) ] << endl ;
}

Wandbox: http://melpon.org/wandbox/permlink/Izdt8DW4UGpVQdUd

@nlohmann nlohmann added the platform: visual studio related to MSVC label Mar 7, 2017
@nlohmann
Copy link
Owner

nlohmann commented Mar 7, 2017

Can you please provide the error messages from MSVC?

@Getfree
Copy link
Author

Getfree commented Mar 7, 2017

No problem. Here:

Z:\>cl /EHsc test3.cpp
test3.cpp
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xstddef(239): error C2593: 'operator <' is ambiguous
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\system_error(436): note: could be 'bool std::operator <(const std::error_condition &,const std::error_condition &) noexcept'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\system_error(427): note: or       'bool std::operator <(const std::error_code &,const std::error_code &) noexcept'
z:\json.hpp(171): note: or       'bool nlohmann::detail::operator <(const nlohmann::detail::value_t,const nlohmann::detail::value_t) noexcept' [found using argument-dependent lookup]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xstddef(239): note: while trying to match the argument list '(const nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t, const nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t)'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xstddef(238): note: while compiling class template member function 'bool std::less<_Kty>::operator ()(const _Ty &,const _Ty &) const'
        with
        [
            _Kty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,
            _Ty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\map(210): note: see reference to function template instantiation 'bool std::less<_Kty>::operator ()(const _Ty &,const _Ty &) const' being compiled
        with
        [
            _Kty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,
            _Ty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\type_traits(421): note: see reference to class template instantiation 'std::less<_Kty>' being compiled
        with
        [
            _Kty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xutility(265): note: see reference to class template instantiation 'std::is_empty<_Ty1>' being compiled
        with
        [
            _Ty1=std::less<nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t>
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xtree(957): note: see reference to class template instantiation 'std::_Tree_comp_alloc<_Traits>' being compiled
        with
        [
            _Traits=std::_Tmap_traits<nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,std::string,std::less<nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t>,std::allocator<std::pair<const nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,std::string>>,false>
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\map(73): note: see reference to class template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>' being compiled
        with
        [
            _Kty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,
            _Ty=std::string,
            _Pr=std::less<nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t>,
            _Alloc=std::allocator<std::pair<const nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,std::string>>
        ]
test3.cpp(11): note: see reference to class template instantiation 'std::map<nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,std::string,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>' being compiled
        with
        [
            _Kty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t,
            _Ty=std::string
        ]

@nlohmann
Copy link
Owner

nlohmann commented Mar 7, 2017

Could you try explicitly to pass nlohmann::detail::operator< as Compare template argument to the map?

@Getfree
Copy link
Author

Getfree commented Mar 7, 2017

I don't know how to pass the operator itself, but I tried passing a functor, like this:

#include <string>
#include <map>
#include <iostream>

#include "json.hpp"
using JSON = nlohmann::json ;

int main (){
	using namespace std ;

	struct value_tCompare {
		bool operator()(const JSON::value_t& a, const JSON::value_t& b) const{
			return a < b ;
		}
	} ;
	
	map<JSON::value_t, string, value_tCompare > jsonTypes ;

	jsonTypes[JSON::value_t::array] = "array" ;

	cout << jsonTypes[ JSON({"val1", 123, false}) ] << endl ;
}

And the result was this:

Z:\>cl /EHsc test3.cpp
test3.cpp
test3.cpp(13): error C2593: 'operator <' is ambiguous
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\system_error(436): note: could be 'bool std::operator <(const std::error_condition &,const std::error_condition &) noexcept'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\system_error(427): note: or       'bool std::operator <(const std::error_code &,const std::error_code &) noexcept'
z:\json.hpp(171): note: or       'bool nlohmann::detail::operator <(const nlohmann::detail::value_t,const nlohmann::detail::value_t) noexcept' [found using argument-dependent lookup]
test3.cpp(13): note: while trying to match the argument list '(const nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t, const nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::value_t)'

Then I tried using nlohmann::detail::operator< explicitly inside the functor, and it compiled successfully.

#include <string>
#include <map>
#include <iostream>

#include "json.hpp"
using JSON = nlohmann::json ;

int main (){
	using namespace std ;

	struct value_tCompare {
		bool operator()(const JSON::value_t& a, const JSON::value_t& b) const{
			return nlohmann::detail::operator<(a,b) ;
		}
	} ;
	
	map<JSON::value_t, string, value_tCompare > jsonTypes ;

	jsonTypes[JSON::value_t::array] = "array" ;

	cout << jsonTypes[ JSON({"val1", 123, false}) ] << endl ;
}

This seems to be an argument-dependent lookup issue. For some reason the compiler is not prioritizing nlohmann::detail::operator< over the others.

Hopefully there is a way to solve it, so we don't need this workaround.

@nlohmann
Copy link
Owner

nlohmann commented Mar 7, 2017

This seems to be an issue where @theodelrieu may know more.

@theodelrieu
Copy link
Contributor

I will take a look tomorrow, this looks like a MSVC-specific weirdness at first sight

@theodelrieu
Copy link
Contributor

There is a workaround mentioned in this StackOverflow thread
Should we add this to the library?

namespace std {
template <>
struct less<::nlohmann::detail::value_t>
{
  bool operator()(::nlohmann::detail::value_t lhs, ::nlohmann::detail::value_t rhs) const noexcept
  {
    return ::nlohmann::detail::operator<(lhs, rhs);
  }
};
}

@nlohmann
Copy link
Owner

nlohmann commented Mar 8, 2017

Is it safe to add this to the std namespace? @gregmarr ?

@theodelrieu
Copy link
Contributor

theodelrieu commented Mar 8, 2017

That's a template specialization, it's allowed and safe.

Plus, this is what the default std::less<nlohmann::detail::value_t> looks like on the other platforms.

@nlohmann nlohmann self-assigned this Mar 11, 2017
@nlohmann nlohmann added this to the Release 3.0.0 milestone Mar 11, 2017
nlohmann added a commit that referenced this issue Mar 11, 2017
MSVC needs this overload to compile code containing a std::map that
uses nlohmann::detail::operator as key.
@nlohmann
Copy link
Owner

The following code now compiles with MSVC 2015 (see https://ci.appveyor.com/project/nlohmann/json/build/1765):

std::map<json::value_t, std::string> jsonTypes ;
jsonTypes[json::value_t::array] = "array";

@Getfree Can you try this version of the code please: f4126e4

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Mar 11, 2017
@Getfree
Copy link
Author

Getfree commented Mar 11, 2017

I added the fixed version to my project and it compiled successfully.

Many thanks.

nlohmann added a commit that referenced this issue Mar 12, 2017
Implemented std::less<value_t> to allow using value_t as std::map key
in MSVC.
@nlohmann nlohmann removed the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Mar 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants