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: Brad Anderson <eco@…> 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 Brad Anderson <eco@…>, 7 years ago

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.

comment:2 by Brad Anderson <eco@…>, 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 Vladimir Batov, 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 Brad Anderson <eco@…>, 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 Vladimir Batov <vbmail247@…>, 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 Brad Anderson <eco@…>, 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 Vladimir Batov <vbmail247@…>, 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 viboes, 7 years ago

Component: Noneconvert
Owner: set to viboes

comment:9 by viboes, 6 years ago

Owner: changed from viboes to Vladimir Batov

comment:10 by Vladimir Batov, 6 years ago

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.