Friday, December 15, 2006

Exceptions (part 2)

So we have seen on my last post that throwing exceptions and functions call have some similarities, we have also seen that throw an object always means copy it even if we catch for reference, as all copies in C++ are based on the static type then even in the exceptions environments the objects thrown are base on the static type.


Not always throw an exception assures you to avoid memory leakages even using "automatic objects", consider this example:



class Foo {
public:
Foo();
~Foo()
private:
AType* aPointer;
BType* bPointer;
};

Foo::Foo()
:aPointer(new AType),
bPointer(new BType)
{ }


we know that the initializer list order depends on the order declaration in the definition of class,
so in this case aPointer is initialized first then bPointer. What happens if the "new BType" throws an exception? Well, given the fact aPointer is a plain pointer then we will have memory leakage. So a first thought can be to use not plain pointers but something like "smart pointer".
So a first approach can be the following:



class Foo {
public:
Foo();
~Foo()
private:
std::auto_ptr aPointer;
std::auto_ptr bPointer;
};

Foo::Foo()
:aPointer(new AType),
bPointer(new BType)
{ }


well this is still not safe. Let see the constructor execution sequence:

1) Constructor AType is executed (new AType)
2) Constructor BType is executed (new BType)
3) Constructor std::auto_ptr is executed ( aPointer( ... ) )
4) Constructor std::auto_ptr is executed ( bPointer( ... ) )

do you see know where the problem is? If still "new BType" throws an exception the address of memory allocated by new AType was still not saved anywhere; unfortunately the correct way to solve this problem is the following:




class Foo {
public:
Foo();
~Foo()
private:
std::auto_ptr aPointer;
std::auto_ptr bPointer;
};

Foo::Foo()
:aPointer(),
bPointer()
{
aPointer = std::auto_ptr(new AType);
bPointer = std::auto_ptr(new BType);
}



throwing an exception can also leave the object in an inconsistent state, consider the following class (do not consider the fact that the class is useless):



class Foo {
public:
Foo()
:theStorage()
{ }

addInt(int anInteger) {
theStorage.push_back(anInteger);
}

void sumOne() {
int i;
for (i=0; i < theStorage.size(); ++i) {
theStorage[i] += 1;
if (i==2) {
throw std::runtime_error("OPS!");
}
}

private:
std::vector theStorage;
};



and his usage:



Foo aFoo;

aFoo.addInt(0);
aFoo.addInt(1);
aFoo.addInt(2);
aFoo.addInt(3);



at this point calling:



aFoo.sumOne();



will throw an exception leaving aFoo with partial updated elements, and from user point of view the aFoo is in an inconsistent state, so the sumOne() function shows here another problem that can break the exception safety of a class. The solution on this kind of problems is to work on a copy of internal class state and then make a swap between the internal state and the modified state.

Tuesday, November 28, 2006

Exceptions (part 1)

As you already know a modern way to deal with "errors" in C++ is the exception handling; however you need to be careful on using this mechanism. Let see how it works and some tips as well.
The exception mechanism is based on the try - catch blocks:




try {
//some code in here that we attempt to execute
}
catch (...) {
// this block is the error handling, the code in this
// block is executed if the code in the
// try block above have thrown an exception
}



easy and net.

Let see how throw an exception so we can see more in depth what this mechanism offers, how use it, what to avoid.
An exception is thrown with a throw:



throw A;



where A is the type of the object thrown, in that case we are throwing an object of type A inizialized with his default constructor, we could have done:



throw A(3, "foo");



or even:



A a(3, "foo");
throw a;



The catch(...) { } handler is supposed to handle all kind of exception that code inside the try block throws, in this way we lose the kind of exception thrown so it's a bit reductive because the error handler doesn't have a clue on what is going on; fortunately is possible to specify wich kind of exceptions we want to manage (we as well ignore some).
Specify which kind of exception we want manage is done in this way:



catch(Foo f) {
}



we can have multiple catch blocks after a try:




try {
//code we are attempting to execute
}
catch(Foo f) {
// error handler in case a Foo type was thrown
}
catch(Bar b) {
// error handler in case a Bar type was thrown
}



if the code in the try block thrown an exception that is not Foo and neither Bar then is like we are not using the try-catch blocks ( apart the introduction of the try's extra scope ).

We can obtain the same behaviour (logging for example we were not able to catch any expected exception) in this way:



try {
//code we are attempting to execute
}
catch(Foo f) {
// error handler in case a Foo type was thrown
}
catch(Bar b) {
// error handler in case a Bar type was thrown
}
catch(...) {
// log the event in here
throw; // this throws again the same exception.
}



There is still something behind all this. As you have seen the catch blocks are very similar to a function declaration where the arguments are passed by value.
The catch blocks can have indeed all kind of parameters:

catch( T )
catch( T & )
catch( T * )
catch( const T )
catch( const T & )
catch( const T * )

so you can think of throwing an exception have same effect of calling a function, but is not.
Consider this code:



void foo()
{
...
A aLocalObject;
...

throw aLocalObject
}



and the call of foo is inside a try block:



try {
foo();
}
catch( A anException) {

}




in this case, as the catch "signature" suggests, the anException is a copy of aLocalObject so
no problem with it, but what if we catch by reference?



catch( A & anException ) {
}



in this case we can think to have a reference to a destroyed object ( when an exception is thrown is not like a function call and all the local variable on that scope are destroyed ), well this is not the case indeed even if you catch an exception by reference the c++ runtime support will perform a copy of the object thrown, and this happens always, you can not avoid this copy even if the object will not be destroyed going out of scope ( a static variable for example ).

So in case of:

catch( A ) { }

you have 2 copies performed, in case of:

catch ( A & )

you have just one copy. For this very reason is not possible to modify the object thrown, because you have on the catch block a copy of it.

Next week the second and last part about exceptions.

Wednesday, November 22, 2006

operator new - new operator

At first shot this two entities can appear to be the same; however they are not.
Let see what they are, what you can change and the safe rules to handle them.

First of all let see what happens when you write something like:

C * pC = new C;
  • Enough memory is allocated to contain the object requested
  • The constructor of C is called to initialize the object the lays in the memory allocated
This described is the new operator behave and you can not change the way he acts.

The first point in the sequence above is the only think you can change, the way the memory is allocated, the new operator uses for this task what is called: operator new.

So the pseudo code for C * pC = new C; could be:
  1. Call operator new
  2. Construct an object of the type request at the location returned from previous step
The operator new signature is something like this:

void * operator new(size_t);

so if you want change the way the new operator allocates the memory for your type then you need to rewrite the operator new.
As you already know when you specify a name in a scope ( for example a method name in a class ) this will hide the same name in the scopes that are containing your actual ( the base class scope for example ). So rewriting your operator new what you do is to hide the other forms of operator new. For instance these forms are:
  1. void * operator new(std::size_t, std::nothrow_t) throw();
  2. void * operator new(std::size_t, void *);
the former is the nothrow new the latter is the in place new. Actualy you can break more than this if you define your own operator new, something like:
  • void * operator new(std::size_t, T);
remember in this case that first argument of operator new shall be always std::size_t, in this case you will hide not only the "less common" operator new version but also the plain new one. I quoted less common because in reality the STL does heavy usage of in place new.