2

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?

Community
  • 1
  • 1
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    The only "generic" way to create something like a `from_string` function *is* with string streams, and it's actually how `boost::lexical_cast` works. It does of course rely on the type (`T` whatever that is) to have an appropriate ` operator>>` overload. You might have some specializations for thing like numbers, where e.g. `strtod` or `stoi` are used) but other than that there's no way to escape the clutches of the `istringstream`. – Some programmer dude Apr 12 '16 at 11:39
  • Even if a `from_string` function existed I don't see it doing much of an improvement performance/space wise. Merely a 2-3n extra operations, resulting in an overall O(n). Clean-code wise, you can just encapsulate your own template `from_string` through `sstream`s. – aybassiouny Apr 12 '16 at 12:02
  • @aybassiouny: 1. You mean from_string. 2. An istringstream probably requires some construction 3. An istringstream does its own peeking, tokenization, maintaining the next unused position etc. while in my case I know there's just one value in the whole string. I'm sure there's some performance benefit in that. – einpoklum Apr 12 '16 at 12:04

1 Answers1

0

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

}
einpoklum
  • 118,144
  • 57
  • 340
  • 684
David Haim
  • 25,446
  • 3
  • 44
  • 78
  • Is that a Dangerous Dave image you have for an avatar? – einpoklum Apr 12 '16 at 12:36
  • yep. I'm Dave. and I'm dangerous :) – David Haim Apr 12 '16 at 12:38
  • Wait, your templated parse function will give me a separate istream object for every value of T. That's not so good... how about an untemplated istringstream getter function, which keeps the static thread_local variable? Also, is thread_local C++11? C++14? C++17? – einpoklum Apr 12 '16 at 12:40
  • 1
    it will, but how many string types are you going to use? 2? 4? it's nothing building 4 stringstream objects. and thread_local + magic statics is C++14 – David Haim Apr 12 '16 at 12:41
  • I mean, let's take the worst case scenario when you have four types of strings (string,wstring,u16string,u32string) and you activate this function from 10 different threads. you will build only 40 objects which will take what? 20 nanoseconds? it's really nothing – David Haim Apr 12 '16 at 12:44
  • Nobody said T is a string type, it's can be any type. – einpoklum Apr 12 '16 at 12:47
  • Also, it seems [thread_local is C++11](http://en.cppreference.com/w/cpp/language/storage_duration). – einpoklum Apr 12 '16 at 12:54
  • @DavidHaim Shouldn't `getStream()` have also `stream.clear()`? – ptrj Apr 16 '16 at 15:22
  • @DavidHaim Hmm, it doesn't look so: http://ideone.com/qYGrU7 Or I'm doing something wrong. – ptrj Apr 17 '16 at 19:19