Input from an istream to a char* pointer?
Asked Answered
C

2

15

I am reading Bjarne Stroustrup's "Programming Principles and Practice Using C++" (second edition). On page 660-661, the writers define a function as follows:

istream& read_word(istream& is, char* buffer, int max)
    // read at most max-1 characters from is into buffer
{
    is.width(max);    // read at most max-1 characters in the next >>
    is >> buffer;     // read whitespace-terminated word,
                      // add zero after the last character read into buffer
    return is;
}

Later in int main(), the function is called as read_word(cin,s,max); where cin is std::cin, max is an int, and s is a char array of size max.

I don't understand how is >> buffer; works. In particular, that line gives an error when I tried to run the code:

C2679 binary '>>': no operator found which takes a right-hand operand of type 'char *' (or there is no acceptable conversion)

There is no user-defined operator>> or further explanation on that line (except the comment) in the book.

I wonder if we can use things like is >> buffer; in any meaningful way? If so, how does it work? Or is this an error in the book?


Update: I'm using Visual Studio 2022. I found that the code runs successfully with C++14/17, but not with C++20. The results are similar for clang and gcc.

Chaney answered 13/7, 2022 at 5:24 Comment(2)
Code looks good to me. godbolt.org/z/hjz8YjcKe If you add a minimal reproducible example and the complete and unadulterated build error message from the output tab maybe someone will see something I missed.Oogenesis
Ha! I didn't even think to try C++ 20. Didn't expect anyone using Visual Studio would be using it. Last I checked, it defaulted (yes defaulted. Not defecated, you stupid spell checker) to C++14, so I defaulted to 14. Maybe I'm being compiler racist, but I'm not as bad as the spell checker which clearly seems to think Visual studio is caca.Oogenesis
S
22

The std::basic_istream::operator >> overload that takes a char* argument (overload #2 in this cppreference page) was removed in C++20. This change will, no doubt, break huge amounts of existing code, including examples given by Bjarne Stroustrup himself!

The C++20 replacement, taking a char (&s)[N] argument instead, seems (to me, at least) far less accessible. One way to adapt to this change is to rewrite the read_word function as a template, when you would no longer require the explicit max argument and the call to is.width():

template <size_t N>
std::istream& read_word(std::istream& is, char (&buffer)[N])
{
    is >> buffer;
    return is;
}

However, as mentioned in Alan Birtles' answer, you should really be moving away from raw char[] arrays and start using std::string, instead.

Spellbound answered 13/7, 2022 at 5:53 Comment(7)
How much of that existing code was correctly setting width to prevent buffer overflows though? I'm not aware of a single time I've used this overload in my professional career either?Michele
@Alan Agreed. The 'old' operator is dangerous and I can sort of understand why the Standard Committee removed it. (Possibly the C++ 'analogue' of the horrible gets function?)Spellbound
Worth noting that The most recent edition of Principles and Practices is pretty old now. About time for a new rev that covers C++17 and 20 (and maybe 23).Oogenesis
What is this madness? We can suddenly do breaking changes in C++ to fix archaic constructs?Service
@Service there have been a few breaking changes in new c++ revisions, e.g the removal of auto_ptrMichele
@Service I'm pretty sure that there are other changes made at C++20 that break some existing code. Probably also true for other Standard releases, too.Spellbound
Found some code a few weeks back that went nuclear over the C++11 auto change.Oogenesis
M
9

The stream operator taking a raw char pointer has been replaced with one taking a fixed sized char array in c++20. The simplest solution is to use std::string instead.

To use an array your code would be something like:

template <size_t N>
istream& read_word(istream& is, char (&buffer)[N], int max)
    // read at most max-1 characters from is into buffer
{
    is.width(max);    // read at most max-1 characters in the next >>
    is >> buffer;     // read whitespace-terminated word,
                      // add zero after the last character read into buffer
    return is;
}

Note that this will now read at most N-1 characters, if N and max are the same then you can omit max and the width call.

Michele answered 13/7, 2022 at 5:52 Comment(2)
Should max also be of type size_t?Nipissing
@Nipissing streamsize would be the most appropriate en.cppreference.com/w/cpp/io/ios_base/width though strangely setw takes an int en.cppreference.com/w/cpp/io/manip/setwMichele

© 2022 - 2024 — McMap. All rights reserved.