Auto destruct

J

JKop

Using MSWindows as an example:

On MSWindows, there's a thing called the System Registry, which is a really
big database that holds all the settings of the OS. There's API's for
working with the registry. The two I will be concerned with are:

RegOpenKeyEx
RegCloseKey

Now, here's some code:

int main()
{
HKEY hkey;

RegOpenKeyEx( &hkey );

... //some stuff

RegCloseKey( hkey );
}


The only problem here is that if an exception is thrown during "some stuff",
then the key never gets closed. Here was my first solution:

class HKEYe
{
public:

HKEY hkey;
bool to_be_closed;

HKEYe() : to_be_closed(false) {}

~HKEYe()
{
if (to_be_closed)
{
RegCloseKey(hkey);
}
}

operator HKEY&() { return hkey; }
operator const HKEY&() const { return hkey; }


HKEY* operator&() { return &hkey; }
const HKEY* operator&() const { return &hkey; }
};

int main()
{
HKEYe hkey;

RegOpenKeyEx( &hkey );

hkey.to_be_closed = true;

//... some stuff

RegCloseKey(hkey);
hkey.to_be_closed = false;

}


Now if an exception is thrown, then the key does get closed.

Anyway, my next thought was to make a template out of this. Has this been
done before? Anyway, here's my first attempt:


template<class T>
class AutoDestructive
{
public:

T t;
bool to_be_auto_destructed;

AutoDestructive() : t(), to_be_auto_destructed(false) {}
template<class A> AutoDestructive(A a) : t(a), to_be_auto_destructed
(false) {}
template<class A> AutoDestructive(const A a) : t(a),
to_be_auto_destructed(false) {}
template<class A, class B> AutoDestructive(A a, B b) : t(a,b),
to_be_auto_destructed(false) {}
template<class A, class B, class C> AutoDestructive(A a, B b, C c) : t
(a,b,c), to_be_auto_destructed(false) {}

virtual ~AutoDestructive() = 0;

operator T&() { return t; }
operator const T&() const { return t; }


T* operator&() { return &t; }
const T* operator&() const { return &t; }
};

class AutoDestructive_HKEY : public AutoDestructive<HKEY>
{
public:

~AutoDestructive_HKEY()
{
if (to_be_auto_destructed) RegCloseKey(t);
}
};

int main()
{
AutoDestructive_HKEY hkey;

LONG error_code = RegOpenKeyEx(HKEY_CLASSES_ROOT,
"*",
0,
KEY_QUERY_VALUE|KEY_SET_VALUE,
&hkey);

if (error_code == ERROR_SUCCESS)
{
hkey.to_be_auto_destructed = true;
}
}


Comments, questions, suggestions welcomed.

-JKop
 
A

Alf P. Steinbach

* JKop:
Now if an exception is thrown, then the key does get closed.

Anyway, my next thought was to make a template out of this. Has this been
done before?

Petri Marginean's ScopeGuard-class.

See the article about ScopeGuard (Andrei Alexandrescu & Petri Marginean) at
<url: http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/>, source code
at <url: ftp://ftp.cuj.com/pub/2000/cujdec2000.zip>.

Note that for MSVC you'll have to replace use of standard __LINE__ with
Microsoft __COUNTER__ to make it work in general, at least if you're using the
"edit & continue" option of that compiler (it's a bit broken).
 
N

Niels Dybdahl

...
Comments, questions, suggestions welcomed.

I prefer writing such a class like this:

class CRegKey {
public:
HKEY hkey;
CRegKey() {
if (RegOpenKeyEx( &hkey ))
throw...
}
~CRegKey() {
RegCloseKey( hkey );
}
};

int main()
{
{
CRegKey regKey;
...
}
};

The code for the class is so small that it is not worth making a template
for it. An invariant for the class is that the handle is open, so you do not
have to test if it is open, you do not have to remember to open it and you
do not have to remember to close it. As long as it is in scope it is open.

Niels Dybdahl
 
P

Peter van Merkerk

JKop said:
Using MSWindows as an example:

On MSWindows, there's a thing called the System Registry, which is a really
big database that holds all the settings of the OS. There's API's for
working with the registry. The two I will be concerned with are:

RegOpenKeyEx
RegCloseKey

Now, here's some code:

int main()
{
HKEY hkey;

RegOpenKeyEx( &hkey );

... //some stuff

RegCloseKey( hkey );
}


The only problem here is that if an exception is thrown during "some stuff",
then the key never gets closed. Here was my first solution:

class HKEYe
{
public:

HKEY hkey;
bool to_be_closed;

HKEYe() : to_be_closed(false) {}

~HKEYe()
{
if (to_be_closed)
{
RegCloseKey(hkey);
}
}

operator HKEY&() { return hkey; }
operator const HKEY&() const { return hkey; }


HKEY* operator&() { return &hkey; }
const HKEY* operator&() const { return &hkey; }
};

int main()
{
HKEYe hkey;

RegOpenKeyEx( &hkey );

hkey.to_be_closed = true;

//... some stuff

RegCloseKey(hkey);
hkey.to_be_closed = false;

}


Now if an exception is thrown, then the key does get closed.

Anyway, my next thought was to make a template out of this. Has this been
done before? Anyway, here's my first attempt:


template<class T>
class AutoDestructive
{
public:

T t;
bool to_be_auto_destructed;

AutoDestructive() : t(), to_be_auto_destructed(false) {}
template<class A> AutoDestructive(A a) : t(a), to_be_auto_destructed
(false) {}
template<class A> AutoDestructive(const A a) : t(a),
to_be_auto_destructed(false) {}
template<class A, class B> AutoDestructive(A a, B b) : t(a,b),
to_be_auto_destructed(false) {}
template<class A, class B, class C> AutoDestructive(A a, B b, C c) : t
(a,b,c), to_be_auto_destructed(false) {}

virtual ~AutoDestructive() = 0;

operator T&() { return t; }
operator const T&() const { return t; }


T* operator&() { return &t; }
const T* operator&() const { return &t; }
};

class AutoDestructive_HKEY : public AutoDestructive<HKEY>
{
public:

~AutoDestructive_HKEY()
{
if (to_be_auto_destructed) RegCloseKey(t);
}
};

int main()
{
AutoDestructive_HKEY hkey;

LONG error_code = RegOpenKeyEx(HKEY_CLASSES_ROOT,
"*",
0,
KEY_QUERY_VALUE|KEY_SET_VALUE,
&hkey);

if (error_code == ERROR_SUCCESS)
{
hkey.to_be_auto_destructed = true;
}
}


Comments, questions, suggestions welcomed.

Congratulations, you just "invented" the RAII idiom. The std::auto_ptr
class in standard library is example of the RAII idiom. This idiom is
described in the book "The C++ Programming Language" from Bjarne
Stroustrup. Using the destructor to do proper clean up is a well known
C++ way to write exception safe code.

This idiom can be used for many more cases than just releasing memory or
handles. For example in a GUI application you could write a class that
in the constructor changes the cursor shape to an hourglass and in the
destructor restores the cursor to its previous shape.

void HeavyProcessing()
{
WaitCursor wc;

// Do some stuff here....
// Exceptions or returns anywhere in the
// function will restore the cursor shape.
}
 
T

Thore Karlsen

[...]
Now if an exception is thrown, then the key does get closed.

Anyway, my next thought was to make a template out of this. Has this been
done before?

Yes. For registry keys, you have CRegKey in ATL 7.x. A more generic
solution can be found in the ScopeGuard class, as mentioned by Alf. You
can also take a look at the SmartAny library in the PowerTools package
at:

http://www.gotdotnet.com/team/cplusplus/
 
J

JKop

Alf P. Steinbach posted:
* JKop:

Petri Marginean's ScopeGuard-class.

See the article about ScopeGuard (Andrei Alexandrescu & Petri
Marginean) at
<url: http://www.cuj.com/documents/s=
8000/cujcexp1812alexandr/>, source
code
at <url: ftp://ftp.cuj.com/pub/2000/cujdec2000.zip>.

Note that for MSVC you'll have to replace use of standard __LINE__ with
Microsoft __COUNTER__ to make it work in general, at least if you're
using the "edit & continue" option of that compiler (it's a bit
broken).

Seems to me that the only problem is that you can't
specify arguments to the function. I suppose an inline
function with global references/pointers would do the job,
but how and ever.


I might keep going with my template and see how it goes.

First thing though, is there any way for a derived class
to inherit the constructors of its base? eg.

class Base
{
public:

Base() {}
Base(int) {}
Base(double&) {}
};

class Derived : public Base
{

};

int main()
{
double k;

Derived monkey(k);
}


I know that you can do the following:

class Derived : public Base
{
public:

Derived() : Base() {}
Derived(int) : Base(int) {}
Derived(double&) : Base(double&) {}
};

But this will be very tedious if there's dozens of
contructors in the base. For example, for my template
class, the base will have loads of constructors so that
you can actually use the constructor of the type you're
"protecting".


-JKop
 
J

JKop

g++ cnt.cpp -ansi -pedantic -o c.exe

C:\WINDOWS\TEMP/ccgrbaaa.o(.text$_ZN15AutoDestructiveIiED2Ev+0x16):cnt.cpp:
undefined reference to `AutoDestructive<int>::CleanUp()'


Can some-one else please try compile it and see what happens.

Anyone know what's wrong with the code?


Here's the code:


typedef int HKEY;
const int ERROR_SUCCESS(5);

void RegCloseKey(HKEY hkey) {}
int RegOpenKeyEx(HKEY *hkey) { return ERROR_SUCCESS; }

template<class T>
class AutoDestructive
{
public:

T t;
bool to_be_auto_destructed;

AutoDestructive() : t(), to_be_auto_destructed(false) {}

virtual void CleanUp() = 0;

~AutoDestructive()
{
this->CleanUp();
}

operator T&() { return t; }
operator const T&() const { return t; }


T* operator&() { return &t; }
const T* operator&() const { return &t; }
};


class AutoDestructive_HKEY : public AutoDestructive<HKEY>
{
public:

virtual void CleanUp()
{
if (to_be_auto_destructed)
{
RegCloseKey(t);
to_be_auto_destructed = false;
}
}
};


int main()
{
AutoDestructive_HKEY hkey;

int error_code = RegOpenKeyEx(&hkey);

if (error_code == ERROR_SUCCESS)
{
hkey.to_be_auto_destructed = true;
}
}


-JKop
 
I

Ioannis Vranos

JKop said:
Using MSWindows as an example:

On MSWindows, there's a thing called the System Registry, which is a really
big database that holds all the settings of the OS. There's API's for
working with the registry. The two I will be concerned with are:

RegOpenKeyEx
RegCloseKey

Now, here's some code:

int main()
{
HKEY hkey;

RegOpenKeyEx( &hkey );

... //some stuff

RegCloseKey( hkey );
}


The only problem here is that if an exception is thrown during "some stuff",
then the key never gets closed. Here was my first solution:

class HKEYe
{
public:

HKEY hkey;
bool to_be_closed;

HKEYe() : to_be_closed(false) {}

~HKEYe()
{
if (to_be_closed)
{
RegCloseKey(hkey);
}
}

operator HKEY&() { return hkey; }
operator const HKEY&() const { return hkey; }


HKEY* operator&() { return &hkey; }
const HKEY* operator&() const { return &hkey; }
};

int main()
{
HKEYe hkey;

RegOpenKeyEx( &hkey );

hkey.to_be_closed = true;

//... some stuff

RegCloseKey(hkey);
hkey.to_be_closed = false;

}


Now if an exception is thrown, then the key does get closed.



System specific stuff but a generic issue. :) Check "Resource
Acquisition is Initialisation" technique (RAII in short) even in my
messages of the past. :)






Regards,

Ioannis Vranos

http://www23.brinkster.com/noicys
 
J

JKop

~AutoDestructive()
{
this->CleanUp();
}

The probem is with the above.

The following doesn't compile either:

class Base
{
public:

virtual void Monkey() = 0;

~Base()
{
this->Monkey();
}
};

class Derived : public Base
{
public:

virtual void Monkey()
{

}
};

int main()
{
Derived checker;
}


WHY?


-JKop
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

JKop said:
The probem is with the above.

You can't safely call virtual functions inside contructors and destructors,
because the state of the object is not completely defined.
 
V

Victor Bazarov

Julián Albo said:
JKop wrote:




You can't safely call virtual functions inside contructors and destructors,
because the state of the object is not completely defined.

Why do you say that? What is exactly not completely defined in the
object's state? The base classes and members have been initialised
when the body of the c-tor is entered, and are still around when
the body of the d-tor is executing. What _else_ is there?

Virtual functions are not linked dynamically from c-tors and d-tors
and instead are statically linked, but that, again, should not be
a problem: the 'CleanUp' member of 'AutoDestructive' should only
require base classes and members for its functionality, and not any
of potential derived classes, no?

So, what's the problem with safety?

V
 
T

tom_usenet

g++ cnt.cpp -ansi -pedantic -o c.exe

C:\WINDOWS\TEMP/ccgrbaaa.o(.text$_ZN15AutoDestructiveIiED2Ev+0x16):cnt.cpp:
undefined reference to `AutoDestructive<int>::CleanUp()'


Can some-one else please try compile it and see what happens.

Anyone know what's wrong with the code?


Here's the code:


typedef int HKEY;
const int ERROR_SUCCESS(5);

void RegCloseKey(HKEY hkey) {}
int RegOpenKeyEx(HKEY *hkey) { return ERROR_SUCCESS; }

template<class T>
class AutoDestructive
{
public:

T t;
bool to_be_auto_destructed;

AutoDestructive() : t(), to_be_auto_destructed(false) {}

virtual void CleanUp() = 0;

~AutoDestructive()
{
this->CleanUp();

There's your problem - calling a virtual function from a destructor
doesn't do what you think it does. Calling a pure virtual function
from a destructor causes undefined behaviour (and fortunately VC seems
to indicate this with a linker error, which is as nice as undefined
behaviour gets).

Basically, destruction goes for typical implementations:

derived class destructor called
derived members destructed in reverse order of declaration
*vtable pointer updated to point to base class vtable*
base class destructor called
base class members destructed in reverse order of declaration

The emphasised line is what causes the problem for you - calling a
virtual function from a constructor or destructor calls the function
in the base object currently being constructed or destructed, not the
one in the most derived class.

Tom
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Victor said:
Why do you say that? What is exactly not completely defined in the
object's state? The base classes and members have been initialised
when the body of the c-tor is entered, and are still around when
the body of the d-tor is executing. What _else_ is there?

The subobject of the base class, yes. The complete object, not.
Virtual functions are not linked dynamically from c-tors and d-tors
and instead are statically linked, but that, again, should not be
a problem: the 'CleanUp' member of 'AutoDestructive' should only
require base classes and members for its functionality, and not any
of potential derived classes, no?

The CleanUp member of AutoDestructive is pure virtual. You can't call it
without a complete object of a derived class,
 
V

Victor Bazarov

Julián Albo said:
Victor Bazarov wrote:

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Do you see the word "pure" there?
The subobject of the base class, yes. The complete object, not.

Really? What is "the complete object"? Why is it "not"?

#include <iostream>

class SomeClass : public SomeBase, SomeBase2 {
int somedata;
public:
virtual void foo() {
std::cout << somedata;
}
~SomeClass() {
// what's not defined here?
// why is it unsafe to call 'foo'?
}
};

Please elaborate on "complete object" using the example above.
The CleanUp member of AutoDestructive is pure virtual. You can't call it
without a complete object of a derived class,

Yes, but calling a pure virtual function that causes undefined behaviour.
Not "unsafe", just "undefined". Besides, in your posting there was just
a statement that a virtual function cannot be "safely" called.

Care to post a correction to it?

Victor
 
J

JKop

There's your problem - calling a virtual function from a destructor
doesn't do what you think it does. Calling a pure virtual function
from a destructor causes undefined behaviour (and fortunately VC seems
to indicate this with a linker error, which is as nice as undefined
behaviour gets).

Basically, destruction goes for typical implementations:

derived class destructor called
derived members destructed in reverse order of declaration
*vtable pointer updated to point to base class vtable*
base class destructor called
base class members destructed in reverse order of declaration

The emphasised line is what causes the problem for you - calling a
virtual function from a constructor or destructor calls the function
in the base object currently being constructed or destructed, not the
one in the most derived class.

Tom


Okay, is there anything wrong with the following though? It compiles for me:


class Base
{
public:

virtual void Monkey() = 0;

void HelperMonkey()
{
this->Monkey();
}

~Base()
{
HelperMonkey();
}
};

class Derived : public Base
{
public:

virtual void Monkey()
{

}
};

int main()
{
Derived checker;
}


-JKop
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Victor said:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Do you see the word "pure" there?

I was answering the OP question in his context, not writing a book. Thanks
for yor review, anyway.
 
J

JKop

It doesn't work. I ran the program and the Win OS printed:

pure virtual method called

This application has requested the Runtime to terminate it
in an unusual way.
Please contact the application's support team for more
information.


A nice bit of help there!


I'm off to redesign it.


-JKop
 
J

JKop

I'm very nearly finished my template. It's "const" compatible, if you know
what I mean, eg. you can define a both a non-const object and a const object
with it and they'll behave as expected. Here's how you use it: First you
make a function like so

void CleanUp_HKEY(HKEY& hkey, bool& to_be_closed)
{
if (to_be_closed)
{
RegCloseKey(hkey);
to_be_closed = false;
}
}

And you define your object as so:

AutoDestructive<HKEY,CleanUp_HKEY> my_key;

To gain access to the actual HKEY object, you use function notation, eg. to
pass it to a function that takes a HKEY:

SomeFunct(my_key());

Or even just setting it yourself:

my_key() = HKEY();


It's not quite finished yet, I don't know if I've to make it volatile
compatible. Also, I need to deal with the constructors of the type I'm
protecting. Anyway, here it is:


//auto_destruct.hpp : assume that there's inclusion guards

template<class T, void (&CleanUp)(T&,bool&)>
class AutoDestructive
{
private:

AutoDestructive(const AutoDestructive& original) :
t(original.t),
t_ref(t),
to_be_auto_destructed(false) { }
AutoDestructive& operator=(const AutoDestructive&);
AutoDestructive* operator&();
const AutoDestructive* operator&() const;

private:

mutable T t;

public:

T& t_ref;

mutable bool to_be_auto_destructed;

AutoDestructive() : t(), t_ref(t), to_be_auto_destructed(false) {}

~AutoDestructive() { CleanUp(t,to_be_auto_destructed); }

T& operator()() { return t_ref; }
const T& operator()() const { return t_ref; }
};


And here's a sample usage:

// blah.cpp

#include <auto_destruct.hpp>

void CleanUp_HKEY(HKEY& hkey, bool& to_be_closed)
{
if (to_be_closed)
{
RegCloseKey(hkey);

to_be_closed = false;
}
}

void CleanUp_HDLL(HDLL& hdll, bool& to_be_freed)
{
if (to_be_freed)
{
FreeLibrary(hdll);
to_be_freed = false;
}
}

typedef AutoDestructive<HKEY,CleanUp_HKEY> AutoDestructive_HKEY;

typedef AutoDestructive<HDLL,CleanUp_HDLL> AutoDestructive_HDLL;

int main()
{
AutoDestructive_HKEY hkey;

LONG error_code = RegOpenKeyEx(HKEY_CLASSES_ROOT,
"*",
0,
KEY_QUERY_VALUE|KEY_SET_VALUE,
&hkey());



if (error_code == ERROR_SUCCESS)
{
hkey.to_be_auto_destructed = true;
}

//Some suff, exception might get thrown here

RegCloseKey(hkey());

hkey.to_be_auto_destructed = false;


AutoDestructive_HDLL hdll;

hdll() = LoadLibrary("Monkey");
if(hdll) hdll.to_be_auto_destructed = true;

//some stuff, exception may be throw

FreeLibrary(hdll());
hdll.to_be_auto_destructed = false;
}


-JKop
 
J

JKop

Here it is again, without the typos. This one compiles:

#include <iostream>
#include <windows.h>

template<class T, void (&CleanUp)(T&,bool&)>
class AutoDestructive
{
private:

AutoDestructive(const AutoDestructive&);
AutoDestructive& operator=(const AutoDestructive&);
AutoDestructive* operator&();
const AutoDestructive* operator&() const;

private:

mutable T t;

public:

T& t_ref;

mutable bool to_be_auto_destructed;

AutoDestructive() : t(), t_ref(t),
to_be_auto_destructed(false) {}
~AutoDestructive() { CleanUp(t,to_be_auto_destructed);
}

T& operator()() { return t_ref; }
const T& operator()() const { return t_ref; }
};

void CleanUp_HKEY(HKEY& hkey, bool& to_be_closed)
{
if (to_be_closed)
{
RegCloseKey(hkey);

to_be_closed = false;
}
}

void CleanUp_HDLL(HMODULE& hdll, bool& to_be_freed)
{
if (to_be_freed)
{
FreeLibrary(hdll);
to_be_freed = false;
}
}

typedef AutoDestructive<HKEY,CleanUp_HKEY>
AutoDestructive_HKEY;

typedef AutoDestructive<HMODULE,CleanUp_HDLL>
AutoDestructive_HMODULE;

int main()
{
AutoDestructive_HKEY hkey;

LONG error_code = RegOpenKeyEx(HKEY_CLASSES_ROOT,
"*",
0,
KEY_QUERY_VALUE|KEY_SET_VALUE,
&hkey());



if (error_code == ERROR_SUCCESS)
{
hkey.to_be_auto_destructed = true;
}

//Some suff, exception might get thrown here

RegCloseKey(hkey());

hkey.to_be_auto_destructed = false;


AutoDestructive_HMODULE hdll;

hdll() = LoadLibrary("Monkey");
if(hdll()) hdll.to_be_auto_destructed = true;

//some stuff, exception may be throw

FreeLibrary(hdll());
hdll.to_be_auto_destructed = false;
}


-JKop
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,174
Messages
2,570,940
Members
47,485
Latest member
Andrewayne909

Latest Threads

Top