Re: Class throwing exceptions in aggregate static initializer causes
abort() ...
* Marvin Barley:
Thank you very much, Mr. Steinbach,
Alf P. Steinbach je napisao/la:
* Marvin Barley:
I have a class that throws exceptions in new initializer, and a static
array of objects of this type.
When something is wrong in initialization, CGI program crashes
miserably. Debugging shows uncaught exception.
How to catch an exception that happened before main() try { ... }
catch (...) { ... } block?
Is there a way?
Locally yes, just use try-catch; globally, in a portable way, no.
I thought there should be a way through set_terminate() to just exit
gracefully, if not to actually catch exception?
You can exit but not gracefully (in a portable way)... ;-) Calling
'exit' attempts to or may attempt to destroy static objects, which is
not a good idea with the process in an unstable state. And 'abort'
typically prints a message.
That said, what you have doesn't seem to be exceptions resulting from
exceptional circumstances (such as resource shortage), but resulting
from programming bugs.
No, Sir, stack unwinding proved that the abort() was called in a stack
child of __cxa_throw() called from
__static_initialization_and_destruction_0() internals.
Yep, programming bug.
Should've printed it out:
[stack trace elided]
.
===========================================
Fix your code and you won't have that problem.
I'll try - if only I could know for sure from inside constructor is
the object statically or dynamically initialized, so I should throw
exception in dynamic allocation and only set <error_code> in static
case?
You can't /detect/ that in a portable way, but you can ensure that a
class is only instantiated dynamically (via new) by making the
destructor private or protected, e.g.
class DynamicObject
{
private:
~DynamicObject() {}
public:
void selfDestroy() { delete this; }
};
Instead of a non-static member function 'selfDestroy' you might prefer
to grant friendship to a global template function 'destroy' as well as
e.g. 'std::auto_ptr' and any other smart-pointer class used in the code.
But I hasten to add, this is not a technique that is likely to help with
your problem, which is not to differentiate between different kinds of
allocation, but simply to get the code working.
[snip]
Don't use raw arrays (even though this code also works with raw arrays).
#define STRCPY(DEST, SRC) >>> if (strlen (SRC) > sizeof (DEST) - 1) >>> throw PARM_ESTRBUFOVERFLOW; >>> else >>> strcpy (DEST, SRC);
Don't use macros.
Couldn't find a way to check sizeof () elegant in function, sorry I
know some very competent programmers who use macros. I think it is a
matter of opinion.
Instead of raw arrays, use std::string for strings, and std::vector for
other kinds of "arrays". Then you don't have to use the macro. And you
avoid all the other bug-causing stuff.
Don't use raw pointers.
Don't use macros.
Reserve all uppercase names for macros (which you shouldn't use) --
see this group's FAQ and Bjarne Stroustrup's FAQ.
Additional errors probably in the declarations you left out.
No need to use keyword 'class' -- C'ism.
No need to use keyword 'class' -- not even a C'ism.
I think these are just style guides not connected to the main problem
of exception in global initializer, wouldn't you agree? I like to
emphasize it is a class in declaration, since I have a short term
memory problem ;-)
Mostly they're style guides, yes, except the first one: that one is your
ticket out of the crash-crash-crash situation.
It might be a good idea to reproduce in a small program that compiles,
and post that code.
Should've thought of it myself:
#include <iostream>
using namespace std;
class formtblitem {
void initialize (const char *val) {
throw -1;
form_field = new char [strlen(val)+1];
strcpy (form_field, val);
}
protected:
char *form_field;
public:
formtblitem() { form_field = NULL; }
formtblitem(char *line) { initialize (line); }
~formtblitem() {
if (form_field) delete [] form_field;
}
const char * FormField (void)
{ return form_field; }
};
If you remove the 'throw' there are still a number of problems here.
Academically (but it goes to portability), you need <cstddef> for NULL.
Practically, if an object of class 'formtblitem' is ever copied then you
will have two or more objects trying to 'delete' the same dynamically
allocated array. Just by using 'std::string' instead you remove that
problem. To be fair, it isn't the problem you're experiencing, which
occurs at initialization instead of destruction, but chances are that
just using std::string instead of raw arrays will fix that too.
typedef formtblitem FTI;
FTI formtbl[] = {
FTI ("global initializer throwing exceptions")
};
int main (int argc, char *argv[]) {
FTI *dynamic = NULL;
try {
FTI automatic("automatic_initializer");
dynamic = new FTI("dynamic_initializer");
} catch (int i) {
cout << "caught exception << i << endl;
// cleanup
if (dynamic) delete [] dynamic;
exit(1);
}
}
Off the cuff (code not touched by compiler's hands):
#include <cstddef> // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cout
#include <ostream> // operator<<, std::endl
#include <string> // std::string
#include <memory> // std::auto_ptr
#include <stdexcept> // std::exception
class FormTblItem
{
private:
std::string myValue;
public:
FormTblItem() {}
FormTblItem( char const line[] ): myValue( line ) {}
char const* value() { return myValue.c_str(); }
};
typedef FormTblItem Fti;
typedef std::auto_ptr<Fti> FtiPtr;
Fti const formTbl[] = { "global_initializer" };
int main()
{
try
{
Fti automatic( "automatic_initializer" );
FtiPtr dynamic( new Fti( "dynamic_initializer" ) );
return EXIT_SUCCESS;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
return EXIT_FAILURE;
}
}
That's all there is to it, in C++. :-)
But of course, if the rest of the code is C++'ified too, then 'value'
should probably return a 'std::string' instead of a 'char const*'.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?