How to avoid repeated istringstream construction when converting stream of string tokens

240 Views Asked by At

I'm (intending to) use the code in this answer to read something from a CSV. Essentially I get an iterator to strings between consecutive , characters; but instead of putting them in a vector of strings, I want to parse these strings into elements of (arbitrary) type T, which comes from template argument. So...

template <typename T>
void foo(const std::string& line) 
{
    // ....
    std::vector<T> vec;
    using namespace boost;
    tokenizer<escaped_list_separator<char> > tk(
       line, escaped_list_separator<char>('\\', ',', '\"'));
    for (tokenizer<escaped_list_separator<char> >::iterator i(tk.begin());
       i!=tk.end();++i) 
    {
       /* magic goes here */
    }

I could use an istringstream` (e.g. as suggested here):

std::istringstream iss(*i);
T t; iss >> t;
vec.push_back(t);

But that's overkill (and I might be constructing twice or even three times here). If C++ had an std::from_string() like its std::to_string, then I would just do

vec.emplace_back(std::from_string(*i));

but that doesn't exist. Perhaps boost::lexical_cast? I'd really rather using something standard.

What should I do instead?

1

There are 1 best solutions below

9
David Haim On

make the istringstream static thread_local

T parse (const string& line){
  static thread_local istringstream stream;
  stream.str(""); //flush the stream
  //keep using stream
}

if your application is mono-threaded, you can discard the thread_local

other solution which does not involve keeping the stream static to a function is to wrap the stream with Parser object, and keep using that object, flushing it's inner buffer with str

class Parser{
  std::stringstream stream;
  public:
  void parse(const std::string& data){
     stream.str("");
     // the rest
  }

}

Parser parser;
parser.parse("my,data");
parser.parse("other,data");

EDIT: In order to prevent instansiation for every T type, encapsulate the stream in diferent function, make a helper function which constructs std::istringstream once form every thread:

namespace detail {
istringstream& getStream(){
      static thread_local istringstream stream;
      stream.str("");
      return stream;
}
} // namespace detail

template<class T>
void parse(){
   auto& stream = detail::getStream();
   //do soemthing with stream

}