From: Early Ehlinger |
|
Subject: External Exception EEFFACE |
NewsGroup: borland.public.cppbuilder.language.cpp |
Date Posted: 17-Feb-2003 at 16:53:2 PST |
First, let me explain what this error really means as I understand it...
Some C++-style exception has leaked out of my code and into the VCL
framework code. The VCL framework has translated this exception into a
Delphi-style exception of type EExternalException. During
Application::HandleException, the VCL decides to display an error message
indicating that an External Exception occurred and that its exception Id was
eefface.
Next, I'd like to briefly discuss my scenario...
I have a large library of code that for various reasons must be pure ISO C++
and not utilize or be aware of the VCL in any way. When this code
encounters an exceptional condition, it will throw exceptions derived from
std::exception. Typically, they actually derive from std::runtime_error,
but knowing that they derive from std::exception is enough information for
my question, which I promise to get to momentarily.
Last comment before the question...
In most cases (for this application), the proper behavior when an
exceptional condition occurs *is* to report it to the user. eefface,
however, is about the least useful piece of information that I can give to
my customers. My current workaround is to litter my code with something
similar to this:
void TformMain::SomeEvent( TObject* Sender )
{
try
{
/* do stuff that may throw std::exception */
}
catch( std::exception& exc )
{
throw Exception( exc.what() );
}
}
The problem I have with this is that I have to litter all of my code with
error handling that is (almost) present anyway in the VCL.
What I've been trying to do is figure out a mechanism for translating C++
exceptions that derive from std::exception into a different VCL-style
exception (i.e., one that derives from Exception) without having to litter
my code with exception handlers. As I said, most of the time, the correct
behavior for my C++ exceptions is to report the error to the user. I've
been looking for an approach for doing that.
To that end, here's what I've come up with. It's extremely ugly, and not
very well documented, but I'd like some feedback if anybody has the time...
typedef Exception* (__fastcall* ExceptObjProcType)
( Sysutils::TExceptionRecord* P );
ExceptObjProcType pOldExceptObjProc;
Exception* __fastcall MyGetExceptionObject(Sysutils::TExceptionRecord* P)
{
class not_standard_exception
{
public:
virtual void foo() = 0;
};
const DWORD cCppException = 0xEEFFACE;
const int cMagic = 0x52;
switch( P->ExceptionCode )
{
case cCppException:
{
// Extract relevant information from the exception record:
char* type_name = (char*)P->ExceptionInformation[0];
not_standard_exception const* p_exception =
reinterpret_cast< not_standard_exception* >
( P->ExceptionInformation[2] + cMagic );
std::stringstream message;
message << "C++ Exception Thrown - Type=\"" << type_name << "\"";
std::exception const* p_std_exception = 0;
/* __try/__except necessary if thrown type
doesn't have v-table, e.g., "throw 1;" */
__try
{
p_std_exception = dynamic_cast< std::exception const* >
( p_exception );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
}
if ( p_std_exception )
{
char const* p_message = p_std_exception->what();
message << "\n" << p_message;
}
return new Exception( message.str().c_str() );
/*
TODO : replace new Exception with something like
new CppException( type_name ) and
new CppStdException( type_name , p_std_exception->what() );
*/
}
default:
return pOldExceptObjProc(P);
}
}
void install_exception_object_handler( )
{
pOldExceptObjProc = reinterpret_cast< ExceptObjProcType >
( ExceptObjProc );
System::ExceptObjProc = MyGetExceptionObject;
}
#pragma startup install_exception_object_handler
--
-- Early Ehlinger CEO, ResPower Inc - Toll-Free : 866-737-7697 --
- RenderFarm - Lightwave , 3dSMax , Bryce , Maya , AfterEffects -
--- www.respower.com -- 400+ GHz Starting At USD$0.50/GHz*Hour --
----------------- SuperComputing For the Masses! ----------------
"I'm so busy programming my computer to let me be lazy, I never
get any rest!" - John Kaster
|
From: Early Ehlinger |
|
Subject: Re: External Exception eefface |
NewsGroup: borland.public.cppbuilder.language.cpp |
Date Posted: 23-Feb-2003 at 19:23:41 PST |
I think I've finally solidified my exception translator to the point where
it is generally useful.
Many thanks to all who gave me feedback.
Schobi, I tried using the throw; trick inside of the exception translator,
but it didn't want to work - caused an Abnormal Program Termination...
I've rejected the macro and try/catch/rethrow mechanisms as being too
redundant and human-error-prone for my purposes. Hopefully I won't regret
that decision at some point by finding out that this library has introduced
a huge bug :)
In any event, I've decided to place my work into the open source domain via
the Lesser GPL. You can find an in-depth article about how and why it
works, along with a downloadable version that translates all of the
std::exception types outlined in ISO/IEC 14882:1998(E) (the ISO Standard),
sections 18.6.1 and 19.1.
Hopefully someone else out there has had significant frustration with
EEFFACE exceptions and will give this little library a try and perhaps even
one day send me a kind word :)
So, without further ado, here is the link:
http://www.respower.com/~earlye/programming/TranslateStandardExceptions.htm
Thanks and good luck to all!
--
-- Early Ehlinger CEO, ResPower Inc - Toll-Free : 866-737-7697 --
- RenderFarm - Lightwave , 3dSMax , Bryce , Maya , AfterEffects -
--- www.respower.com -- 400+ GHz Starting At USD$0.50/GHz*Hour --
----------------- SuperComputing For the Masses! ----------------
"I'm so busy programming my computer to let me be lazy, I never
get any rest!" - John Kaster
|
From: Hendrik Schober |
|
Subject: Re: External Exception eefface |
NewsGroup: borland.public.cppbuilder.language.cpp |
Date Posted: 19-Feb-2003 at 13:46:46 PST |
"Early Ehlinger" wrote:
> [...]
> * An extensible, supported mechanism for translating C++ exceptions into VCL
> exceptions. Hopefully they could come up with something better than
> Application->OnException, which has already decided that the exception is
> EExternalException...
> * A separate event in Application, e.g., Application->OnCppException, that
> hands the C++ object back to the application for display.
This has come up here several times over the
last couple of years and, AFAIK, nobody found
a satisfying solution for it. (But I haven't
seen something as close as yours either.)
When I was using BCB (some years ago) I went
down the macro approach Chris suggested and
it has worked out on some projects. I think
this works best if you strictly seperate your
GUI code (event handlers) from you application
logic. It's not that hard to browse through
a file full of VCL event handlers and check
whether they all have those macros. Of course,
if the functions aren't merely forwarding, but
contain a lot of logic themselves, it is a lot
harder.
Anyway, once you got that far, something else
might work. I haven't time to test this with
BCB, but I suspect your function to be called
from an exception handler (aka catch statement.
Now this is all VCL and Delphi, so it might
not work that way, but in C++ an old trick to
get at a more specific type of an exception
caught using an unspecific exception type is
the re-throw trick:
try {
f();
} catch(...) { // unspecific catch
handle_exception();
}
void handle_exception()
{
try {
throw; // re-throws exception last caught
} catch( MyException& x ) {
// handle 'MyException' type
} catch( std::exception& x ) {
// handle 'std::exception' type
} catch( YourException& x ) {
// handle 'YourException' type
} catch(...) { // out of ideas
throw; // give up
}
}
As I understand your code, it would be the
equivalent of the 'handle_exception()' function
in the example above, called from the VCL.
Have you tried the re-throw approach?
> -- Early Ehlinger CEO, ResPower Inc - Toll-Free : 866-737-7697 --
Schobi
--
SpamTrap@gmx.de is never read
I'm Schobi at suespammers org
|
From: Early Ehlinger |
|
Subject: Re: External Exception eefface |
NewsGroup: borland.public.cppbuilder.language.cpp |
Date Posted: 18-Feb-2003 at 8:33:6 PST |
"Chris Uzdavinis [TeamB]" wrote:
> You have an interesting solution, but it's outside anything I have
> experience with. While ugly, a macro might be helpful here.
>
> ...
>
> Anyway, this is not such a complex solution as what you have, but it's
> easy to implement, and is very reliable. Just a thought.
>
> --
> Chris(TeamB);
I don't necessarily think that macros are ugly. I use them all the time in
COM objects to make exceptions work with ISupportErrorInfo:
HRESULT TSomeObjectImpl::SomeMethod( )
{
try
{
}
TRANSLATE_EXCEPTIONS( ISomeInterface )
return S_OK;
}
But I do not have enough faith in myself to remember to use them on every
event handler that I write. Having forgotten to place them into my COM
objects on a number of occasions, I know that I'm going to forget and it's
going to happen when Mr. Murphy wants it to.
What I like about my solution is that it's global and moves nearly all
exception handling into my code library. The only time I have to use a
try/except is when my code can handle an exception silently, which is quite
rare in a GUI application.
I'm not ignoring the problems, either:
* It deals with serious compiler magic.
* Why in the %^%^%^% is the exception object 0x52 bytes after
P->ExceptionInformation[2]?
* What's in that first 0x52 bytes?
* How did I even notice that that's where it was?
* I'll take one guess as to whether this will work in version 7...
* Sure, once it's installed, I don't have to worry about try/catch for most
circumstances. But there's always the possibility that my code has an
unforeseen bug (stay away, Murphy!)
I would much prefer to see Borland take notice of the fact that this is
something that needs attention and provide either:
* An extensible, supported mechanism for translating C++ exceptions into VCL
exceptions. Hopefully they could come up with something better than
Application->OnException, which has already decided that the exception is
EExternalException...
* A separate event in Application, e.g., Application->OnCppException, that
hands the C++ object back to the application for display.
--
-- Early Ehlinger CEO, ResPower Inc - Toll-Free : 866-737-7697 --
- RenderFarm - Lightwave , 3dSMax , Bryce , Maya , AfterEffects -
--- www.respower.com -- 400+ GHz Starting At USD$0.50/GHz*Hour --
----------------- SuperComputing For the Masses! ----------------
"I'm so busy programming my computer to let me be lazy, I never
get any rest!" - John Kaster
|
From: Chris Uzdavinis [TeamB] |
|
Subject: Re: External Exception eefface |
NewsGroup: borland.public.cppbuilder.language.cpp |
Date Posted: 17-Feb-2003 at 23:45:47 PST |
"Early Ehlinger" writes:
> First, let me explain what this error really means as I understand it...
>
> Some C++-style exception has leaked out of my code and into the VCL
> framework code. The VCL framework has translated this exception
> into a Delphi-style exception of type EExternalException. During
> Application::HandleException, the VCL decides to display an error
> message indicating that an External Exception occurred and that its
> exception Id was eefface.
You have an interesting solution, but it's outside anything I have
experience with. While ugly, a macro might be helpful here.
BEGIN_EXCEPT_STD_TO_VCL;
function_call();
END_EXCEPT_STD_TO_VCL;
Where the macros expand to a try-catch translation block.
Another idea:
EXCEPT_STD_TO_VCL((
function_call();
))
Here the macro would expand to the entire try-catch block. I used
double parenthesis because your function_call() might have arguments,
and they are separated by commas. Due to macro parsing, the commas
would be considered macro argument separators without the extra parens
around the expression.
Anyway, this is not such a complex solution as what you have, but it's
easy to implement, and is very reliable. Just a thought.
--
Chris(TeamB);
|
From: Early Ehlinger |
|
Subject: Re: External Exception eefface |
NewsGroup: borland.public.cppbuilder.language.cpp |
Date Posted: 17-Feb-2003 at 17:3:38 PST |
"Early Ehlinger" wrote:
> Exception* __fastcall MyGetExceptionObject
> (Sysutils::TExceptionRecord* P)
> {
> class not_standard_exception
> {
> public:
> virtual void foo() = 0;
> };
> /* ... */
> }
Of course this definition of not_standard_exception will not work, because
it has a function (foo) that is not-inline.
If you're trying to test this out, use this instead...
#include
#pragma hdrstop
// #include "TranslateStandardExceptions.h"
#include
//--------------------------------------------------------------------------
-
#pragma package(smart_init)
typedef Exception* (__fastcall* ExceptObjProcType)(
Sysutils::TExceptionRecord* P );
ExceptObjProcType pOldExceptObjProc;
class not_standard_exception
{
public:
virtual void foo() = 0;
};
Exception* __fastcall MyGetExceptionObject(Sysutils::TExceptionRecord* P)
{
const DWORD cCppException = 0xEEFFACE;
const int cMagic = 0x52;
switch( P->ExceptionCode )
{
case cCppException:
{
// Extract relevant information from the exception record:
char* type_name = (char*)P->ExceptionInformation[0];
not_standard_exception const* p_exception = reinterpret_cast<
not_standard_exception* >( P->ExceptionInformation[2] + cMagic );
std::stringstream message;
message << "C++ Exception Thrown - Type=\"" << type_name << "\"";
std::exception const* p_std_exception = 0;
__try
{ p_std_exception = dynamic_cast< std::exception const* >(
p_exception ); }
__except( EXCEPTION_EXECUTE_HANDLER )
{ }
if ( p_std_exception )
{
char const* p_message = p_std_exception->what();
message << "\n" << p_message;
}
return new Exception( message.str().c_str() );
}
default:
return pOldExceptObjProc(P);
}
}
void install_exception_object_handler( )
{
pOldExceptObjProc = reinterpret_cast< ExceptObjProcType >(
ExceptObjProc );
System::ExceptObjProc = MyGetExceptionObject;
}
#pragma startup install_exception_object_handler
|
|
|