-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
MSVC 2015 build fails when attempting to compare object_t #167
Comments
A few more comments before I call it a day: 2- It seems that the IsStreamInsertable intends for the proper overload to be:
BorgType has a CTOR that should just work for that:
This should be an implcit conversion to BorgType, which should result in the operator << being called above. That version DOES come up in the function lookup (see error on line 82):
however it for some reason doesn't realize it is convertable? 3- I'm not sure this is meaningful, but it seems that template-instance listed has T=test_type instead of the std::map that it always was before? It seems that on the referred to line that it is using the CLASS templateto captureExpression, though if the previous lines where it lists it as test_type, these should be the same?? |
|
Ah, thanks! I missed reading through the macro, and got caught up by the rest of it! Thanks! Hopefully that'll help my debugging today. |
As to the the familiar error C2593, It's just the compiler has more than one implicit convertion choices so it doesn't choose anymore. This may help, but I can not figure out where are the choices right now. |
Ya, this one is pretty annoying. The super annoying part is that the compiler lists EVERY operator<< overload as ambigious, and I strongly suspect that it is only ambigious between a few choices. I am going to binary search/build through the code today I think, otherwise no real idea on how to proceed. |
No, you got it wrong. The list is just all the overloads the compiler search from, which means nothing. |
So, here is something CRAZY interesting that makes me think we're getting some weird bug.... In Master, the existience of this unit test line on test/unit.cpp:11219 causes the problem to happen. Additionally, the unordered_map test that was added in #165 does the exact same thing! If I comment out those lines, (all of issue #89 on master), everything compiles fine! The only thing I can think of is that since that is the ONLY line that customizes the basic_json object, that the compiler is getting confused about the type of object_t? @mysyljr @nlohmann Any ideas/thoughts? |
Additionally, changing that line to match the default template (so that it isn't actually specialized) makes it work. ANY difference int he template causes the ambiguous operator << issue. |
@mysyljr : Sorry, missed your last comment until now. It didnt' come up until I refreshed github for some reason? What I meant was, in CLANG/G++ in an ambiguous overload situation it will list only the possible ones, not EVERY possible overload. I'll try your suggestion. |
@erichkeane My point is that the "sizeof( testStreamable(s << t) ) }; " way makes only an ambiguous error. But the "s<<t" way makes a real execution and will tell what exactly can be the choices, one more error in fact. |
Ah, good debug tip, thanks! I'll give it a try. I don't really get much experience with MSVC, so I am just used the clang/gcc error messages. Seems like compiler error message are a foreign language of their own. |
I'm using msvc2013 and it's not so much full of magic than gcc. It's only incompatible in details. When you found an error, maybe it's only gcc let it hides. |
And who designed the BorgType? I'm not sure if I'm right, but I think anything who can be implicit converted to a type and call operator<< will get a bug like this, which seems not fine. ^_^ |
In this case, both GCC and CLANG compile this properly (and both are well known to be extremely standards compliant), so I suspect that the issue is an oddity with MSVC. I'll note that I added
to the namespace with the BorgType struct, and this added zero additional errors. I DO notice that this only happens when there is multiple template specializations, and now that I look closer, lines 83-86 (below) in the error list seems to show that it is considering BOTH overloads of operator << via argument dependent lookup, which I believe is a mistake. I believe that the ones on json.hpp:4898 (1st and 3rd lines below) are the ones it is confused by, however my understanding of the lookup rules would make the 2nd set not valid, since they are different types.
|
BTW, BorgType was generated as a part of the Catch library, here: https://github.com/philsquared/Catch |
@erichkeane I really think it's a bug of Catch. I'll check it tommorrow.^_^ |
Alright, cool. I've run out of ideas/thoughts on how to push further, I suspect my lack of experience with MSVC is holding me back. If you could post any further things you identify when you look at it tomorrow, I'd love the chance to poke at it some more with any additional ideas you get. |
It's not Catch library's fault. Because only if you define a conflicting implicit conversion operator<< overload in the same namespace of BorgType, you will get an ambiguous error, that happens when you hack into it not when you use it. Now it suggests that even without BorgType, without CHECK(*p1 == value.get()), you will fail if you do a test "s<<t", for it's ambiguous at json library's design. I thought it was a simple problem, but it was in fact a design problem. Maybe I should install a VS2015 to get a whole view. |
Hi there! I really appreciate that you're digging into this issue. Unfortunately, I have no idea what goes wrong there... I would love to see the code compile with MSVC again, and if I can help, please let me know! (And Happy New Year to all of you! 🎆) |
@nlohmann:You're welcome! I had a few spare hours with a VM, but unfortunately couldn't come up with the solution. I PERSONALLY suspect that this is a defect with MSVC. For NOW, the work around is (on windows), only instantiate a single template of the basic_json object. I believe that since it isn't a regression, that #144 should be able to be merged. You could merge #165 if you cared to, however the alternative proposal in #164 is a great solution that I can start working on (if you're OK with the API change). Happy new year to you as well! |
This case failed for msvc:
convert(json::object_t()); This line: I find that the problem is linked to implicit construction from object_t and friend operator<< in basic_json, but of course both are not the final root of the problem because both are simple and can't be wrong. |
Though I may not get it solved this day, I find that there are two bugs all together, for compiler stops when it meets the first one. If I can't solve them I'll post a new issue in detail. Here is where the second wrong in json.hpp: |
IMO (And in Clang/G++'s opinion), the ambigious line of os << _value; should not be ambigious, as the two are totally different types. This is an issue with the MSVC compiler I suspect, though I'm not sure how to work around it. Not sure the issue with the 2nd one, since I didn't see the error message with it. |
Just comment out the whole section: SECTION("pointer access to object_t"), then the 2nd error will show. Now we need a way to detect which conversion caused the conflict. |
template <class CompatibleArrayType, typename
std::enable_if<
not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
= 0>
basic_json(const CompatibleArrayType& val)
: m_type(value_t::array)
{
using std::begin;
using std::end;
m_value.array = create<array_t>(begin(val), end(val));
} Just beautify the code I pasted ✨ 😌 |
Problem can be concluded to this: std::is_constructible<
basic_json<std::map, std::vector, bool, int32_t, float, std::allocator>,
basic_json<std::map, std::vector, bool, int64_t, double, std::allocator>
>::value is true somehow. |
I think my explanation still can't directly point to a compiler issue. Anyone can help to introduce the conversion relationship inside the json library? @erichkeane @nlohmann 😌 |
Well, that sounds like it could be the implicit conversion to the type enumeration and then construction from it. |
@gregmarr But I don't think so. I'll test. |
The problem can be minimally reproduced with this: nhohmann::json x;
nhohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, float> y { { "a", 1 } };
std::cout << y; This is just in a Intellisense is actually more helpful than the compiler output and points directly to the compiler being unable to decide between two versions of Helping the compiler find the right one by explicitly casting using this: nhohmann::json x;
nhohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, float> y { { "a", 1 } };
std::cout << (nhohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, float>) y; gets it to work, but this is merely covering up the underlying issue which is that the compiler bizarrely can't seem to decide the right one even though it seems so obvious. For what its worth I'm leaning towards it being the compiler although try as I might I can't reproduce the problem with a simplified version of the code outside of the json library. Without fixing the problem, there are a few workarounds we could employ to get AppVeyor to work so that at least we can get all current tests to run under VS2015 (even the custom The problematic types are (in addtion iterator
const_iterator
reverse_iterator
const_reverse_iterator
array_t::iterator
object_t The other types are either primitive types that are already overloaded, and probably wouldn't cause a problem even if they weren't, the string type, and pointers that are dealt with differently in Catch. The problematic Going down either road means that the tests would still function as tests, although the debugging info would be slightly less helpful (but we would still have line numbers). It would be possible to only switch on the fix for VS2015 using macros of course. None of this solves the underlying issue however and VS2015 support would still be incomplete however even having multiple versions of |
@twelsby Thanks for the detailed analysis. So far, I would be happy if the code would "just compile" with MSVC. As it seems not to be an issue with catch, wrapping |
@nlohmann, my local testing did not include modifying 100% of the affected To summarise some of the available workarounds and their implications (as I see it) more explicitly, they include: Modify ( __catchResult <= expr ).endExpression(); Pros:
Cons:
Modify all problematic Pros:
Cons:
Provide overloads for #ifdef _MSC_VER
namespace Catch {
std::string toString(json::iterator value) { return Detail::unprintableString; }
std::string toString(json::const_iterator value) { return Detail::unprintableString; }
std::string toString(json::reverse_iterator value) { return Detail::unprintableString; }
std::string toString(json::const_reverse_iterator value) { return Detail::unprintableString; }
std::string toString(json::array_t::iterator value) { return Detail::unprintableString; }
std::string toString(json::object_t value) {
return (static_cast<std::ostringstream&>(std::ostringstream() << static_cast<json>(value))).str();
}
}
#endif Pros:
Cons:
Loss of debug quality For the record I prefer workaround three (as you can probably tell). Once it has been successfully built all unit tests pass by the way. My local build settings and the unit test parameters were as per the |
If you want I can add a commit with this code to my pull request and it will show the AppVeyor results or submit another pull request (via a different branch). I just don't know how to do it without the version 2.0.0 changes short of reverting them, applying this code and then reapplying them (it would be nice if github allowed two forks). |
Well I have some more insight into the underlying problem... and another possible fix. I wish I could claim that it was due to my deep understanding of c++ or inherent genius but alas this is not so because I cheated and took the path of least resistance. The answer is commit 457bfc2 - this is where it all went wrong. It was introduced to solve the problem of implicit conversion to strings (issue #144), per: s2 = o["name"]; To achieve this additional conditions were placed on the instantiation of not std::is_same<ValueType, typename string_t::value_type>::value
not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value Comment out the second one and all is well. Compilation works under VS2015 and all tests pass. It doesn't even affect the fix for #144. Now could someone who does have a deep understanding of c++ or inherent genius please explain why this works. Don't be modest. Of course this 'fix' introduces its own problem because gcc (and no doubt clang) still complains about the line from issue #144. The obvious workaround is to omit adding the extra condition for only VS2015 by wrapping it in a I get the feeling there is an even better solution out there that doesn't require conditional compilation. By the way, I am now getting floating point -Wfloat-equal warnings at 2913 and 2917 which is in the new code from commit 663ad13. They are typical gcc paranoia so probably could be ignored, suppressed or whatever (its not like we are adding floats like 0.8+0.1 != 0.9 - comparing a float with one you saved earlier should not be unsafe because there is no possibility for rounding error). |
Rather than incorporate this in pull #183, I have split it off so that it can be merged for version 1.0.1 (what I should have done to begin with). |
Fixed with #188. |
See #144 and #165 for examples of the build.
It seems that the following results in a test to see if a value is streamable in catch.hpp:
unit.cpp:2588 CHECK(*p1 == value.get<test_type>());
This causes a call to operator == in catch.hpp (17771):
THAT causes a call to the captureExpression function on catch.hpp:1829
The Catch::toString results in a call to catch.hpp: 1731's toString->StringMaker:
StringMaker simply offloads to Detail::StringMakerBase, but first goes through the Detail::IsStreamInsertable::value! This happens on 1575:
On Linux, this appears to realize there is NO overload for operator << that works, however MSVC doesn't seem to know that, and decides that it is an ambiguous call between just about every possible overload.
The text was updated successfully, but these errors were encountered: