
Apparently, the week of exceptions on Habré successfully arrived :). Having accumulated a sufficient “pillow” of karma so as not to be afraid to go into a minus, I, perhaps, will also express my opinion on this issue. At once I will make a reservation that the opinion is purely personal, based on a small practical experience of commercial development: C ++, Objective-C, C #, Java, Python, Ruby.
What is a mistake?
Before embarking on a bloody mess, it is advisable to evaluate the battlefield and synchronize the terminology. Under the “errors” in the context of software development can be understood quite different things, so for starters, I will try to describe these entities as something to describe and call:
- The simplest thing that can happen in a program is an operating system or hardware failure. The failed system call CreateEvent () or pthread_mutex_lock (), division by zero, garbage in the results of the system call - all this can happen for various reasons, ranging from broken iron and ending with viruses in the system, but usually from us and our program it is not very dependent.
- A slightly more complicated situation is the lack of the resources we need. Unexpectedly, memory, handles, file descriptors can run out. May not have rights to write or read the necessary files. Pipe may not open. Or vice versa - do not close. Access to the database may or may not be. Such a situation may already be caused by our program (it took too much memory) and the instability of the system (the virus took too much memory of the virus).
- And the most common situation is an error in the logic of the program or the interaction of its parts. We are trying to remove a non-existing list item, call the method with invalid arguments, perform a non-atomic operation in a multi-threaded manner. As a rule, this leads to either incorrect behavior of the program (the “toolbar disappeared”) or its crash with an access violation / unhandled exception.
As you can see, a lot of different things and bad things can happen - but this is not a complete list :). And what should a programmer do? Here, in my opinion, we are confronted with a very interesting and important question - how exactly does our program respond to this or that error? Perhaps now I will remind you once again that I am expressing my purely personal opinion. And I will say the following - exactly how to react to an error entirely depends on the specific program. If we have run out of memory in the driver, we must survive at all costs so that the user does not get a blue screen of death. If we have a memory in a toy like a happy farm, then it makes sense to drop, apologize and ask to send the bug report to the developer. The system service, designed to spin for many months without rebooting, should be sympathetic to the CreateEvent () error. This error in an application such as Photoshop means that the system will most likely die in a second, and it is better to fall honestly rather than try to swallow the error, let the user save the file and safely spoil it due to a subsequent failure during recording. Consequently, we can divide errors into
expected and
unexpected . For different programs and different requirements, the same errors can be considered both expected and unexpected. With the expected errors, we somehow work. It was not possible to open the file - we are talking about this to the user and continue to work. Failed to allocate memory for loading a gigabyte file there - we are talking about this to the user and continue to work. We do not work with unexpected errors in most cases. Out of memory when trying to allocate twenty bytes to create an object - we fall. Not created a system object which for the whole program three pieces - falling. Can not read the system pipe that the specification should read? It is better to drop than to leave the program in an unstable state and then spoil the user data. If he restarts the program, he will hate the damaged file until the end of his days. And for serious cases there is an autosave and restarting us if what the watchdog.
')
What happened to the exceptions?
In the heyday of procedural programming, the error handling syntax was trivial and was based on what the function returned. If the function returns TRUE, everything is fine; if FALSE, an error has occurred. At the same time, two approaches to working with errors immediately stood out:
- Two-in-one approach - the function returns FALSE or a null pointer for both expected and unexpected errors. Such an approach was usually applied in the general-purpose API and in the code of user programs, when most of the errors could be safely considered fatal and fall. For those rare cases when sharing was still needed, some kind of additional machinery like GetLastError () was used. A snippet of that time copying data from one file to another and returning an error in case of any problems:
BOOL Copy( CHAR* sname, CHAR* dname )
{
FILE *sfile = 0, *dfile = 0;
void* mem = 0;
UINT32 size = 0, written = 0;
BOOL ret = FALSE;
sfile = fopen( sname, "rb" );
if( ! sfile ) goto cleanup;
dfile = fopen( dname, "wb" );
if( ! dfile ) goto cleanup;
mem = malloc( F_CHUNK_SIZE );
if( ! mem ) goto cleanup;
do
{
size = fread( sfile, mem, F_CHUNK_SIZE );
written = fwrite( dfile, mem, size );
if( size != written ) goto cleanup;
}
while( size )
ret = TRUE;
cleanup:
if( sfile) fclose( sfile );
if( dfile) fclose( dfile );
if( mem ) free( mem );
return ret;
}
- , FALSE , ( error), . , apache, ( ) ( , 20 ). , ( FALSE) ( HANDLE).
BOOL Copy( CHAR* sname, CHAR* dname, OUT HANDLE* error )
{
HANDLE sfile = 0, dfile = 0, data = 0;
UINT32 size = 0;
ENSURE( PoolAlloc() );
ENSURE( FileOpen( sname, OUT& sfile, OUT error ) );
REQUIRE( SUCCESS( error ) );
ENSURE( FileOpen( dname, OUT& dfile, OUT error ) );
REQUIRE( SUCCESS( error ) );
ENSURE( MemAlloc( OUT& data ) );
REQUIRE( SUCCESS( error ) );
do
{
ENSURE( FileRead( sfile, F_CHUNK_SIZE, OUT& data, OUT error ) );
REQUIRE( SUCCESS( error ) );
ENSURE( FileWrite( dfile, & data ) );
REQUIRE( SUCCESS( error ) );
ENSURE( MemGetSize( OUT& size ) )
}
while( size );
ENSURE( PoolFree() );
return TRUE;
}
, , — .
, . (fopen, fclose) . (BOOL ret , ENSURE ) .
— . — , , . — if REQUIRE — . , :
- — — .
void Copy( string sname, string dname )
{
file source( sname );
file destination( sname );
source.open( "rb" );
destination.open( "wb" );
data bytes;
do
{
bytes = source.read( F_CHUNK_SIZE );
destination.write( bytes )
}
while( bytes.size() )
}
- — / nullable :
bool Copy( string sname, string dname )
{
file source( sname );
file destination( sname );
if( ! source.open( "rb" ) || ! destination.open( "wb" ) ) return false;
data bytes;
do
{
bytes = source.read( F_CHUNK_SIZE );
if( bytes.isValid() )
{
if( ! destination.write( bytes ) ) return false;
}
}
while( bytes.isValid() && bytes.size() )
}
?
, :). , . . . — . — - ? :
- , C++, . , . , .
- API . (checked) (unchecked), API , .
- « ». , , try catch , .
. , , — C++. « », « » « » C++. , API , . , — API , .