c++gcclexical-castboost-units

boost::lexical_cast with boost::units::quantity does not compile any more


I have a problem with code that parses values taken via boost_program_options to boost::units quantities. It used to work just fine, and now, on a new setup, no longer does. The obscure error message hints at a problem with boost::lexical_cast, which seems to be used by program_options.

A minimal example (in which I got rid of the all program options stuff) is

#include <iostream>
#include <boost/units/io.hpp>
#include <boost/units/systems/si.hpp>
#include <boost/units/systems/si/io.hpp>
#include <boost/lexical_cast.hpp>
#include <sstream>

using namespace boost::units;

std::istream& operator>>(std::istream& in, quantity<si::current>& c)
{
    c = quantity<si::current>(0.7 * si::ampere);
    return in;
}

int main()
{
    quantity<si::current> c = boost::lexical_cast<quantity<si::current> >("3.0A");
    std::cout << c << std::endl;
}

This compiles without errors on my old setup (gcc 4.7.2, boost 1.49), but on gcc 4.9.2 with boost 1.55 no longer compiles (by the way, please ignore the fact that even if it compiles, it throws an Exception in either case, because nothing is actually read from the stream; I did not want to obfuscate this post by including the whole parsing logic). Instead, I get the error message

In file included from /usr/include/boost/serialization/tracking.hpp:20:0,
                 from /usr/include/boost/serialization/nvp.hpp:32,
                 from /usr/include/boost/units/io.hpp:27,
                 from bla3.cpp:2:
/usr/include/boost/lexical_cast.hpp: In instantiation of ‘struct boost::detail::deduce_target_char_impl<boost::detail::deduce_character_type_later<boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::current_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10l, boost::units::static_rational<3l> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > > > > >’:
/usr/include/boost/lexical_cast.hpp:415:89:   required from ‘struct boost::detail::deduce_target_char<boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::current_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10l, boost::units::static_rational<3l> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > > > >’
/usr/include/boost/lexical_cast.hpp:674:92:   required from ‘struct boost::detail::lexical_cast_stream_traits<const char*, boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::current_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10l, boost::units::static_rational<3l> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > > > >’
/usr/include/boost/lexical_cast.hpp:2363:19:   required from ‘static Target boost::detail::lexical_cast_do_cast<Target, Source>::lexical_cast_impl(const Source&) [with Target = boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::current_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10l, boost::units::static_rational<3l> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > > >; Source = const char*]’
/usr/include/boost/lexical_cast.hpp:2543:50:   required from ‘Target boost::lexical_cast(const Source&) [with Target = boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::current_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10l, boost::units::static_rational<3l> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > > >; Source = char [5]]’
bla3.cpp:18:81:   required from here
/usr/include/boost/lexical_cast.hpp:388:13: error: invalid application of ‘sizeof’ to incomplete type ‘boost::STATIC_ASSERTION_FAILURE<false>’
             BOOST_STATIC_ASSERT_MSG((result_t::value || boost::has_right_shift<std::basic_istream<wchar_t>, T >::value), 
             ^

If I use a different custom type (not a boost::units::quantity) in the lexical cast and overload the >> operator for it, or if I directly try to read a quantity from a stringstream, all works fine.

Can someone tell me what I am doing wrong here?


Solution

  • An overloaded operator, like a free function, needs to be in an associated namespace to be located by argument-dependent lookup.

    In this case you would need to open either the std or boost::units namespaces, since the arguments to operator>> are in namespace std and boost::units respectively:

    namespace boost { namespace units {
    
    std::istream& operator>>(std::istream& in, quantity<si::current>& c)
    {
        c = quantity<si::current>(0.7 * si::ampere);
        return in;
    }
    
    }}
    

    This should indicate to you that this is not entirely safe, as your code would collide with any stream in operator supplied by Boost.Units in a future version, or by another 3rd-party user. You should consider writing your own wrapper for quantity<si::current> and supplying a stream in operator there.