ApprenticeHacker
ApprenticeHacker

Reputation: 22011

Generic way to convert a string into a numerical type?

I have this class:

template<typename T> class Parser
{
    public:
        Parser() : count(0) {}
        virtual void parse(const string&);
        void get_token(void);
    private:
        T result;
        char token;
        string expression;
        int count;
};

now had the class not been generic, had the result been say, a double, I would have used this method to detect numbers.

while((strchr("1234567890.",token))
{
     /* add token to a "temp" string */
     /* etc. etc. */
}

result = atof(temp.c_str());

But since result is generic, I can't use any method like atof and atoi etc.

What do I do?

Upvotes: 0

Views: 2686

Answers (4)

Synck
Synck

Reputation: 3548

With C++17 you can use the templated std::from_chars. https://en.cppreference.com/w/cpp/utility/from_chars

#include <charconv>
#include <iostream>

template <typename Number>
auto stringTo(std::string_view str)
{
    Number number;
    std::from_chars(str.data(), str.data() + str.size(), number);
    return number;
}

int main()
{
    const auto str = std::string("42");
    std::cout << stringTo<long>(str) << '\n';
    std::cout << stringTo<double>(str) << '\n'; 
}

Check the return value of std::from_chars to detect errors.

const auto result = std::from_chars(...);
if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
{
   std::cout << "string to number error" << '\n';
}

More info and examples: https://www.bfilipek.com/2018/12/fromchars.html

GCC and clang don't yet support the floating point version of std::from_chars (August 2019).

Upvotes: 0

Software_Designer
Software_Designer

Reputation: 8587

Another generic template based Numeric To String converter. It takes ints and doubles.

#include <sstream>
#include <iostream>
#include <string>
using namespace std;

template <class T>
inline std::string Numeric_To_String (const T& t)
{
    std::stringstream ss;
    ss << t;
return ss.str();
}


int main(int argc, char *argv[])
{
   int i = 9;
   double d = 1.2345;
   string s;

  cout <<"Generic Numeric_To_String( anyDatatype ) \n\n";

  s = Numeric_To_String( i );
  cout <<"int i to string    : "<< s <<"   "<< endl; 

  s = Numeric_To_String( d );
  cout <<"double d to string : "<< s <<"   "<< endl;
  cout <<" \n";   

  return 0;
}

Upvotes: 1

v01pe
v01pe

Reputation: 1136

If you only have a hand full of types you want to parse, you can use template specialization:

template<>
void Parser<int>::parse(const string&)
{
    result = atoi(string.c_str());
}

template<>
void Parser<float>::parse(const string&)
{
    result = atof(string.c_str());
}

... But this only works if you implement every convertion you need, of course.

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385194

Boost has this functionality built-in:

 #include <boost/lexical_cast.hpp>

 void Parser<T>::get_token() {
     std::string token = ...;
     result = boost::lexical_cast<T>(token);
 }

Add exception handling as required.


Or, perhaps you don't want to use Boost for some reason:

void Parser<T>::get_token() {
     std::string token = ...;

     std::stringstream ss;
     ss << token;
     ss >> result;
}

Check the error state of ss as required.


More expansive answers may be found on this related question, though it discusses only int specifically.

Upvotes: 6

Related Questions