Home > Software design >  Wrapping std::getline()
Wrapping std::getline()

Time:05-25

I am struggling with the problem of reading input from file on a per-line basis, in a cross-platform way.

Different platforms use different sequences of characters to represent a new line/end of line.

std::getline doesn't deal with these in a cross platform way.

What do I mean by this?

  • std::getline changes its behavior depending on the platform on which an executable is compiled. On Windows platforms, it expects to see CRLF to denote line endings. On Linux, it expects just LF.
  • It does not handle cases where a file contains a line ending which is not what the platform expects. For example a file created on a Windows machine is likely to have CRLF line endings. If that file is copied to a Linux machine without changing the line ending format then std::getline "breaks".

It seemed to me that the easiest way to work around this would be to create a new function which wraps std::getline. Something like this:

return_type GetLine(stream_type ifs, string_type s)
{
    return_type ret = std::getline(ifs, s);

    s.erase(std::remove(s.begin(), s.end(), '\r' ), s.end());
    s.erase(std::remove(s.begin(), s.end(), '\n' ), s.end());

    return ret;
}

However at this point I'm stuck. From some searching, although getline returns a stream object (?) it also has an implicit cast-to-bool operator.

I could force return_type to be bool, but then this prevents my wrapper function from returning a stream object, if such a thing were to be required in future.

I also haven't been able to make sense of the STL templates in a sufficient enough way to determine what stream_type and string_type should be. I can force them to be std::ifstream and std::string, but I think this decision would also make the function less generic.

How should I proceed here?

CodePudding user response:

You should take the stream by reference because streams typically cannot be copied. Also the string should be passed by reference because you want to write to it.

To be generic you can use the same interface as std::getline does. As you want to use specific delimiters, they need not be passed as arguments. If you make the function a template then it will work with any stream that also works for std::getline:

#include <iostream>
#include <sstream>
#include <string>

template< class CharT, class Traits, class Allocator >
std::basic_istream<CharT,Traits>& my_getline( 
    std::basic_istream<CharT,Traits>& input,                                               
    std::basic_string<CharT,Traits,Allocator>& str) 
{                                                   
    return std::getline(input,str);
}



int main() {
    std::istringstream s{"hello world"};
    std::string foo;
    my_getline(s,foo);
    std::cout << foo;
}

However at this point I'm stuck. From some searching, although getline returns a stream object (?) it also has an implicit cast-to-bool operator.

It's not getline that converts to bool but the stream returned by getline can be converted to bool. Your line is almost correct, but it needs to be a reference (and you need not spell out the type explicitly):

 auto& ret = std::getline(ifs, s);
 // more code
 return ret;

Note that I didn't address the actual issue of extracting characters until any of the delimiters is encountered (rather than only the platform specific newline that you already get with bare std::getline).

  • Related