0

I have the following method:

template<typename CharT, typename Traits, typename Alloc>
auto getline_n(std::basic_istream<CharT, Traits>& in, std::basic_string<CharT, Traits, Alloc>& str, std::streamsize n) -> decltype(in)

Full version of the this method included at the bottom.

I need to be able to convert a std::ifstream to a std::basic_istream<CharT, Traits>& so I can pass it to this method.

Here is what the calling code might look like:

std::ifstream pipe2;
pipe2 = std::ifstream {};
pipe2.open("/<pathToNamedPipe>");
std::basic_istream<char,???> s = pipe2;
std::string line{};
getline_n(s, line, 50);

Not really clear on Traits and what they are for std::basic_istream? See ??? above.

Example 2 (because people were asking why I don't just pass pipe2 which was the first thing tried BTW) as in:

std::ifstream pipe2;
pipe2 = std::ifstream {};
pipe2.open("/<pathToNamedPipe>");
std::string line{};
getline_n(pipe2, line, 50);

then I get a Xcode compiler error:

"'std::ifstream' (aka 'basic_ifstream<char>') to 'char **'."

That is why I am trying to convert it to std::basic_istream<char,???>.

Here is the complete method I want to call:

template<typename CharT, typename Traits, typename Alloc>
auto getline_n(std::basic_istream<CharT, Traits>& in, std::basic_string<CharT, Traits, Alloc>& str, std::streamsize n) -> decltype(in) {
    std::ios_base::iostate state = std::ios_base::goodbit;
    bool extracted = false;
    const typename std::basic_istream<CharT, Traits>::sentry s(in, true);
    if(s) {
        try {
            str.erase();
            typename Traits::int_type ch = in.rdbuf()->sgetc();
            for(; ; ch = in.rdbuf()->snextc()) {
                if(Traits::eq_int_type(ch, Traits::eof())) {
                    // eof spotted, quit
                    state |= std::ios_base::eofbit;
                    break;
                }
                else if(str.size() == n) {
                    // maximum number of characters met, quit
                    extracted = true;
                    in.rdbuf()->sbumpc();
                    break;
                }
                else if(str.max_size() <= str.size()) {
                    // string too big
                    state |= std::ios_base::failbit;
                    break;
                }
                else {
                    // character valid
                    str += Traits::to_char_type(ch);
                    extracted = true;
                }
            }
        }
        catch(...) {
            in.setstate(std::ios_base::badbit);
        }
    }

    if(!extracted) {
        state |= std::ios_base::failbit;
    }
    in.setstate(state);
    return in;
}

It originates from this SO post:

How to safely read a line from an std::istream?

The background for this question is that std::getline has a bug that I'm attempting to work around with a custom getline (getline_n) as discussed in this SO Post:

Why does the buffering of std::ifstream "break" std::getline when using LLVM?

bhartsb
  • 1,316
  • 14
  • 39
  • I'm not sure what the issue is. You can read from an `ifstream` as well as you can read from an `istream`. Is `getline_n` doing something that requires an `istream` in particular? – cigien Jun 27 '20 at 12:47
  • @cigien If I attempt to pass pipe2 I get this error: No viable conversion from 'std::ifstream' (aka 'basic_ifstream') to 'char **'. I added the link to a SO post that has this getline_n method. – bhartsb Jun 27 '20 at 12:49
  • Ok, but add that information to the question. Also, add the implementation of `getline_n`; there may be an easier way to address your problem. – cigien Jun 27 '20 at 12:51
  • @cigien added the getline_n implementation. – bhartsb Jun 27 '20 at 12:55
  • Ok, also show exactly how you are trying to call it with `pipe2`, and what error message you get when you do that. – cigien Jun 27 '20 at 12:56
  • @cigien I put it in my comment to you previously but I've now added it to the question as well. – bhartsb Jun 27 '20 at 13:00
  • But how are you calling it exactly? Which argument is `pipe2`? There's no point referring to error messages for code we can't see. – cigien Jun 27 '20 at 13:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/216770/discussion-between-bhartsb-and-cigien). – bhartsb Jun 27 '20 at 13:06
  • Instead of a discussion in chat, provide a [mcve] in the question (preferably a new one) – Asteroids With Wings Jun 27 '20 at 13:27

1 Answers1

1

You don't.

Your std::ifstream is already derived from std::basic_istream<CharT, Traits>.

The function is defined that way so that you could pass a [reference to] another kind of stream instead, if you wanted to.

Absent errors elsewhere in your program, this code already works.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
  • No it doesn't work. See example 2 above and the Xcode LLVM Clang compiler error I'm seeing. – bhartsb Jun 27 '20 at 13:29
  • @bhartsb None of that has anything to do with the code you showed us. You pass an `ifstream` to a function taking a ref to one of its base classes. That's it. Period. If it's not working for you, then your mistake is somewhere _not in the question_. Perhaps another `getline_n` overload that takes `char**`? Either way, the answer to _this_ question is that no you don't convert `ifstream` to that thing; you don't need to. – Asteroids With Wings Jun 27 '20 at 13:31
  • You are correct it was my mistake the template was not being seen in the compilation unit so I was getting that error message which completely threw me off. I had it included below where it was being called from. Your "Perhaps another getline_n overload that takes char**?" tipped me off. – bhartsb Jun 27 '20 at 13:54
  • I don't know why the compiler gives me the error message of example 2 instead of just "Use of undeclared identifier". For whatever reason it doesn't like getline_n(pipe, line, 50); For example if I comment out the template method completely, and just leave that line calling it I get the error. If I remove the third argument (50) I get a different error "Use of undeclared identifier" instead. – bhartsb Jun 27 '20 at 14:11
  • BTW `pipe2 = std::ifstream {};` is completely pointless. Instead of those three lines, just declare your stream like this: `std::ifstream pipe2("");` – Asteroids With Wings Jun 28 '20 at 19:49