Copy streams using rdbuf fails on empty input - c++

It is a well known method to copy a stream into another using rdbuf:
#include <iostream>
#include <fstream>
int main()
{
std::ifstream in{"/tmp/foo.txt"};
std::cerr << in.rdbuf();
std::cerr << "Done\n";
}
However, this breaks (= sets the bad bit) my cerr when /tmp/foo.txt is empty. As a result, Done\n is not displayed.
Why is that? Observed with G++/libstdc++/GNU Linux and Clang++/libc++/OS X.

That seems to be the defined behaviour - see e.g. http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt:
If no characters were inserted, executes setstate(failbit)
I agree it's a bit unhelpful.

Related

Empty file check on std::ofstream

I've been using std::ofstream for writing purposes quite a bit. I open the file, do some operations based on certain conditions and close it.
Let's say that later I want to check if anything is really written into the file or not. There is not is_emtpy() kind of simple check available with std::ofstream.
One way I thought is of using the stat way which is independent of std::ofstream.
Wonder how do everyone else do it?
Standard output streams provide a tellp() method which can be used to determine the current write position. The type and meaning of the return value is implementation-defined, but for it to be useful, it must return distinct values.
#include <iostream>
#include <fstream>
int main()
{
std::ofstream out{"/tmp/test"};
auto const empty_pos = out.tellp();
std::clog.setf(std::ios_base::boolalpha);
std::clog << "At start: " << (out.tellp() != empty_pos) << '\n';
out << 'c';
std::clog << "After writing 1 char: " << (out.tellp() != empty_pos) << '\n';
}
In principle, empty_pos may be different for each stream, so a truly portable program will take that into account.
Note also that this doesn't necessarily mean that the output is visible to other programs - use std::flush if that's important.

How to get rid of no "matching function call error" when iterating over a stream buffer?

I am trying to store binary data that should have the type of a std::complex< float > into a vector, through iterating over each element of the stream buffer. However I keep getting an error saying
no matching function for call to ‘std::istreambuf_iterator<std::complex<float> >::istreambuf_iterator(std::ifstream&)’
std::for_each(std::istreambuf_iterator<std::complex<float> >(i_f1),
I've tried searching for a solution but cannot find anything that would work. I am also trying to follow an example given in How to read entire stream into a std::vector? . Furthermore I'm compiling using g++ and -std=c++11.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cmath>
#include <boost/tuple/tuple.hpp>
#include <algorithm>
#include <iterator>
int main(){
//path to files
std::string data_path= "/$HOME/some_path/";
//file to be opened
std::string f_name1 = "ch1_d2.dat";
std::ifstream i_f1(data_path + f_name1, std::ios::binary);
if (!i_f1){
std::cout << "Error occurred reading file "<<f_name1 <<std::endl; std::cout << "Exiting" << std::endl;
return 0;
}
//Place buffer contents into vector
std::vector<std::complex<float> > data1;
std::for_each(std::istreambuf_iterator<std::complex<float> >(i_f1),
std::istreambuf_iterator<std::complex<float> >(),
[&data1](std::complex<float> vd){
data1.push_back(vd);
});
// Test to see if vector was read in correctly
for (auto i = data1.begin(); i != data1.end(); i++){
std::cout << *i << " ";
}
i_f1.close();
return 0;
}
I am quite lost at what I'm doing wrong, and am thus wondering why the
std::istreambuf_iterator()
does not accept the stream I am giving it as parameter?
Also the error message is confusing me as it seems to imply that I am calling the function in a wrong way, or a function that is non-existent.
Thanks
You want to read std::complex from i_f1 (which is a std::ifstream) using operator>> for std::complex, so you need a std::istream_iterator instead of std::istreambuf_iterator1:
std::for_each(std::istream_iterator<std::complex<float> >(i_f1),
std::istream_iterator<std::complex<float> >(),
[&data1](std::complex<float> vd){
data1.push_back(vd);
});
Your code can actually be simplified to:
std::vector<std::complex<float>> data1{
std::istream_iterator<std::complex<float>>(i_f1),
std::istream_iterator<std::complex<float>>()};
1 std::istreambuf_iterator is used to iterate character per character on, e.g., a std::basic_istream, not to iterate over it using overloads of operator>>.
You're probably using the wrong tool for the job.
You're trying to use a buffer iterator, which iterates over the constituent parts of a stream's buffer. But you're telling your computer that the buffer is one of complex<float>s … it isn't. An ifstream's buffer is of chars. Hence the constructor you're trying to use (one that takes an ifstream with a buffer of complex<float>) does not exist.
You can use an istream_iterator to perform a formatted iteration, i.e. to use the stream's magical powers (in this case, lexically interpreting input as complex<float>s) rather than directly accessing its underlying bytes.
You can read more on the previous question "the difference betwen istreambuf_iterator and istream_iterator".
The example you linked to does also go some way to explaining this.

Why does stream insertion of an empty streambuf fail?

I was using the simple file-slurp and I decided to add some error checking. I was surprised that an empty file gives an error. This doesn't happen with every empty sequence either, "" works fine. I also verified that rdbuf() is returning a non-null pointer.
#include <iostream>
#include <sstream>
using namespace std;
int main(int, char**){
istringstream in(""); // Succeeds if non-empty
stringstream sstr;
if (sstr << in.rdbuf()) { // Succeeds if I replace in.rdbuf() with ""
cout << "Read Successful\n";
}else{
cout << "Read Failed\n";
}
}
It sets failbit because the standard requires it. (I feel foolish now. I thought I might have been doing something wrong.) Section 27.7.3.6.3.9 of the November 2014 Draft says:
If the function inserts no characters, it calls setstate(failbit) (which may throw ios_base::failure (27.5.5.4)).
Why the committee decided to make this behavior different from other sequence insertions (like char* and string) which do not consider insertion of nothing to be a failure is still a mystery. But I now know it is not a failure indicating misuse of the object.

What happened to << when using ofstream without any filename pointed?

(1) default constructor
Constructs an ofstream object that is not associated with any file.
Internally, its ostream base constructor is passed a pointer to a newly constructed filebuf object (the internal file stream buffer).
what happened to << when using ofstream without any filename pointed?
ofstream ofstream;
ofstream<<1<<endl;
where is the "1" go? is there any problems? I tried it, no issues. but I can't found any code clue for this, can anybody show the internal code explain for it?
Nothing happens.
[C++11: 27.9.1.1/3]: In particular:
If the file is not open for reading the input sequence cannot be read.
If the file is not open for writing the output sequence cannot be written.
A joint file position is maintained for both the input sequence and the output sequence
The stream is closed, an error flag is set and the data is ignored.
Example:
#include <iostream>
#include <fstream>
int main()
{
std::ofstream ofs;
ofs << 1 << std::endl;
std::cout << ofs.good() << std::endl;
}
// Output: 0
Live demo
The short version: the operations on the ofstream all fail, causing nothing to happen. The data that you write is lost and not stored anywhere, and the failbit will be set, causing the stream's fail() member function to return true.
The long version: when an ofstream is constructed without specifying a file, it default-constructs a filebuf. This creates a filebuf where is_open evaluates to false. As part of the stream insertion operation, the data to be written will need to be sent to the disk by calling filebuf::overflow, which, since is_open is false, will return EOF, causing the operation to fail.
Hope this helps!

Why does std::copy (from istream to ostream) raises an ios::failure exception?

The following code should copy data from an wifstream to wcout.
After the content is copied, the program throws a ios::failure exception.
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <locale>
#include <iterator>
#include <algorithm>
int main(void)
{
std::locale::global(std::locale(""));
std::wifstream is;
is.exceptions( std::ios::failbit | std::ios::badbit );
is.open("test.ts", std::ios::binary);
is >> std::noskipws;
std::istream_iterator<wchar_t, wchar_t> in(is);
std::istream_iterator<wchar_t, wchar_t> end;
std::copy(in, end,
std::ostream_iterator<wchar_t, wchar_t>(std::wcout));
return 0;
}
The stream should only throw an exception (see exception mask) if anything goes bad, but not on EOF.
To avoid skipping white space use the std::istreambuf_iterator
std::copy(std::istreambuf_iterator<wchar_t, wchar_t>(is),
std::istreambuf_iterator<wchar_t, wchar_t>(),
std::ostream_iterator<wchar_t, wchar_t>(std::wcout));
The exception:
The local may be using codecvt facet that is failing.
Try commenting out the locale line see what happens.
Have you tried to print what the exceptions is?
try
{
// do work
}
catch(std::exception const& e)
{
std::cout << e.what() << "\n";
}
Because you're using std::istream_iterator, the attempt to read a character past the end of the stream sets both eofbit and failbit (and only after some error bits are set, does the iterator become equal to the end iterator)
Stripping to bare essentials and reverting to char to make it even simpler, program is equivalent to:
#include <iostream>
#include <fstream>
int main()
{
std::ifstream is("test.txt", std::ios::binary);
is.exceptions(std::ios::failbit); // failbit only because that's what you get
is >> std::noskipws;
if(is)
for(char c; is >> c;) // will throw!
std::cout << c;
}
According to §27.6.1.2.3/10:
After a sentry object is constructed a character is extracted from in, if one is available, and stored in c. Otherwise, the function calls in.setstate(failbit).
So, when it reaches the end of the file and can no longer extract a character, it will set the fail bit, which you've set to produce an exception. Using std::copy doesn't change the behavior -- an istream_iterator reads via operator>>.
You can copy the file a bit more easily:
std::wifstream is("test.ts", std::ios::binary);
std::wcout << is.rdbuf();