conversion: errno => exception

M

Markus Elfring

Do you know a class library that can convert the error/return codes
that are listed in the standard header file "errno.h" into a
well-known exception hierarchy?
Did anybody derive it from "std:runtime_error"?

Regards,
Markus
 
A

Alf P. Steinbach

* Markus Elfring:
Do you know a class library that can convert the error/return codes
that are listed in the standard header file "errno.h" into a
well-known exception hierarchy?
Did anybody derive it from "std:runtime_error"?

Why do you want a hierarchy?

Off the cuff:

void throwStdIoError()
{
throw std::runtime_error( std::strerror( std::errno ) );
}
 
R

Rolf Magnus

Alf said:
* Markus Elfring:

Why do you want a hierarchy?

Off the cuff:

void throwStdIoError()
{
throw std::runtime_error( std::strerror( std::errno ) );
}

It would be quite hard for the function that catches the exception to find
out what the error actually is.
 
A

Alf P. Steinbach

* Rolf Magnus:
It would be quite hard for the function that catches the exception to find
out what the error actually is.

If it's not within the context of the throwing call, why should it care?
 
M

Markus Elfring

Why do you want a hierarchy?

The reasons are similar to the design decisions for the base classes
that are specified in the header <exception> and related files.
I would like to unify error reporting and put return codes into groups
or categories. I see a problem there to find the appropriate names for
the categorization.

The application of design patterns is another use case.
- http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
- http://www.refactoring.com/catalog/replaceTypeCodeWithClass.html
- http://industriallogic.com/xp/refactoring/typeCodeWithClass.html

I imagine a factory class that provides a method like this one.
errno_exception& create(int err_no) const throw(std::eek:ut_of_range);

I suggest two approaches for its implementation.
1. a huge switch statement
2. Instances are copied from a static collection (array, vector or
map).

Have you got more ideas?
Is a complete solution available already?

Regards,
Markus
 
A

Alf P. Steinbach

* Markus Elfring:
The reasons are similar to the design decisions for the base classes
that are specified in the header <exception> and related files.
I would like to unify error reporting and put return codes into groups
or categories. I see a problem there to find the appropriate names for
the categorization.

If naming is a problem, then the idea is probably not meaningful.

The base classes in <exception> are meant to facilitate different handling
of different kinds of error state, primarily to diffentiate between
recoverable/ignorable on the one hand, and unrecoverable/non-ignorable.

But <exception> is very badly designed (if it is designed at all ;-)).
It probably emerged as a "let's get it over with, choose something!" solution
to a lot of confusion and disagreement. And so for many years Microsoft's
documentation incorrectly referred to a completely different hierarchy...


Regarding "replaceTypeCodeWithClass", consider how much easier it is to
"replace type code with log message", which is all you ordinarily need.

Do not be blinded by design patterns.

A list of design patterns can be a good source of ideas, but forcing a
pattern on a design is usually UnGood: instead, see what pattern is there
in the first place.

I imagine a factory class that provides a method like this one.
errno_exception& create(int err_no) const throw(std::eek:ut_of_range);

Regarding C++ the result should be const, and it's not a good idea to use
std::eek:ut_of_range (I haven't checked that the name is correct): instead,
use std::runtime_error or derived classes for recoverable errors, so that
other code won't get an exception it doesn't expect and won't have to
check for a zillion silly different types of exception.
 
R

Rolf Magnus

Alf said:
* Rolf Magnus:

If it's not within the context of the throwing call, why should it care?

If it doesn't care, what's the purpose of the exception?
 
A

Alf P. Steinbach

* Rolf Magnus:
If it doesn't care, what's the purpose of the exception?

Most actual code does not use information about the type of a caught
exception.

The only relevant information is, usually, that an exception occurred, and
(for purposes of logging) the explanatory string carried by the exception.

Hence your question is really "why does C++ support exceptions", and that
belongs in [comp.std.c++], or a programming textbook, if anywhere.
 
T

Tom Widmer

The reasons are similar to the design decisions for the base classes
that are specified in the header <exception> and related files.
I would like to unify error reporting and put return codes into groups
or categories. I see a problem there to find the appropriate names for
the categorization.

The application of design patterns is another use case.
- http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
- http://www.refactoring.com/catalog/replaceTypeCodeWithClass.html
- http://industriallogic.com/xp/refactoring/typeCodeWithClass.html

I imagine a factory class that provides a method like this one.
errno_exception& create(int err_no) const throw(std::eek:ut_of_range);

That isn't going to work, since if you do:

throw create(errno);

the exception object will be sliced, since in C++ the static type of a
throw statement determines the type of exception thrown. This of
course assumes that you are going to add classes derived from
errno_exception.
I suggest two approaches for its implementation.
1. a huge switch statement
2. Instances are copied from a static collection (array, vector or
map).

Have you got more ideas?

You could use a map from error number to a throw function. e.g.

template <class Exception> void thrower(int errorCode/*other
params?*/)
{
throw Exception(errorCode/*other params?*/);
}

and a map:

std::map<int, void(int)> throwers;

Insert in the map with:

throwers[someErrorCode] = &thrower<SomeExceptionClass>;

Then you throw with:

void throwErrNo(int errorNumber/*other params?*/)
{
iterator i = throwers.find(someErrorCode);
if (i != throwers.end())
{
(i->second)(someErrorCode/*other params?*/);
}
else
{
//either throw errno_exception or out_of_range
//or even assert?
}
}

If the sequence of error numbers is fairly contiguous, you could use a
vector rather than a map. Alternatively, you could just use the switch
statement - this depends on whether you want the thing to be runtime
pluggable with new error codes, etc.
Is a complete solution available already?

Not that I know of.

Tom
 
A

Alf P. Steinbach

* Tom Widmer:
* Markus Elfring:

That isn't going to work, since if you do:

throw create(errno);

the exception object will be sliced, since in C++ the static type of a
throw statement determines the type of exception thrown. This of
course assumes that you are going to add classes derived from
errno_exception.

Well, what you describe here wasn't mentioned by Markus.

Hence the "that" that isn't going to work is your code... ;-)

Because of that the natural assumption is that his errno_exception is a
factory, or that it directly provides a virtual throw function.
 
T

Tom Widmer

* Tom Widmer:

Well, what you describe here wasn't mentioned by Markus.

Hence the "that" that isn't going to work is your code... ;-)

Because of that the natural assumption is that his errno_exception is a
factory, or that it directly provides a virtual throw function.

True, but I thought it was worth highlighting the issue just in case
he wasn't aware of it.

Actually, a virtual throw method is generally a good idea in any
exception heirarchy, along with a clone method, so that you can
propogate exceptions between threads, etc.

Tom
 
M

Markus Elfring

That isn't going to work, since if you do:

How do you think about an interface like this one?
virtual errno_exception& create(int err_no) const;

throw create(errno);

I would like to use ...
throw my_exception_factory.create(errno);

the exception object will be sliced, since in C++ the static type of a
throw statement determines the type of exception thrown. This of
course assumes that you are going to add classes derived from
errno_exception.

I do not think that "object slicing" will happen.
http://www.savinov.spb.ru/think/tic0161.html#Heading428

How does your statement fit to the handling of "covariant return types"?
http://www.fmi.uni-konstanz.de/~kuehl/c++-faq/virtual-functions.html#faq-20.6
http://www.fmi.uni-konstanz.de/~kuehl/c++-faq/abcs.html#faq-22.5
http://semantics.org/once_weakly/ck10_covariantreturn.pdf
 
T

Tom Widmer

How do you think about an interface like this one?
virtual errno_exception& create(int err_no) const;



I would like to use ...
throw my_exception_factory.create(errno);

That's exactly what won't work. You will need something like:

my_exception_factory.create(errno).throw_self();

where throw_self is a virtual function that is implemented in every
exception class that simply does throw *this;
Or you can have:

my_exception_factory.throw_error(errno);

or similar.
I do not think that "object slicing" will happen.
http://www.savinov.spb.ru/think/tic0161.html#Heading428

Throwing an exception involves copying it. In the statement "throw t"
the static type of t is used to determine the exception that is
thrown. t is copied into a "temporary" exception object of type
static-type-of-t, and this copy will be a slice if the static type of
t doesn't match its dynamic type.

I don't think that covariant returns will make any difference here.

Tom
 
M

Markus Elfring

Throwing an exception involves copying it. In the statement "throw t"
the static type of t is used to determine the exception that is
thrown. t is copied into a "temporary" exception object of type
static-type-of-t, and this copy will be a slice if the static type of
t doesn't match its dynamic type.

How do you think about the effects by the following design sketch?

#include <iostream.h>
#include <errno.h>

struct errno_exception
{
virtual ~errno_exception() {}
virtual int get_code() const { return EZERO; }
};


struct invalid : public errno_exception
{
};


struct invalid_value : public invalid
{
virtual int get_code() const { return EINVAL; }
};


struct invalid_environment : public invalid
{
virtual int get_code() const { return EINVENV; }
};


struct invalid_format : public invalid
{
virtual int get_code() const { return EINVFMT; }
};


struct no_entry : public errno_exception
{
virtual int get_code() const { return ENOENT; }
};


struct exception_factory
{
virtual ~exception_factory() {}
virtual errno_exception& create (int err_no) const = 0;
};


struct factory1 : public exception_factory
{
virtual errno_exception& create (int err_no) const;
};


struct errno_exceptions
{
errno_exception _ZERO;
no_entry _NOENT;
invalid_format _INVFMT;
invalid_environment _INVENV;
invalid_value _INVAL;
};


errno_exceptions my_e_e;


errno_exception& factory1::create (int err_no) const
{
switch (err_no)
{
case ENOENT:
return my_e_e._NOENT;

case EINVFMT:
return my_e_e._INVFMT;

case EINVENV:
return my_e_e._INVENV;

case EINVAL:
return my_e_e._INVAL;

default:
return my_e_e._ZERO;
}
}


struct exception_thrower
{
virtual ~exception_thrower() {}
virtual void create (int code) const = 0;
};


struct factory2 : public exception_thrower
{
virtual void create (int code) const;
};


void factory2::create (int code) const
{
switch (code)
{
case ENOENT:
throw no_entry();

case EINVFMT:
throw invalid_format();

case EINVENV:
throw invalid_environment();

case EINVAL:
throw invalid_value();

default:
throw errno_exception();
}
}


int main ()
{
factory1 f1;

try
{
throw f1.create(EINVFMT);
}
catch (invalid const& i)
{
cout << "1. invalid = " << i.get_code() << endl;
}
catch (errno_exception const& e)
{
cout << "1. errno exception base" << endl;
}

try
{
factory2 f2;
f2.create(EINVFMT);
}
catch (invalid const& i)
{
cout << "2. invalid = " << i.get_code() << endl;
}
catch (errno_exception const& e)
{
cout << "2. errno exception base" << endl;
}

return 0;
}
 

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

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top