jeffrey@toad.net
October 16th, 2005, 12:19 PM
I hope I do not get a religious argument started, but here goes...
When I program, I try to write code that is easy to read, and easy to use. This should make it easy to maintain, and reuse by others (copy and paste).
The most generic way I know to control flow through a function is with goto statements (while keeping the function easy to read).
Consider the following:
LONG CAESEncRegKey::ReadData(BYTE **pcbData, DWORD *pdwSize) const
{
LONG lResult = ERROR_SUCCESS;
HKEY hKey = NULL;
ASSERT( NULL != pcbData && NULL != pdwSize );
// Sanity Check
if( NULL == pcbData || NULL == pdwSize ) {
lResult = ERROR_INVALID_PARAMETER;
goto FINISHED;
}
// Open the Key
lResult = RegOpenKeyEx( _hKey, _szSubKey, 0, KEY_READ, &hKey );
// Sanity Check
if( ERROR_SUCCESS != lResult ) { goto FINISHED; }
// Query for needed buffer size
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, NULL, pdwSize );
if( ERROR_SUCCESS != lResult ) { goto FINISHED; }
// Allocate a buffer
*pcbData = new BYTE[ *pdwSize + sizeof(TCHAR) ];
// Sanity Check
if( NULL == *pcbData ) {
lResult = ERROR_NOT_ENOUGH_MEMORY;
goto FINISHED;
}
// Query for the actual value
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, *pcbData, pdwSize );
FINISHED:
if( NULL != hKey ) { RegCloseKey( hKey ); }
return lResult;
}
If using it/then/else, the code could be nested four levels. I don’t think this is easy to read (again, my humble opinion).
Another option would be to use try/throw/catch. However, this option would force the program to use Exception handling, which is something a programmer may not want to do. For example, eVC 4.0 does not implement exception handling, so one could write a program/class/function that is basically useless.
With that said, the following usess Exceptions:
LONG CAESEncRegKey::ReadData(BYTE **pcbData, DWORD *pdwSize) const
{
LONG lResult = ERROR_SUCCESS;
HKEY hKey = NULL;
try {
// Sanity Check
if( NULL == pcbData || NULL == pdwSize ) {
throw (LONG) ERROR_INVALID_PARAMETER;
}
// Open the Key
lResult = RegOpenKeyEx( _hKey, _szSubKey, 0, KEY_READ, &hKey );
// Sanity Check
if( ERROR_SUCCESS != lResult ) {
throw (LONG) lResult;
}
// Query for needed buffer size
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, NULL, pdwSize );
if( ERROR_SUCCESS != lResult ) {
throw (LONG) lResult;
}
// Allocate a buffer
*pcbData = new BYTE[ *pdwSize + sizeof(TCHAR) ];
// Sanity Check
if( NULL == *pcbData ) {
throw (LONG) ERROR_NOT_ENOUGH_MEMORY;
}
// Query for the actual value
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, *pcbData, pdwSize );
}
catch ( LONG lr ) {
lResult = lr;
}
catch ( ... ) {
lResult = ERROR_GEN_FAILURE;
}
if( NULL != hKey ) { RegCloseKey( hKey ); }
return lResult;
}
I suppose my question is, what is the preferred method?
Jeff
When I program, I try to write code that is easy to read, and easy to use. This should make it easy to maintain, and reuse by others (copy and paste).
The most generic way I know to control flow through a function is with goto statements (while keeping the function easy to read).
Consider the following:
LONG CAESEncRegKey::ReadData(BYTE **pcbData, DWORD *pdwSize) const
{
LONG lResult = ERROR_SUCCESS;
HKEY hKey = NULL;
ASSERT( NULL != pcbData && NULL != pdwSize );
// Sanity Check
if( NULL == pcbData || NULL == pdwSize ) {
lResult = ERROR_INVALID_PARAMETER;
goto FINISHED;
}
// Open the Key
lResult = RegOpenKeyEx( _hKey, _szSubKey, 0, KEY_READ, &hKey );
// Sanity Check
if( ERROR_SUCCESS != lResult ) { goto FINISHED; }
// Query for needed buffer size
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, NULL, pdwSize );
if( ERROR_SUCCESS != lResult ) { goto FINISHED; }
// Allocate a buffer
*pcbData = new BYTE[ *pdwSize + sizeof(TCHAR) ];
// Sanity Check
if( NULL == *pcbData ) {
lResult = ERROR_NOT_ENOUGH_MEMORY;
goto FINISHED;
}
// Query for the actual value
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, *pcbData, pdwSize );
FINISHED:
if( NULL != hKey ) { RegCloseKey( hKey ); }
return lResult;
}
If using it/then/else, the code could be nested four levels. I don’t think this is easy to read (again, my humble opinion).
Another option would be to use try/throw/catch. However, this option would force the program to use Exception handling, which is something a programmer may not want to do. For example, eVC 4.0 does not implement exception handling, so one could write a program/class/function that is basically useless.
With that said, the following usess Exceptions:
LONG CAESEncRegKey::ReadData(BYTE **pcbData, DWORD *pdwSize) const
{
LONG lResult = ERROR_SUCCESS;
HKEY hKey = NULL;
try {
// Sanity Check
if( NULL == pcbData || NULL == pdwSize ) {
throw (LONG) ERROR_INVALID_PARAMETER;
}
// Open the Key
lResult = RegOpenKeyEx( _hKey, _szSubKey, 0, KEY_READ, &hKey );
// Sanity Check
if( ERROR_SUCCESS != lResult ) {
throw (LONG) lResult;
}
// Query for needed buffer size
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, NULL, pdwSize );
if( ERROR_SUCCESS != lResult ) {
throw (LONG) lResult;
}
// Allocate a buffer
*pcbData = new BYTE[ *pdwSize + sizeof(TCHAR) ];
// Sanity Check
if( NULL == *pcbData ) {
throw (LONG) ERROR_NOT_ENOUGH_MEMORY;
}
// Query for the actual value
lResult = RegQueryValueEx( hKey, _szValueName, 0, NULL, *pcbData, pdwSize );
}
catch ( LONG lr ) {
lResult = lr;
}
catch ( ... ) {
lResult = ERROR_GEN_FAILURE;
}
if( NULL != hKey ) { RegCloseKey( hKey ); }
return lResult;
}
I suppose my question is, what is the preferred method?
Jeff