Suppose I wanted operator>> to extract entire lines from an istream instead of whitespace-separated words. I was surprised to see that this, although horrible, actually worked:
#include <iostream>
#include <string>
namespace std {
istream &operator>>(istream &is, string &str) {
getline(is, str);
}
}
int main() {
std::string line;
std::cin >> line;
std::cout << "Read: '" << line << "'\n";
}
If I type multiple words into stdin, it actually does call my operator and read an entire line.
I would expect this definition of operator>> to conflict with the official one, producing a link error. Why doesn't it?
Edit: I thought maybe the real operator>> was a template, and non-template functions take precedence, but this still works just as well:
namespace std {
template<typename charT>
basic_istream<charT> &operator>>(basic_istream<charT> &is, basic_string<charT> &s) {
getline(is, s);
}
}
It happens to work because the official template is less specific (there are additional template arguments).
However it is Undefined Behaviourâ„¢. You are only permitted to provide overloads of standard library symbols for your own types. If you come across another standard library that will define extra overloads (it may), it will stop working and you won't know why.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Im trying to read from a file called stock.txt, which contains the following values:
ID, Item, Colour, Size, Quantity, Price
11,T-shirt,blue,XL,2,10.500000
12,Supreme,red,M,10,20.500000
13,BANG,red,M,10,20.500000
I wanted to store each item in a list, how can I do that?
int main() {
ifstream infile;
infile.open("Stock.txt");
string id; string title; string colour; string size; string quantity; string cost;
//If file open is successful
while(infile.good()){
getline(infile,id,',');
getline(infile,title,',');
getline(infile,colour,',');
getline(infile,size,',');
getline(infile,quantity,',');
getline(infile,cost,'\n');
}
infile.close();
}
You should use a more moden C++ approach.
I would be happy, if you could study this solution and try to use some features in the future.
In the object orient world, we use classes (or structs) and put data and functions, operating on this data, in one (encapsulated) object.
Only the class should know, how to read and write its data. Not some outside global functions. Therefor I added 2 member functions to your struct. I have overwritten the inserter and the extractor operator.
And in the extractor, we will use modern C++ algorithms, to split a string into tokens. For this purpose, we have the std::sregex_token_iterator. And because there is a specialized function for this purpose, we should use it. And besides, it is ultra simple.
With the below one-liner, we split the complete string into tokens and put the resulting tokens in a std::vector
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
Then we copy the resulting data in our member variables.
For demo output I have also overwritten the inserter operator. Now you can use the exteractor and inserter operators (">>" and "<<") for variables of type Stock, as for any other C++ integral variable.
In main, we use also an ultrasimple approach. First, we open the file and check, if this was OK.
Then we define a variable "stocks" (A std::vector of Stock) and use its range constructor and the std::istream_operator to read the complete file. And, since the App has an overwritten extractor operator, it knows, how to read and will parse the complete CSV file for us.
Again, the very simple and short one-liner
std::vector stocks(std::istream_iterator<Stock>(inFile), {});
will read the complete source file, all lines, parse the lines and store the member variables in the single stock elements of the resulting std::vector.
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <regex>
#include <iterator>
#include <algorithm>
std::regex delimiter{ "," };
struct Stock {
// The data. Member variables
std::string id{};
std::string title{};
std::string colour{};
std::string size{};
std::string quantity{};
std::string cost{};
// Overwrite extractor operator
friend std::istream& operator >> (std::istream& is, Stock& s) {
// Read a complete line
if (std::string line{}; std::getline(is, line)) {
// Tokenize it
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
// If we read at least 6 tokens then assign the values to our struct
if (6U <= token.size()) {
// Now copy the data from the vector to our members
s.id = token[0];
s.title = token[1];
s.colour = token[2];
s.size = token[3];
s.quantity = token[4];
s.cost = token[5];
}
}
return is;
}
// Overwrite inserter operator
friend std::ostream& operator << (std::ostream& os, const Stock& s) {
return os << "ID: " << s.id << "\nTitle: " << s.colour
<< "\nSize: " << s.size << "\nQuantity: " << s.quantity << "\nCost: " << s.cost;
}
};
int main() {
// Open file and check, if it could be opened
if (std::ifstream inFile("stock.txt"); inFile) {
// Define the variable and use range constructor to read and parse the complete file
std::vector stocks(std::istream_iterator<Stock>(inFile), {});
// Show result to the user
std::copy(stocks.begin(), stocks.end(), std::ostream_iterator<Stock>(std::cout, "\n"));
}
return 0;
}
Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
You should model each row with a class or struct:
struct Record
{
int id;
std::string title;
std::string colour;
std::string size;
int quantity;
double price;
friend std::istream& operator>>(std::istream& input, Record & r);
};
std::istream& operator>>(std::istream& input, Record & r)
{
char comma;
input >> r.id;
input >> comma;
std::getline(input, r.title, ',');
std::getline(input, r.colour, ',');
std::getline(input, r.size, ',');
input >> r.quantity;
input >> comma;
input >> r.price;
input.ignore(100000, '\n');
return input;
}
Now you can read into a list:
std::list<Record> database;
Record r;
while (infile >> r)
{
database.push_back(r);
}
The overloading of operator>> makes the code simpler and easier to read.
My question would have a boolean answer: yes or not. Whichever it would be, can someone explain how the following code is compiled by both GNU-g++ 4.9.2 and clang 3.5, while GNU-g++ 5.1.1 no longer accepts it, claiming that there is no matching operator==?
And how it could be changed, for this last compiler, in order to have the same results i.e. to have the operator>> able to distinguish, in such a simple way, whether
it is called by the standard input stream or by something else?
# include <iostream>
# include <fstream>
struct S {};
std::istream& operator >> (std::istream& i, S& s)
{
if(i == std::cin) std::clog << "this is standard input\n";
else std::clog << "this is some other input stream\n";
return i;
}
int main(int narg, const char ** args)
{
S s;
std :: cin >> s;
std::ifstream inp(args[1]);
inp >> s;
}
// to be executed with the name of an existing
// disk file on the command line....
No. There's no operator== that operates on std::istream objects.
Your being able to compare two std::istreams is an unfortunate consequence caused by the conversion function of std::istream, specifically operator void*. In the expression i == std::cin, both i and std::cin are implicitly converted to void*, and the resulting values are compared. This is not really meaningful. This conversion function was removed in C++11 (replaced by an explicit conversion function, which won't be called in this situation), so if you enable the C++11 mode, the code will not compile.
As is stated here, if you want to check whether the reference i refers to std::cin, you can use &i == &std::cin.
Standard C++ streams don't have ==, >, < operators because they are not very meaningful in that context: what should "stream a is > than stream b" mean?
However, it shouldn't matter what type of istream you're dealing with: as long as you're not using specific methods defined in descendant classes (like is_open), base methods are shared (extracting, for instance). Whether you're extracting a string from a istringstream or a ifstream, you just do in >> str and let polymorphism take action.
If you really want to know the polymorphic type of the istream, you can use typeid or, simply, function overloading. In your case, with RTTI(typeid)
if ( typeid(in) == typeid(std::cin) )
// cin
else
// another istream type
Using overloading:
std::istream& operator >> (std::istream& i, S& s)
{
std::clog << "this is standard input\n";
return i;
}
std::ifstream& operator>>(std::ifstream& i, S& s)
{
std::clog << "this is some file input stream\n";
return i;
}
I am overloading my istream operator, so I can replace std::cin with my object. I know I will have to feed it an empty stringstream for the final output to work.
I would like to feed an std::ifstream into a std::stringstream as so:
while(ifs >> ss) {}
Is this possible? Here is an example prototype code:
friend istream & operator >> (istream & is, Database & db)
{
ifstream ifs;
ifs.open(db.inputFilename_, ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "Couldn't read " << db.inputFilename_ << endl;
return is;
}
while (ifs >> db.iss)
{}
ifs.close()
return db.iss;
}
I am not interested in any answers that start with "use Boost" :) This is a purely standard C++ project. Thank you for any help or pointers.
Right now I am getting:
error: invalid operands to binary expression ('ifstream' (aka 'basic_ifstream<char>') and 'istringstream' (aka 'basic_istringstream<char>'))
Simply do this:
if(ifs){
db.iss << ifs.rdbuf();
ifs.close();
}
You can use std::copy with an std::istream_iterator for std::cin and std::ostream_iterator for the std::stringstream.
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>
void redirect(std::ifstream &is, std::stringstream &os) {
is >> std::noskipws;
std::istream_iterator<char> begin(is);
std::istream_iterator<char> end;
std::ostream_iterator<char> out(os);
std::copy(begin, end, out);
}
Note that this copies the entire file into the std::stringstream, and thus for really large files that can't fit in memory, this will fail. The rdbuf solution that NaCl gave will similarly have an issue with very large files.
You can solve the large file problem by not reading all of the input at once. However, this will most likely require you to restructure your code inside your parsers. Without more detail on their implementations, I can't point you in the right direction.
Learning C++ with help of Bruce Eckel "Thinking in C++". Stuck in exercise 05 of chapter "Iostreams":
Text of exercise
We know that setw( ) allows for a minimum of characters read in, but what if you wanted to read a
maximum? Write an effector that allows the user to specify a maximum number of characters to
extract. Have your effector also work for output, in such a way that output fields are truncated, if
necessary, to stay within width limits.
I understand how to create manipulators both without and with parameter (which one is called effectors in the book terminology). But do not understand how to limit maximum number of characters to extract. std::ios_base::width specifies the minimum number of characters.
Shoud I do some tricks with underlying streambuf object?
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
using namespace std;
class fixW{
char* chrP;
char str[1024];
size_t Max;
public:
fixW(char* p,size_t m=25):chrP(p),Max(m){}
friend istream& operator >>(istream& is,fixW fw){
is >>fw.str;
size_t n=strlen(fw.str);
cout <<" n= "<<n << endl;
if(n>=25){
fw.str[fw.Max]='\0';
}
strcpy(fw.chrP,fw.str);
return is;
}
friend ostream& operator<<(ostream& os, fixW fw){
for(size_t i= 0; i<fw.Max; ++i){
fw.str[i] = fw.chrP[i];
}
fw.str[fw.Max]='\0';
return os <<fw.str;
}
};
int main(){
char s[80];
cin >> fixW(s,25);
cout << s << endl;
cout << fixW(s,10)<<endl;
cout << s <<endl;
return 0;
}
Its not a perfect solution (but I can't think of another way at the moment without reading the iostream library).
Say you manipulator is:
class MaxFieldSize {/*STUFF*/};
When you write the stream operator(s) you write a slightly funky one that does not return an actual stream (but rather returns a stream with a wrapper around it).
MaxFieldWdithStream operator<<(std::ostream&, MaxFieldSize const& manip);
Now you overload all the stream operator of this class to truncate their input before returning a normal stream object.
class MaxFieldWithStream { std::ostream& printTruncatedData(std::string& value);};
Then all you need is the generic overloads:
template<typename T>
std::ostream& operator<<(MaxFieldWithStream& mfwstream, T const& value)
{
std::stringstream trunStream;
trunStream << value;
return mfwstream.printTruncatedData(trunStream.substr(0, mfwstream.widthNeeded));
}
// You will probably need another overload for io-manipulators.
I would also add a conversion operator that converts MaxFieldWithStream to std::iostream automatically so that if it is passed to a function it still behaves like a stream (though it will loose its max width property).
class MaxFieldWithStream
{
std::ostream& printTruncatedData(std::string& value);};
operator st::ostream&() const { return BLABLAVLA;}
};
In the following code:
using namespace std;
//ostream& operator<< (ostream& out,const string & str)
//{
// out << str.c_str();
// return out;
//}
int _tmain(int argc, _TCHAR* argv[])
{
ofstream file("file.out");
vector<string> test(2);
test[0] = "str1";
test[1] = "str2";
ostream_iterator<string> sIt(file);
copy(test.begin(), test.end(), sIt);
file.close();
return 0;
}
what is the proper way to overload operator << to make
copy(test.begin(), test.end(), sIt); work.
What am I missing?
EDIT: I'm just stupid... forgot to include "string" header
Thank you!
You do not need to overload operator<< to work with strings, it already knows how to handle them.
std::copy( test.begin(), test.end(),
std::ostream_iterator<std::string>( file, "\n" ) );
will produce:
str1
str2
Is there anything different/special that you want to be done there?
As David already pointed out, there is already a operator<< for strings, so you don't have to provide one. Should you really want to define your own overload anyway, then there's a slight problem, because actually you are not allowed to do that. operator<< is defined in the std namespace, so if you want to have a usable overload for std::string (the version in most implementations is a template function, so there is a potential overload), you'll have to do this in the std namespace too (this because of the ways ambiguities and overloads are resolved in C++, there are some caveats here) . For example:
namespace std {
ostream& operator<< (ostream& out,const string & str)
{
out << "A STRINGY:" << str.c_str();
return out;
}
}
However, adding stuff into the std namespace is not allowed for ordinary users, because it might have implementation specific effects that are unforeseeable and could break all kinds of stuff inside the standard library. Also, there is no guarantee, that your implementation of the standard library has a overloadable operator<<. This means, it could work or it could not.
Lemme just add the link from cplusplus.com for future reference
http://www.cplusplus.com/reference/algorithm/copy/