Different errors opening a file stream

G

Grumble

Hello all,

According to P.J. Plauger's C++ library reference,

std::ifstream ifs(filename);

will initialize several objects, then call

sb.open(filename, ios_base::in);

which calls cstdio's fopen().

As far as I understand, fopen() can fail for many different reasons:

EINVAL or any of the errors specified for open() i.e. EEXIST, EISDIR,
EACCES, ENAMETOOLONG, ENOENT, ENOTDIR, ENXIO, ENODEV, EROFS, ETXTBSY,
EFAULT, ELOOP, ENOSPC, ENOMEM, EMFILE, ENFILE (if my documentation is
correct) or any of the errors specified for malloc().

If I understand correctly, I can test for failure with

if (ifs == NULL) handle_error();

How can I tell the user precisely why I couldn't open his file?
Is errno saved somewhere inside the file stream object?

(Perhaps I should have asked in comp.unix.programmer? I'm not sure.)
 
R

Rob Williscroft

Grumble wrote in in comp.lang.c++:
Hello all,

According to P.J. Plauger's C++ library reference,

std::ifstream ifs(filename);

will initialize several objects, then call

sb.open(filename, ios_base::in);
which calls cstdio's fopen().

This is implementation defined, i.e std::fstream's aren't required
to use C's stdio FILE's. You can't portably rely on it.
As far as I understand, fopen() can fail for many different reasons:

EINVAL or any of the errors specified for open() i.e. EEXIST, EISDIR,
EACCES, ENAMETOOLONG, ENOENT, ENOTDIR, ENXIO, ENODEV, EROFS, ETXTBSY,
EFAULT, ELOOP, ENOSPC, ENOMEM, EMFILE, ENFILE (if my documentation is
correct) or any of the errors specified for malloc().

Again fopen() isn't required to use open().

If I understand correctly, I can test for failure with
if (ifs == NULL) handle_error();

This will work, but:

if ( ! ifs ) handle_error();

or:

if ( ! ifs.good() ) handle_error();

is better (the ifs *object* isn't a pointer).
How can I tell the user precisely why I couldn't open his file?
Is errno saved somewhere inside the file stream object?

Maybe, but again this would be implementation defined, so
you can't portably rely on it.
(Perhaps I should have asked in comp.unix.programmer? I'm not sure.)

If having your code *only* run on unix/posix systems is Ok then
this is a good idea.

What isn't specified is how std::fstream's interact with the
standard unix features.

A workaround:

#include <fstream>
#include <string>

std::string why_cant_i_read( std::string const &filename );

int main()
{
std::ifstream( filename );
if ( ! ifs )
{
std::string msg = why_cant_i_read( filename );
handle_error( msg );
}
}

#include "unix-stuff"

std::string why_cant_i_read( std::string const &filename )
{
// do the unix/posix only stuff here

int fd = open( filename.c_str(), O_RDONLY, 0 ) );
if ( fd >= 0 )
{
close( fd );
return "duno' works here";
}

switch ( errno )
{
case EACCES:
return "Access Denied";
// ...
}
return "Unknown Error";
}

The above is far better than trying to muck about with
std::fstream, should you ever need to port your code to
a non-unix platform, you have just the one function to
rewrite.

HTH.

Rob.
 
T

tom_usenet

Hello all,

According to P.J. Plauger's C++ library reference,

std::ifstream ifs(filename);

will initialize several objects, then call

sb.open(filename, ios_base::in);

which calls cstdio's fopen().

As far as I understand, fopen() can fail for many different reasons:

EINVAL or any of the errors specified for open() i.e. EEXIST, EISDIR,
EACCES, ENAMETOOLONG, ENOENT, ENOTDIR, ENXIO, ENODEV, EROFS, ETXTBSY,
EFAULT, ELOOP, ENOSPC, ENOMEM, EMFILE, ENFILE (if my documentation is
correct) or any of the errors specified for malloc().

In POSIX, yes, but not in standard C. fopen might not set errno at all
on failure on some platforms.
If I understand correctly, I can test for failure with

if (ifs == NULL) handle_error();

How can I tell the user precisely why I couldn't open his file?
Is errno saved somewhere inside the file stream object?

No, but you can usually query errno in a platform specific manner for
more information. e.g.

if (!ifs)
{
switch(errno) //or use GetLastError() in Win32?
{
case EEXIST:
etc...
}
//or use strerror, perror, etc.
}

That's not guaranteed to give you useful information, but in practice
constructing an fstream leaves errno in the relevent state.

Tom
 
G

Grumble

Rob said:
This will work, but:

if ( ! ifs ) handle_error();

or:

if ( ! ifs.good() ) handle_error();

is better (the ifs *object* isn't a pointer).

I know ifs is not a pointer; std::ifstream must define (or inherit)
operator==(), otherwise a compile-time error would occur.

Unless I am mistaken, "p == NULL" and "!p" are syntactically equivalent
for plain pointers. Are operator!() and operator==() defined in such a
way that "ifs == NULL" and "!ifs" are not equivalent?

FWIW, the codes generated by gcc-3.2.2 on x86 are slightly different:

call 80489a4 <_init+0x28>
add $0x10,%esp
test %eax,%eax
jne 8048c47 <_Z7myfunc1Pc+0x53>

call 8048a14 <_init+0x98>
add $0x10,%esp
test %al,%al
je 8048cb1 <_Z7myfunc2Pc+0x53>
 
R

Rob Williscroft

Grumble wrote in in comp.lang.c++:
I know ifs is not a pointer; std::ifstream must define (or inherit)
operator==(), otherwise a compile-time error would occur.

No operator == (), but it does define operator void * (), this is
a substitute for operator bool () which gives unwanted conversion's
to int, double etc.

IOW it was never intended that you should write ifs == NULL.

Perhapse instead of "better", I should have said "clearer to
those of us who read NULL as relating to pointers".

void f( bool b )
{
if ( b == NULL ) it_just_doesnt_make_sense();
}
Unless I am mistaken, "p == NULL" and "!p" are syntactically equivalent
for plain pointers. Are operator!() and operator==() defined in such a
way that "ifs == NULL" and "!ifs" are not equivalent?

FWIW, the codes generated by gcc-3.2.2 on x86 are slightly different:

call 80489a4 <_init+0x28>
add $0x10,%esp

void *
test %eax,%eax
jne 8048c47 <_Z7myfunc1Pc+0x53>

call 8048a14 <_init+0x98>
add $0x10,%esp
bool

test %al,%al
je 8048cb1 <_Z7myfunc2Pc+0x53>



Rob.
 
G

Grumble

Rob said:
No operator == (), but it does define operator void * (), this is a
substitute for operator bool () which gives unwanted conversion's to
int, double etc.

IOW it was never intended that you should write ifs == NULL.

Perhapse instead of "better", I should have said "clearer to those of
us who read NULL as relating to pointers".

I think I understand. Please correct me if I am wrong.

"if ( !ifs ) stmt;"
NOT "if (ifs == NULL) stmt;"

"if ( ifs ) stmt;"
NOT "if (ifs != NULL) stmt;"

"while (ws(ifs) && getline(ifs, line))"
NOT while (ws(ifs) != NULL && getline(ifs, line) != NULL)

while (ifs >> token && token != ".")
NOT while (ifs >> token != NULL && token != ".")

When I write "if ( ifs )" operator void*() const is called.

A possible implementation is (e.g. libstdc++-v3)
{ return this->fail() ? 0 : const_cast<basic_ios*>(this); }


Thank you very much, Rob. Thank you too, Tom.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top