Opened 7 years ago
Closed 6 years ago
#11800 closed Bugs (fixed)
Boost Convert fails on user defined types with no value_type
| Reported by: | Owned by: | Vladimir Batov | |
|---|---|---|---|
| Milestone: | To Be Determined | Component: | convert | 
| Version: | Boost 1.59.0 | Severity: | Problem | 
| Keywords: | Cc: | 
Description
Both the stream and strtol converters seem to make use of is_string which appears to not be using SFINAE correctly resulting in a compilation error ('value_type' : is not a member of 'X').
In 1.59.0 the offending line is boost/convert/detail/is_string.hpp:26.
        template<typename T> struct is_string<T, /*is_range_class=*/true>
        {
            static bool const value = cnv::is_char<typename T::value_type>::value;
        };
      Change History (10)
comment:1 by , 7 years ago
comment:2 by , 7 years ago
Here is a reproducible using a slightly modified version of the example included with boost convert.
#include <boost/convert.hpp>
#include <boost/convert/stream.hpp>
//#define VALUE_TYPE value_type
#define VALUE_TYPE type
struct change
{
    enum VALUE_TYPE { no, up, dn };
    change(VALUE_TYPE v =no) : value_(v) {}
    bool operator==(change v) const { return value_ == v.value_; }
    VALUE_TYPE value() const { return value_; }
    private: VALUE_TYPE value_;
};
std::istream& operator>>(std::istream& stream, change& chg)
{
    std::string str; stream >> str;
    /**/ if (str == "up") chg = change::up;
    else if (str == "dn") chg = change::dn;
    else if (str == "no") chg = change::no;
    else stream.setstate(std::ios_base::failbit);
    return stream;
}
std::ostream& operator<<(std::ostream& stream, change const& chg)
{
    return stream << (chg == change::up ? "up" : chg == change::dn ? "dn" : "no");
}
int main()
{
    boost::cnv::cstream cnv;
    boost::convert<std::string>(change(change::up), cnv);
}
The original uses value_type but if you change it to something else (as I've done here) you'll get the compilation error in MSVC 2013.
comment:3 by , 7 years ago
Thank you for the analysis and the code snippet. It's much appreciated. Unfortunately, I have no access to MSVC 2013. So, my reasoning is based on the information provided and my looking at the code. There I see that the mentioned code causing compilation error:
template<typename T> struct is_string<T, /*is_range_class=*/true> {
static bool const value = cnv::is_char<typename T::value_type>::value;
};
only kicks in if T is a class (checked by boost::is_class<T>) and T is a range (checked by cnv::is_range<T>). The "struct change" is a class. However, it is not a range and, therefore, cnv::is_range<T> should fail for the class as it does not have begin() and end() methods (see range.hpp lines 19-26). That mentioned check does correctly fail with the compilers (gcc and clang) available to me. More so, judging from http://www.boost.org/development/tests/develop/developer/convert.html, the respective test (convert_test_has_member) also works on msvc-12.0 (aka MSVC-2013, aka BOOST_MSVC=1800) correctly. However, it is known that earlier versions are broken and fail to compile convert_test_has_member.
However, I understand you are using msvc-12.0 (MSVC-2013, BOOST_MSVC=1800) which seems to work in regression tests but fails for you. Could that be that your version of MSVC-2013 needs an update that would bring its behavior in line with msvc-12.0 in the regression tests?
comment:4 by , 7 years ago
I'm using the latest version of Visual Studio 2013 (Version 12.0.40629.00 Update 5). K-ballo in the IRC channel was able to reproduce it as well using Visual Studio 2013.
I've now tested with Visual Studio 2015 Update 1 RC1 and it works fine there so it's definitely a Visual Studio 2013 thing. I can't explain it or why the tests don't catch it, however.
comment:5 by , 7 years ago
Thank you for your reply. It appears I'll have to extend the respective tests and see how Visual Studio 2013 (aka msvc-12.0 in the regression tests) behave. Unfortunately it's lengthy (I'll have to monitor regression tests). Is it a show-stopper for you? Can you work around the problem for now to keep you going? Just to make sure, could you please print out the value of BOOST_MSVC for me please? The reason is that for "BOOST_MSVC < 1800" the respective test is disabled outright in has_member.cpp due to insufficient SFINAE support. However, you indicated that you are using Visual Studio 2013 which (according to https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) should have BOOST_MSVC=1800, i.e. actually run that has_member.cpp test.
Thanks, Vladimir.
comment:6 by , 7 years ago
It is not a showstopper for me. I've just added some self referential value_type typedefs to work around it until I upgrade to VS2015 in the nearish future.
BOOST_MSVC correctly comes out to 1800 for me.
comment:7 by , 7 years ago
Many thanks. I am glad it's not blocking you. That way I'll see what I can do without a hurry.
comment:8 by , 7 years ago
| Component: | None → convert | 
|---|---|
| Owner: | set to | 
comment:9 by , 6 years ago
| Owner: | changed from to | 
|---|
comment:10 by , 6 years ago
| Resolution: | → fixed | 
|---|---|
| Status: | new → closed | 

Appears to be MSVC 2013 only. Someone in IRC said 2015 works fine. The check for a range type is what is truly failing, I think.