ramu said:
Hi,
Why exceptions ?
Suppose we are writing a code like this
class test { };
int main()
{
test *ob = new test;
if (ob == NULL)
cout<<"Memory is not allocated"<<endl;
//....
//...
}
Like that we can check any issue using if conditions. Then what is the
need to put the code inside a try block and have a catch block for
that. This may be only a overhead ?!
For a single function call, of course it's not obvious. But programs
are more complex, and it's a good thing to be able to combine function
calls in a single expression.
For example:
void doUpdateSelectedShopInventory(UserContext userContext){
try{
doTheSameChangeInShop(getSelectedShop(userContext),
getChangedDataFromInventory(getInventory(userContext),
getLastSynchronizationDate(userContext)));
}catch(NeverSynchronized& e){
copyInventoryToShop(getInventory(userContext),getSelectedShop(userContext));
}catch(InventoryNotChanged& e){
reportWarning("That inventory didn't change");
}
}
Notice that we didn't handle the DatabaseException, and the
NoShopSelected exceptions. These will be caught and handled by the
functions that call doUpdateSelectedShopInventory(). Here, we just
handle what we know how to handle.
try{
switch(userCommand){
case CmdUpdateSelectedShop:
try{
doUpdateSelectedShopInventory(userContext);
}catch(NoShopSelected& e){
reportMessage("Please select a shop first");
}
break;
...
}
}catch(DatabaseException& e){
reportError("Cannot use the database now, please try again later.");
}
The main problem if you try to avoid exceptions, is that you cannot
combine function calls any more. Either the returned value encodes an
error, or you have an additionnal output parameter to signal an error.
In both cases, unless you implement what I describe in:
http://groups.google.com/group/comp.programming/msg/2b899c6850cc731d?hl=en (*)
you would have to transform the function code into procedural code such as:
ObjectWithError doUpdateSelectedShopInventory(UserContext userContext){
Shop shop=getSelectedShop(userContext);
if(shop->hasError()){
if(shop->isErrorNoShop()){
reportMespsage("Please select a shop first");
// Well, what if this function is not called in the
// context of a user selected shop, but that of a batch
// processing?
}else{
// what should we do?
return(something);
}
}else{
Inventory inventory=getInventory(userContext);
if(inventory->hasError()){
return(something); // notice, Shop is not Inventory, we need to return a superclass?
}
Date date=getLastSynchronizationDate(userContext);
if((date->hasError())
and (date->errorNeverSynchronized())){
copyInventoryToShop(inventory,shop);
}else if(date->hasError()){
return(something);
}else{
InventoryChanges data=getChangedDataFromInventory(inventorydate);
if(data->hasError()){
if(data->errorInventoryNotChanged()){
reportWarning("That inventory didn't change");
}else{
return(something);
}
}else{
doTheSameChangeInShop(shop,data);
}
}
}
}
So you can see that the result is rather unreadable, less maintainable
(be careful with these if/else...), imposes more complexities in the
object model unrelated to the domain (include "error" in Shop,
Inventory?, put them in an unrelated superclass such as
ObjectWithError?), makes you handle errors in the wrong place, or to
handle the transmission of the error up to the callers. In effect,
you are badly and inefficiently re-implementing exceptions. (See
Greenspun's Tenth Law).
(*) In C++, you could use templates instead of macros to wrap the
types like in this example, but you will admit that it's a lot of
work...