Search code examples
c++boost-spiritboost-spirit-qi

Boost.Spirit parse string to number with radix


I have a template function which casts string to number as following:

template <typename RetType,
    typename Parser =
    typename boost::spirit::traits::create_parser<RetType>::type>
    inline std::enable_if_t<std::is_arithmetic<RetType>::value, RetType>
    cast(const std::string &input)
{
    RetType result;

    if(input.empty())
    {
        // handle this
    }
    auto itBeg = input.cbegin();
    auto itEnd = input.cend();
    if(!bsq::parse(itBeg, itEnd, Parser(), result) || itBeg != itEnd)
    {
        // handle that
    }
    return result;
}

now I would like to create a function similar to above which will parse string that represents a number in some radix

template <typename RetType, unsigned Radix,
    typename Parser =
    typename boost::spirit::traits::create_parser<RetType>::type>
    inline std::enable_if_t<std::is_arithmetic<RetType>::value, RetType>
    cast(const std::string &input)
{
    RetType result;

    if(input.empty())
    {
        // handle this
    }
    auto itBeg = input.cbegin();
    auto itEnd = input.cend();
    if(!bsq::parse(itBeg, itEnd, Parser<RetType, Radix, 1 - 1>() /*something like this*/, result) || itBeg != itEnd)
    {
        // handle that
    }

    return result;
}

the Parser is invalid, of course, but what is the right way to define "automatic" arithmetic parser with radix?


Solution

  • I'd use qi::int_parser<> directly:

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <type_traits>
    
    template <typename RetType, unsigned Radix = 10, typename Parser = typename boost::spirit::qi::int_parser<RetType, Radix> >
    inline typename std::enable_if<std::is_arithmetic<RetType>::value, RetType>::type
        cast(const std::string &input)
    {
        RetType result;
    
        if(input.empty())
        {
            // handle this
        }
        auto itBeg = input.cbegin();
        auto itEnd = input.cend();
        if(!boost::spirit::qi::parse(itBeg, itEnd, Parser(), result) || itBeg != itEnd)
        {
            // handle that
            throw "oops";
        }
    
        return result;
    }
    
    int main() {
        std::cout << cast<int>    ("10") << "\n";
        std::cout << cast<int,  2>("10") << "\n";
        std::cout << cast<int,  8>("10") << "\n";
        std::cout << cast<int, 16>("10") << "\n";
        std::cout << cast<int, 16>("ee") << "\n";
    }
    

    Prints

    10
    2
    8
    16
    238
    

    Hint: to be very accurate you might want to detect signed/unsigned types and use uint_parser<> accordingly