The problem is that std::fstream doesn't throw exceptions by default but rather sets bits that can then be examined. Apparently, it can then be made to throw exceptions (I think) by using the exceptions function as explained here -- see e.g. c++ reference page
But what if, for example, the write permissions of the file mean that the file cannot be written to? This would mean that when I try and do
ofstream file("file", ios::out);
a failure would result. But how can it be determined if it failed for the precise reason that the uid didn't have write permissions? I guess what I am looking for is some mechanism that will tell me precisely this e.g. it might show "File cannot be written to because...". I do not want to have to check file permissions because there are so many reasons why write failure could occur (hard-drive failures/corruption etc.). It would be better if it would just tell me exactly why it failed.
Does anybody know if such an error checking system exists for iostreams (ostreams in particular)? (Maybe in boost?)
You can try to check errno and perhaps convert it to a human-readable form with strerror.
The standard doesn't guarantee anything about applicability of errno to I/O failures, but in practice it should work.
Related
For example, in cppreference.com, it says operator>>(std::basic_string) can throw exceptions on these conditions.
may throw std::ios_base::failure if no characters are extracted
from is (e.g the stream is at end of file, or consists of whitespace
only), or if an exception is thrown during input.
But how can I know what really cause that exception, like ENOMEM, EINTR in C style errno? Does std::ios_base::failure::what and std::ios_base::failure::code tell me that? If it is, where can I find documentation about what exact error code is set in what exact situation? For instance, I want something like POSIX documentation on scanf, which contains a list of error numbers and corresponding error conditions.
This recently asked question has raised another interesting issue, as discussed in the comments to one of its answers.
To summarize: the OP there was having issues with code like that below, when subsequently attempting to read and write data from/to the two streams 'concurrently':
ifstream infile;
infile.open("accounts.txt");
ofstream outfile;
outfile.open("accounts.txt");
Although the issue, in itself, was successfully resolved, it has raised a question to which I cannot find an authoritative answer (and I've made some quite extensive searches of Stack Overflow and the wider web).
It is very clearly stated what happens when calling the open() method of a stream that is already associated with a file (cppreference), but what I cannot find an answer to is what happens when (as in this case) the file is already associated with a (different) stream.
If the stream is already associated with a file (i.e., it is already
open), calling this function fails.
I can see several possible scenarios here:
The second open call will fail and any attempted writes to it will also fail (but that is not the case in the cited question).
The second open call will 'override' the first, effectively closing it (this could explain the issues encountered in said code).
Both streams remain open but enter into a 'mutual clobbering' match regarding their internal file pointers and buffers.
We enter the realm of undefined (or implementation-defined) behaviour.
Note that, as the first open() call is made by an input stream, the operating system will not necessarily 'lock' the file, as it probably would for an output stream.
So, does anyone have a definitive answer to this? Or a citation from the Standard (cppreference will be 'acceptable' if nothing more authoritative can be found)?
basic_filebuf::open (and all things that depend on it, like fstream::open) has no statement about what will happen in this case. A filesystem may allow it or it may not.
What the standard says is that, if the file successfully opens, then you can play with it in accord with the interface. And if it doesn't successfully open, then there will be an error. That is, the standard allows a filesystem to permit it or forbid it, but it doesn't say which must happen. The implementation can even randomly forbid it. Or forbid you from opening any files in any way. All are (theoretically) valid.
To me, this falls even out of the 'implementation defined' field. The very same code will have different behaviour depending of the underlying filesystem or OS (some OSes forbid to open a file twice).
No.
Such a scenario is not discussed by the standard.
It's not even managed by the implementation (your compiler, standard library implementation etc).
The stream ultimately asks the operating system for access to that file in the desired mode, and it's up to the operating system to decide whether that access shall be granted at that time.
A simple analogy would be your program making some API call to a web application over a network. Perhaps the web application does not permit more than ten calls per minute, and returns some error code if you attempt more than that. But that doesn't mean your program has undefined behaviour in such a case.
C implementations exist for many different platforms, whose underlying file systems may handle such corner cases differently. For the Standard to mandate any particular corner-case behavior would have made the language practical only on platforms whose file systems behave in such fashion. Instead, the Standard regards such issues as being outside its jurisdiction (i.e. to use its own terminology, "Undefined Behavior"). That doesn't mean that implementations whose target OS offers useful guarantees shouldn't make such guarantees to programs when practical, but implementation designers are presumed to know more than the Committee about how best to serve their customers.
On the other hand, it may sometime be helpful for an implementation not to expose the underlying OS behavior. On an OS that doesn't have a distinct "append" mode, for example, but code needing an "open for append" could do an "open existing file for write" followed by "seek to end of file", an attempt to open two streams for appending to the same file could result in data corruption when one stream writes part of a file, and the other stream then rewrites that same portion. It may be helpful for an implementation that detects that condition to either inject its own logic to either ensure smooth merging of the data or block the second open request. Either course of action might be better, depending upon an application's purpose, but--as noted above--the choice is outside the Standard's jurisdiction.
I open the zip file as stream twice.The zip file contains some XML files.
std::ifstream("filename") file;
zipstream *p1 = new zipstream(file);
zipstream *p2 = new zipstream(file);
p1->getNextEntry();
auto p3 = p1.rdbuf();
autp p4 = p2.rdbuf();
Then see p3 address = p4 address, but the member variables are different between them. Such as _IGfirst.
The contents of one of the XML files are as follows:
<test>
<one value="0.00001"/>
</test>
When the contents of file are read in two thread at the same time.error happend.
string One = p1.getPropertyValue("one");
// one = "0001two"
The "standard" offered way to copy files, for example through c++ is.
ifstream ins;
ofstream out s;
// I've omitted the opening of those stream, since it is not important for the following question.
s << ins.rdbuf();
Now, the problem is that the above line may fail, and in some cases, especially the one I'll talk about, there is no way I found to know that something went wrong.
Assume the input stream is actually a large file sitting on remote NAS machine, for example.
Now, there may be connetion errors which will cause the file handle to be invalid.
Trying to simulate this, I used a large file, in debugger I stopped on this line, found the handle, then continued the debugger and then closed the file handle forcibly (through process-explorer of the sysinternals suite).
The result is: the line finished, the file is not being copied correctly, sometimes it is not copied at all, sometimes only part of the file is being closed.
In contradiction to the documentation of the operator<<, and the rdbuf(), there is no setting of bad/fail state, and no exception is being thrown.
The only thing I could notice, is that when trying to close the input-stream, an exception is being raised.
Did anyone so the same phenomenon ?!
Is there anyway to check/test this for correct completion ?
Regards,
Streams are notoriously bad with regards to error reporting. In this
case, all you can do is check for badbit on the output stream; the
standard requires the << operators to absorb all exceptions, setting
the badbit if they occur. And obviously, this doesn't tell you
whether the error was due to an error on input or an error on output.
Typically, if you're using the implementations which come with most
compilers, there won't be an error reported on input anyway. For the
most part, unless things have changed in recent years, implementations
of filebuf treat errors on input exactly like end of file.
If you need better error reporting, I think you're stuck with
implementing your own streambuf (not really very difficult), which
keeps track of the various errors; you can then ask the streambuf
after the transfer what errors it received.
What is the best way to get meaningful file access error messages, in a portable way from std::fstreams ? The primitiveness of badbits and failbits is getting to be bit annoying. I have written my own exception hierarchies against win32 and POSIX before, and that was far more flexible than the way the STL does it.
I am getting "basic::ios_clear" as an error message from the what method of a downcasted catch (std::exception) of a fstream which has exceptions enabled. This doesn't mean much to me, although I do know what the problem is I'd like my program to be a tad more informative so that when I start deployment a few months later my life will be easier.
Is there anything in Boost to extract meaningful messages out of the fstream's implementation cross platform and cross STL implementation ?
Nobody stops you from also checking errno/strerror (e.g. in your exception handler) for a more specific reason for failure.
UPDATE -- regarding portability
Incidentally, IIRC Visual Studio's fstream implementation calls the _open/_read/_write/etc. CRT methods, which set errno. Microsoft makes no guarantee about GetLastError still containing the correct value after the CRT methods return. Idem for the cygwin, mingw etc. implementations, which set errno with no claims or guarantees about GetLastError.
So I stand by my claim that all you need, can, and therefore want to do is check errno.
Now, given all of the above, if you still want to complicate your life and overengineer by using Boost::System instead of simply calling strerror then I guess my definition and your definition of elegance and simplicity are not the same. :)
What information do you want? badbit indicates an I/O error. eofbit indicates eof. failbit indicates a parse error.
To eliminate one solution, anyway, I don't think you can override the native-type input functions because of ADL. You could implement operator>>(istream, input_safe_int) where input_safe_int is constructed from int&. Put a try block inside, etc.
I've had luck catching the std::ios_base::failure and then re-raising a std::system_error using errno:
terminate called after throwing an instance of 'std::system_error'
what(): broken/path: No such file or directory
#include <fstream>
int main() {
const std::string filename{ "broken/path" };
try {
std::ifstream file{ filename };
file.exceptions(std::ios::failbit); // std::ios_base_failure is thrown here
} catch (std::ios_base::failure&) {
throw std::system_error{ errno, std::generic_category(), filename };
}
}
This works on UNIX and Windows because "All errno values are … UNIX-compatible" (source).
I've run into a bit of an odd circumstance while using boost::filesystem::exists. If you attempt to check for the existance of a file on a drive that isn't ready or has no media in it, it throws a basic_filesystem_error. As far as I'm concerned for most all uses of bfs::exists, if a drive isn't ready it means the file doesn't exist.
I can wrap my calls with a try-catch to properly handle this condition, however it becomes a bit cumbersome and makes the code a bit clunky. And worse, it means I'm using a special case of basic_filesystem_error for flow control, which means if a different reason for that exception were to arrise, I wouldn't be handling it properly anymore.
The general case where this comes up is if I attempt to check if a file exists on a CD or DVD drive. My code which used to be:
if( bfs::exists( myFilePath ) )
{
...
}
Becomes:
bool fileExists( false );
try
{
fileExists = bfs::exists( myFilePath );
}
catch( bfs::basic_filesystem_error<bfs::path> e )
{
fileExists = false;
}
if( fileExists )
{
...
}
I'm not overly enamored with the thought of making this change all over the place in my existing code base.
I'm considering making a seperate function somewhere that wrapps up the try-catch and replacing my bfs::exist calls with that, but I'm still not satisfied that using the try-catch in that manner is a good idea. It seems like I'm opening the door for missing more important and relevant exceptional conditions.
I'm aware that you can recompile boost for a non-throwing version of the function, but I don't think that really avoids my exception handling concerns.
Has anyone run into this problem with removable media drives before, and if so how did you overcome it?
According to the documentation, exists(file_status s) returns "status_known(s) && s.type() != file_not_found".
The documentation also states that:
If the underlying file system reports an error during [file_status] attribute determination:
If the error indicating that p could not be resolved, as if by POSIX errors ENOENT [i.e., not found] ... return file_status(not_found_flag).
It seems to me that throwing an exception is not the intended behavior. (when you create the status object its status is known, and that status is not_found).
However, the documentation continues by saying:
[Note: The effect of this behavior is to distinguish between knowing that p does not exist, and not being able to determine the status of p. This distinction is important to users. --end note]
Which implies that the library does intend to make a distinction between "file does not exist" and "I can't determine that file does not exist." You may wish to contact the library's authors for a clearer statement.
However, testing for the existence of a file is a race condition: the file may have existed when the OS looked, but there is no guarantee that it will continue to exist; likewise, the file may not have existed when the OS looked, but there is no guarantee that it will continue not existing. The race condition can have security implications.
Instead, open the file and then see what its attributes are. Once the file is open, the OS makes certain guarantees about what will change and what won't.
It's a bug, probably related to:
https://svn.boost.org/trac/boost/ticket/2725
Are you using the latest Boost version?
If yes, report another bug there. See:
http://www.boost.org/support/bugs.html
I removed this upset finally by recompiling boost, and relink the updated filesystem .lib files to my project