Introduction
I've got this idea as an expansion to HRESULT Error Check Simplifier By Wanderley Caloni, so I guess his introduction would be better than if I wrote one. Many thanks to Wanderley Caloni for writing the article.
I'll just introduce here what I have added. The article mentioned above, has a class that checks if an HRESULT
value is SUCCEEDED
or not. If not, it throws an exception. What is added is some extra functionality, to be able to show a message box, with the line number and file name and a custom error message.
Using the code
Take a look at this usage example:
if( NULL == ( direct3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
throw MyException(_E("Can't create Direct3D object"));
hrToException(_E("Can't get adapter display mode")) =
direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&Mode);
The throw MyException(_E("Can't create Direct3D object"));
is normal and is known to any C++ developer, it only throws an exception.
But the idea is in the next example hrToException(_E("Can't get adapter display mode")) = direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&Mode);
: it simply means that if the return HRESULT
value from the function call direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&Mode)
is FAILED
, then throw an exception with the current line number and the current file name and containing the error string "Can't get adapter display mode".
The old way to do this was:
if(FAILED(direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&Mode))
throw MyException("Can't get adapter display mode",__FILE__,__LINE__);
Imagine your code with this kind of if
s all over the file :-). At least the new class removes the branching logic off your shoulder.
Here's the _E()
macro. It just adds the line number and the file name to the parameter of the exception constructor, to save some typing.
#define _E(str) str##,##__FILE__##,##__LINE__
Your code should be in a try
-catch
block.
#define _MYTRY try {
There is a pre-defined catch
block, to produce the message box, and also to save some typing. You might change the error[]
length if you plan to use longer error strings.
#define _MYCATCH \
} \
catch(MyException ex) \
{ \
char error[255]; \
sprintf(error,"%s ( HRESULT = %i ), file: %s, line: %i" ,
ex.error_str , ex.hr , ex.file , ex.lineNumber);\
::MessageBox(hWnd,error , "MyException Caught" , MB_OK); \
exit(E_FAIL); \
}
The exception class holds only these four pieces of information.
struct MyException
{
...
char* error_str;
char* file;
int lineNumber;
HRESULT hr;
};
Check the line:
hrToException(_E("Can't get adapter display mode")) =
direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&Mode);
It is done like this:
class HResultToException
{
public:
HResultToException& operator()(char* error,
char* file_name,int line_number)
{
error_str = error;
this->file_name = file_name;
this->line_number = line_number;
return *this;
}
void operator=(HRESULT hr)
{
if(FAILED(hr))
throw MyException(error_str,hr,file_name,line_number);
}
};
Conclusion
This article is only to show some ideas to enhance readability, but it lacks reusability. I would appreciate if someone can make it more reusable. Anyway, the article is meant to show an example, that the reader is not supposed to use directly. The reader is supposed to catch the idea and re-implement it as it fits him/her the best.