Handling errors within templated classes

R

RichardOnRails

I have a Stack class that works fine. In particular, when it
encounters an error, it cout's a msg and exits. However, I'd like to
change it to report the error and continue with dummy data as
necessary. My problem is how to construct dummy data consistent with
the type the class was instantiated with.

My class, stripped to it's essentials for my purpose here, is:

template <class T>
class Stack {
public:
T pop()
{
if ( top < 0 )
{
cout << "Error\n";
return WHAT;
}
return st[top--];
}
private:
int top;
T st[5];
};

How might the "return WHAT;" statement be coded to yield something
like:

if ( typeof(T) == int
doThis(0);
else
doThat("xxx");

It was recommended by one person that I check out "template
specialization", which I Googled. I found several offerings, but I
don't think they were applicable to the situation I'm trying to
address (but maybe I'm wrong).

I'm running MinGW on WinXP-Pro/.SP2.

Thanks in Advance,
Richard
 
K

Kai-Uwe Bux

RichardOnRails said:
I have a Stack class that works fine. In particular, when it
encounters an error, it cout's a msg and exits. However, I'd like to
change it to report the error and continue with dummy data as
necessary. My problem is how to construct dummy data consistent with
the type the class was instantiated with.

My class, stripped to it's essentials for my purpose here, is:

template <class T>
class Stack {
public:
T pop()
{
if ( top < 0 )
{
cout << "Error\n";
return WHAT;

You can obtain a default value for T by using the default constructor
(provided T has one):

return ( T() );
}
return st[top--];
}
private:
int top;
T st[5];
};

How might the "return WHAT;" statement be coded to yield something
like:

if ( typeof(T) == int
doThis(0);
else
doThat("xxx");

It was recommended by one person that I check out "template
specialization", which I Googled. I found several offerings, but I
don't think they were applicable to the situation I'm trying to
address (but maybe I'm wrong).

I am not following the doThis() vs. doThat() example. In particular, I have
no idea what the "xxx" is doing there.


BTW: The stack class looks fishy. Poping off an empty stack should trigger
an assert(). If you really want the program to continue, you may opt for
throwing an exception. If you return invented data, client code has no
chance detecting that there way an error.

Also: a hard-coded limit size of 5 is (a) rather small and (b) not
necessary at all. Why don't you use std::stack<>?


Best

Kai-Uwe Bux
 
R

RichardOnRails

RichardOnRails said:
I have a Stack class that works fine. In particular, when it
encounters an error, it cout's a msg and exits. However, I'd like to
change it to report the error and continue with dummy data as
necessary. My problem is how to construct dummy data consistent with
the type the class was instantiated with.
My class, stripped to it's essentials for my purpose here, is:
template <class T>
class Stack {
public:
T pop()
{
if ( top < 0 )
{
cout << "Error\n";
return WHAT;

You can obtain a default value for T by using the default constructor
(provided T has one):

return ( T() );


}
return st[top--];
}
private:
int top;
T st[5];
};
How might the "return WHAT;" statement be coded to yield something
like:
if ( typeof(T) == int
doThis(0);
else
doThat("xxx");
It was recommended by one person that I check out "template
specialization", which I Googled. I found several offerings, but I
don't think they were applicable to the situation I'm trying to
address (but maybe I'm wrong).

I am not following the doThis() vs. doThat() example. In particular, I have
no idea what the "xxx" is doing there.

BTW: The stack class looks fishy. Poping off an empty stack should trigger
an assert(). If you really want the program to continue, you may opt for
throwing an exception. If you return invented data, client code has no
chance detecting that there way an error.

Also: a hard-coded limit size of 5 is (a) rather small and (b) not
necessary at all. Why don't you use std::stack<>?

Best

Kai-Uwe Bux

Hi Kai-Uwe,

Thanks for your response.

Your questions/suggestions are well founded. However, I was using a
"toy" application to test alternative error-handling strategies. One
was to trap the error, display a msg and exit the app. However,
sometimes it's desirable to recover somehow from errors so that
additional errors can be exposed in a single pass through all the
calls in an app.

I finally came up with a way to do that. If you're still interested,
tell me what you think of the following example, which compiles with
the current version of MinGW's g++ and executes as intended: it
exposes two errors.

Regards,
Richard


// TemplateErrHandler.c++
// K:\_Projects\C++\MinGW\04a_TemplateErrHandler\

#include <string>
#include <iostream>
using namespace std;

template <class T>
class MyClass {
public:
MyClass() {}
T MyFn()
{
/* Invalid attampts:
return -1029384756; // Error flag
return "My Message"; // Error flag
*/

// Correct attempt:
cout << "ERROR: An error was detected; execution continuing\n";
T tObj;
return tOverloaded(tObj);
}
private:
int n;
int tOverloaded(int val) {return -1029384756;}
char* tOverloaded(string val){return "My Message";}
};

int main ()
{
MyClass<int> intInstance;
cout << "From intInstance: " << intInstance.MyFn() << "\n";

MyClass<string> strInstance;
cout << "From strInstance: " << strInstance.MyFn() << "\n";
}
 
M

Markus Moll

Hi
I was using a "toy" application to test alternative error-handling
strategies. One was to trap the error, display a msg and exit the app.
However, sometimes it's desirable to recover somehow from errors so that
additional errors can be exposed in a single pass through all the
calls in an app.

Could you explain what kind of errors you want to detect?
I finally came up with a way to do that. If you're still interested,
tell me what you think of the following example, which compiles with
the current version of MinGW's g++ and executes as intended: it
exposes two errors.

Please explain in more detail what exactly you want to do, because I cannot
extract it from the example you give (nor from your previous posting...)
#include <string>
#include <iostream>
using namespace std;

template <class T>
class MyClass {
public:
MyClass() {}
T MyFn()
{
/* Invalid attampts:
return -1029384756; // Error flag
return "My Message"; // Error flag
*/

// Correct attempt:
cout << "ERROR: An error was detected; execution continuing\n";
T tObj;
return tOverloaded(tObj);

Okay... you create a default object tObj of type T. The value you return is
the return value of another overloaded function, named tOverloaded. This
return value does not at all depend on tObj (Question: Why do you create
the object, then?) Furthermore, for types T where no conversion to either
int or std::string exists said:
}
private:
int n;

You _never_ use n. Why do you declare it?
int tOverloaded(int val) {return -1029384756;}
char* tOverloaded(string val){return "My Message";}

Don't use the string-literal-to-char-pointer conversion. It's only there for
C-compatibility (string literals are immutable). I think you wanted to
return string there. Furthermore: Is there _any_ reason why you return "My
Message" and -1029384756?
};

int main ()
{
MyClass<int> intInstance;
cout << "From intInstance: " << intInstance.MyFn() << "\n";

MyClass<string> strInstance;
cout << "From strInstance: " << strInstance.MyFn() << "\n";
}

So you are using MyClass<T> to return some arbitrary T value.

WHY?

And why don't you simply use:

template<typename T> T some_value();
template<> string some_value<string>() { return "My Message"; }
template<> int some_value<int>() { return -1029384756; }

int main()
{
cout << "From int: " << some_value<int>() << "\n";
cout << "From str: " << some_value<string>() << "\n";
}

And what does this have to do with error handling and/or exposing two
errors?

Markus
 
R

RichardOnRails

Hi


Could you explain what kind of errors you want to detect?


Please explain in more detail what exactly you want to do, because I cannot
extract it from the example you give (nor from your previous posting...)






Okay... you create a default object tObj of type T. The value you return is
the return value of another overloaded function, named tOverloaded. This
return value does not at all depend on tObj (Question: Why do you create
the object, then?) Furthermore, for types T where no conversion to either


You _never_ use n. Why do you declare it?


Don't use the string-literal-to-char-pointer conversion. It's only there for
C-compatibility (string literals are immutable). I think you wanted to
return string there. Furthermore: Is there _any_ reason why you return "My
Message" and -1029384756?




So you are using MyClass<T> to return some arbitrary T value.

WHY?

And why don't you simply use:

template<typename T> T some_value();
template<> string some_value<string>() { return "My Message"; }
template<> int some_value<int>() { return -1029384756; }

int main()
{
cout << "From int: " << some_value<int>() << "\n";
cout << "From str: " << some_value<string>() << "\n";

}

And what does this have to do with error handling and/or exposing two
errors?

Markus

Hi,

Did you read the original post? I think that explains what I was
trying to do.

In Ruby, if I suspected that an object reference I had referred either
to a string or a number, I could write:

if obj.responds_to(zero?) puts "obj refers to a number"
else if obj.responds_to(upcase) puts "obj refers to a string"
else puts "Who knows what obj refers to?"

Stoustrup omitted such a capability from C++. I wanted effect such a
capability, and that's what my example does.

Maybe the following version will be more illustative.
--
Richard


// TemplateErrHandler.c++
// K:\_Projects\C++\MinGW\04a_TemplateErrHandler\

/*TEST RESULTS:
K:\_Projects\C++\MinGW\04a_TemplateErrHandler>g++ TemplateErrHandler.c
++

K:\_Projects\C++\MinGW\04a_TemplateErrHandler>a
ERROR: An error was detected; execution continuing
From intInstance: -1029384756
ERROR: An error was detected; execution continuing
From strInstance: My Message

K:\_Projects\C++\MinGW\04a_TemplateErrHandler>
*/

#include <string>
#include <iostream>
using namespace std;

template <class T>
class MyClass {
public:
MyClass() {}
T MyFn()
{
/* Invalid attampts:
return -1029384756; // Error flag
return "My Message"; // Error flag
*/

// Correct attempt:
cout << "ERROR: An error was detected; \n"
<< " execution will continue \n"
<< " by returning an artifical value.\n";
T tObj;
return tOverloaded(tObj);
}
private:
int n;
int tOverloaded(int val) {return -1029384756;}
char* tOverloaded(string val){return "My Message";}
};

int main ()
{
MyClass<int> intInstance;
cout << "Return value from intInstance: " << intInstance.MyFn() << "\n
\n";

MyClass<string> strInstance;
cout << "Return value from strInstance: " << strInstance.MyFn() << "\n
\n";
}
 
J

James Kanze

Your questions/suggestions are well founded. However, I was
using a "toy" application to test alternative error-handling
strategies. One was to trap the error, display a msg and
exit the app. However, sometimes it's desirable to recover
somehow from errors so that additional errors can be exposed
in a single pass through all the calls in an app.

There are two standard solutions for this problem, depending on
the types of errors you're interested in. If the error is
something "exceptional", i.e. something which won't normally
occur (like disk full or insufficient memory), you should
definitely raise an exception. If the error is something
"normal" (just about any errors in user input, for example), the
usual solution is to use some variant of the Barton and Nackman
"Fallible" idiom, perhaps extended to allow additional
information concerning the error. (There's an implementation
supporting extended error codes at my site:
http://kanze.james.neuf.fr/code-en.html, in the Basic
subsystem.)

Another, less frequently used idiom is to provide a callback.
I finally came up with a way to do that. If you're still
interested, tell me what you think of the following example,
which compiles with the current version of MinGW's g++ and
executes as intended: it exposes two errors.

It has two major flaws: the calling code doesn't know that there
was an error, and it outputs error messages to std::cout, which
is not necessarily where the client code wants them.
 
A

Alf P. Steinbach

* James Kanze:
There are two standard solutions for this problem, depending on
the types of errors you're interested in. If the error is
something "exceptional", i.e. something which won't normally
occur (like disk full or insufficient memory), you should
definitely raise an exception.

A newbie should definitely let the program abort, in the case of memory
exhaustion via a new-handler (set_new_handler or whatever the name was),
because with basic resource depletion higher level code won't be able to
do anything and will most likely fail in turn: even the throwing of a
standard exception might fail... It is perhaps ironic that I have
argued the position of using exception, e.g. with Dave Abrahams (who
then took the position I'm doing here, that of terminating at once).
But that was in the context of very advanced error handling, of possibly
cleaning up some things and doing a Bird Of Phoenix: error handling for
newbies and ordinary error handling is something else.

If the error is something
"normal" (just about any errors in user input, for example), the
usual solution is to use some variant of the Barton and Nackman
"Fallible" idiom, perhaps extended to allow additional
information concerning the error. (There's an implementation
supporting extended error codes at my site:
http://kanze.james.neuf.fr/code-en.html, in the Basic
subsystem.)

That's good advice, but the advice about choosing exceptions or not
based on how "exceptional" the situation is IMO ungood advice -- no
matter that it is advice that's (mindlessly, and with reference to some
"authority") repeated by a majority of C++ programmers.

In addition to being so vague and subjective as to be worthless, it's
worthless because what can be "exceptional" in one context need not be
exceptional in some other context.

Instead, use exceptions where they provide clarity and are most
practical -- same advice as for use of loop constructs and other
language constructs.


Cheers,

- Alf
 
J

Joe Greer

Alf P. Steinbach said:
That's good advice, but the advice about choosing exceptions or not
based on how "exceptional" the situation is IMO ungood advice -- no
matter that it is advice that's (mindlessly, and with reference to some
"authority") repeated by a majority of C++ programmers.

In addition to being so vague and subjective as to be worthless, it's
worthless because what can be "exceptional" in one context need not be
exceptional in some other context.

Instead, use exceptions where they provide clarity and are most
practical -- same advice as for use of loop constructs and other
language constructs.


I like the rule of thumb given on the boost site. Use exceptions if what you want is
stack unwinding, otherwise use an error code. In other words, if you generally expect
the immediate caller to handle the error in some way, don't use exceptions. If you
expect the error to be handled at a higher level than the immediate caller, then an
exception might well be the way to go.

joe
 
R

RichardOnRails

I like the rule of thumb given on the boost site. Use exceptions if what you want is
stack unwinding, otherwise use an error code. In other words, if you generally expect
the immediate caller to handle the error in some way, don't use exceptions. If you
expect the error to be handled at a higher level than the immediate caller, then an
exception might well be the way to go.

joe

Hey guys, thanks for your additional ideas. I was just playing around
with a stack implementation I downloaded because I hadn't written any C
++ since I retired 6 years ago. Because it was using it merely as a
toy I added rudimentary error handling with messages directed to
stdout with immediate exit thereafter.

Then I thought I should play with the idea of adding a boolean DEBUG
symbol to allow reporting multiple error in a single session.
However, allowing continued execution after issuing an error message
proved problematical when the offending method's definition specified
a templated return type.

In the latter case, I had to be conjure a suitable dummy return
value. And that value had to be a literal of of the templated type.
I had in mind the ease with which I could that in Ruby, which is rich
in metaprogramming facilities. But nothing came to mind for C++.
Hence the post.

Then the idea of creating overload methods came to me and I was a
happy campler. So I'm signing off from this thread with thanks to all
respondents. I will keep your ideas in mind if I ever do serious C++
programming again.
 
J

James Kanze

* James Kanze:
A newbie should definitely let the program abort,

Probably. (If the newbie is working in a larger team, he should
use whatever mechanisms the team has established. But setting
up a framework to do anything but abort isn't a task for a
newbie.)

Running out of memory just happens to be a convenient example.
More generally, of course, you have a complete hierarchy of ways
to handle errors, from aborting, to simply noting the error and
continuing in a degraded mode (something like ostream, for
example). Which one to use will depend on the type of error and
the application constraits. There is no universally "right"
solution. Which is really what lies at the heart of the
original question: if you're writing a generic class, for use in
many different applications, what do you do? The most useful
solution is generally to use the least radical solution which
could reasonably apply. In the case of out of memory, it's hard
to imagine anything less that an exception as being acceptable,
although I agree that at least 90% of the time (newbie or not),
aborting is what is really required. But from the user's point
of view, it's easier to convert an exception into an
abort---just don't catch it:)---than it is to convert an abort
into an exception.
in the case of memory exhaustion via a new-handler
(set_new_handler or whatever the name was), because with basic
resource depletion higher level code won't be able to do
anything and will most likely fail in turn: even the throwing
of a standard exception might fail...

Formally, just about anything might fail at any time. The
standard has a cop out saying that not enough resources is
undefined behavior. Of course, we all count on quality of
implementation as well. On the systems I use, you can catch
bad_alloc and continue (although you may have to configure the
OS correctly in order for the error to be detected). On the
systems I use, there are even ways of guaranteeing that the
stack doesn't overflow (but they're very system dependent).
It is perhaps ironic that I have argued the position of using
exception, e.g. with Dave Abrahams (who then took the position
I'm doing here, that of terminating at once). But that was in
the context of very advanced error handling, of possibly
cleaning up some things and doing a Bird Of Phoenix: error
handling for newbies and ordinary error handling is something
else.

It all depends on the application. I write large scale server
software. My programs run on dedicated machines, with
sufficient memory. Most of the time, if I run out of memory,
the only possible reason could be a memory leak. In that case,
it's definitly best to abort and restart the application; the
situation won't get any better otherwise. But there are
exceptions: consider a protocol like LDAP, where the client can
send arbitrary "expressions", which will typically be
represented internally in the form of an expression graph.
Regardless of how much memory you have, expressions which
require more can exist. In such case, catching bad_alloc and
responding with an "insufficient resources" error is a very
viable solution. (If you've programmed correctly, the stack
walkback of the exception will ensure that all of the nodes are
released.)
That's good advice, but the advice about choosing exceptions
or not based on how "exceptional" the situation is IMO ungood
advice -- no matter that it is advice that's (mindlessly,
and with reference to some "authority") repeated by a majority
of C++ programmers.
In addition to being so vague and subjective as to be
worthless, it's worthless because what can be "exceptional" in
one context need not be exceptional in some other context.

It's vague and subjective precisely because it depends on the
context. No one is claiming anything else. It's a sort of way
of saying that it depends, with a very vague suggestion about
what might be relevant to consider.
Instead, use exceptions where they provide clarity and are
most practical -- same advice as for use of loop constructs
and other language constructs.

OK. I'm not sure what "most practical" really means in this
context, but clarity is certainly important. Of course, which
solution is clearest will also depend on the application:
"provides clarity and are most pratical" is also vague and
subjective.

Which is fine, because the issue is vague and subjective; since
the correct answer depends a lot on context, any context free
answer must be vague and subjective.
 
J

James Kanze

I like the rule of thumb given on the boost site. Use
exceptions if what you want is stack unwinding, otherwise use
an error code. In other words, if you generally expect the
immediate caller to handle the error in some way, don't use
exceptions. If you expect the error to be handled at a higher
level than the immediate caller, then an exception might well
be the way to go.

That's the general idea, but it also begs the question. If
you're writing a generic class, used in many different contexts,
how do you know which is which?

There are many different strategies for handling errors, in
order of gravity: abort, exception, return code, and internal
status with a degraded mode of operation. (There are others,
but they are either rare, or should be.) As a general rule, if
you're in doubt, you should use the weakest method: in
particular, library code should never abort except in response
to a programming error (assertion failure), and should prefer a
return code to an exception if there's any reasonable chance of
the error being handled "locally". The cases where internal
state and degraded operation are rarer, but two of them are very
widespread: IO and floating point arithmetic.
 

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,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top