RAII for value objects

M

Markus Elfring

The class "auto_ptr" implements the RAII pattern for pointer types. It
seems that an implementation is not provided for non-pointer values by
the STL so far.
I imagine to use the "acquisition" for boolean values or flags.

Would you like that a template class will be added to implement locks
or state indicators for example?

Regards,
Markus
 
B

Buster

Markus said:
The class "auto_ptr" implements the RAII pattern for pointer types. It
seems that an implementation is not provided for non-pointer values by
the STL so far.
I imagine to use the "acquisition" for boolean values or flags.

Would you like that a template class will be added to implement locks
or state indicators for example?

RAII is supported directly by the language. Write a class with a
constructor and a destructor, having a data member of the value type you
require.
 
P

Pavel Vozenilek

Markus Elfring said:
The class "auto_ptr" implements the RAII pattern for pointer types. It
seems that an implementation is not provided for non-pointer values by
the STL so far.
I imagine to use the "acquisition" for boolean values or flags.
Take look on state_saver class inside Boost.Serialization library
version 1.32 (available on http://www.boost.org/ as pre-release,
will be released really soon now).

/Pavel
 
M

Markus Elfring

Take look on state_saver class inside Boost.Serialization library
version 1.32 (available on http://www.boost.org/ as pre-release,
will be released really soon now).

The implementation that is described in the document
"http://boost.org/libs/io/doc/ios_state.html" is too much for the use
cases that I suggested.
It is great for the intended application area. Would you like to
extract the core RAII from your approach to show an efficient boost
style solution?

Regards,
Markus
 
M

Markus Elfring

RAII is supported directly by the language. Write a class with a
constructor and a destructor, having a data member of the value type you
require.

How do you think about this design sketch?

template<typename X, const initial, const final>
class RAII
{
public:
RAII() : _value(initial) {}
RAII(const X& x) : _value(x) {}
~RAII () { _value = final; }

X& operator=(const X& x) { if (this != &x) { _value = x._value }
return *this; }
X& operator*() const { return _value; }
X* operator->() const throw() { return &_value; }
// Do you need more methods?

private:
X _value;
}

I imagine also a variant that performs method calls (lock / unlock for
example) instead of the assignments in the constructor and destructor.
The constant template parameters must be replaced by an other useful
type in this case.

Regards,
Markus
 
B

Buster

Markus said:
How do you think about this design sketch?

template<typename X, const initial, const final>
class RAII
{
public:
RAII() : _value(initial) {}
RAII(const X& x) : _value(x) {}
~RAII () { _value = final; }

X& operator=(const X& x) { if (this != &x) { _value = x._value }
return *this; }
X& operator*() const { return _value; }
X* operator->() const throw() { return &_value; }
// Do you need more methods?

private:
X _value;
}
 
M

Markus Elfring

I imagine also a variant that performs method calls (lock / unlock for
example) instead of the assignments in the constructor and destructor.
The constant template parameters must be replaced by an other useful
type in this case.

How do you think about the following design alternatives?

class method_pair
{
public:
method_pair() {}
virtual ~method_pair() {}

virtual void begin() = 0;
virtual void end() = 0;
}

template<class MP> class RAII2
{
public:
RAII2(MP& mp) : _m_p(mp) { _m_p.begin(); }
~RAII2() { _m_p.end(); }

// Is anything missing here?

private:
MP& _m_p;
}

template<class MP> class RAII3 : private MP
{
public:
RAII3() { begin(); }
~RAII3() { end(); }

// Is anything missing here?
}

I guess that the implementations will need a few more typedefs and
reformatting to fulfill the requirements and conventions of good
libraries.
Would this proposal be an acceptable candidate for addition to the
standard templates library?

Regards,
Markus
 
P

Pavel Vozenilek

Markus Elfring said:
The implementation that is described in the document
"http://boost.org/libs/io/doc/ios_state.html" is too much for the use
cases that I suggested.
It is great for the intended application area. Would you like to
extract the core RAII from your approach to show an efficient boost
style solution?
There are plans to convert state_saver into independent
Boost lib though AFAIK it didn't started yet.

/Pavel
 
B

Buster

Markus said:
How do you think about the following design alternatives?

class method_pair
{
public:
method_pair() {}
virtual ~method_pair() {}

virtual void begin() = 0;
virtual void end() = 0;
}

template<class MP> class RAII2
{
public:
RAII2(MP& mp) : _m_p(mp) { _m_p.begin(); }
~RAII2() { _m_p.end(); }

// Is anything missing here?

private:
MP& _m_p;
}

template<class MP> class RAII3 : private MP
{
public:
RAII3() { begin(); }
~RAII3() { end(); }

// Is anything missing here?
}

I guess that the implementations will need a few more typedefs and
reformatting to fulfill the requirements and conventions of good
libraries.
Would this proposal be an acceptable candidate for addition to the
standard templates library?

No, I still don't like it. Perhaps an example will help. Say you have a
class, X, with a constructor and destructor. Then using your RAII2
class, for example, you might need this much code:

class X_MP_adapter : public method_pair
{
public:
void begin () { x = new X; }
void end () { delete x; }
private:
X x;
};

void function ()
{
RAII2 <MP_adapter> x;
// Do some stuff.
// Finalization code called automatically.
}

This seems like a waste of time. First of all, the base class
method_pair and its virtual functions are unnecessary because, in virtue
of the template parameter MP, you know what the static type of
RAII2::_m_p is (in this case, X_MP_adapter) is. You just need to
document in RAII2 that the statements "x.begin ();" and "x.end ();" must
be well formed for an object x of type MP.

More to the point, you don't need the X_MP_adapter and RAII2 classes at
all. You can just write:

void function ()
{
X x;
// Do some stuff.
// Finalization code called automatically.
}

Is there some specific problem you're trying to solve? If I've missed
your point, it might help me to understand better if I saw some real
code.
 
M

Markus Elfring

No, I still don't like it. Perhaps an example will help.

How do you think about a design with function objects?

template<typename Context> class initializer
: public unary_function<Context, void>
{
initializer() {}
virtual ~initializer() {}

virtual void operator() (argument_type& context) = 0;
}

template<typename Context> class finalizer
: public unary_function<Context, void>
{
finalizer() {}
virtual ~finalizer() {}

virtual void operator() (argument_type& context) = 0;
}

// Does it make sense to introduce an interface "Callable"?

template<typename Context,
template<typename Context> class initializer,
template<typename Context> class finalizer>
class RAII4
: private initializer<Context>, finalizer<Context>
{
public:
RAII4(Context& c) : _context(c) {}
virtual ~RAII4() {}

virtual void begin() { initializer<Context>::eek:perator()(_context); };
virtual void end() { finalizer<Context>::eek:perator()(_context); };

// Would you like to add anything here?

private:
Context& _context;
}


// small example: templates and classes in action

class door : public method_pair
{
public:
door() {}
virtual ~door() {}

virtual void open() { cout << "Hallo"; };
virtual void close() { cout << "Bye"; };

virtual void begin() { open(); };
virtual void end() { close(); };
}

class room : public method_pair
{
public:
method_pair() : _guard(_one) {}
virtual ~method_pair() {}

virtual void enter() { _one.open(); cout << "Nice furniture"; };
virtual void leave() { cout << "See you later"; _one.close(); };

virtual void begin() { enter(); };
virtual void end() { leave(); };

private:
door _one;
RAII2<door> _guard;
}

Regards,
Markus
 
M

Markus Elfring

I'm sorry, I don't understand the question.

I think that you can ignore the type change because you want to
achieve that a variable will be initalized once and the cleanup will
be automatically performed when its scope has ended.

I hope that a compiler does not optimize the resulting instructions
away because it might happen that no of its other methods will be
directly called after its initialization (=> unused variable?).
That is the usual precondition for the successful work of the RAII
idiom.

Regards,
Markus
 
M

Markus Elfring

There are plans to convert state_saver into independent
Boost lib though AFAIK it didn't started yet.

Is your development different from the "IO stream state savers"?
Are you planning to create separate components?
 
M

Markus Elfring

This seems like a waste of time. First of all, the base class
method_pair and its virtual functions are unnecessary because, in virtue
of the template parameter MP, you know what the static type of
RAII2::_m_p is (in this case, X_MP_adapter) is. You just need to
document in RAII2 that the statements "x.begin ();" and "x.end ();" must
be well formed for an object x of type MP.

I try to express the RAII design pattern/idiom in C++ language terms.

I think that the virtual methods are needed so that they can be
overloaded.
Do you know a wording which would achieve a similar effect with code
generation by the templates?

The difference between RAII2 and RAII3 is this:
- RAII2 uses delegation over the variable "_m_p". A method pair can be
selected at run time.
- RAII3 uses private inheritance to specify construction and
destruction as an implementation detail that should not be visible to
the clients of the class. The selction is done at compile time.

More to the point, you don't need the X_MP_adapter and RAII2 classes at
all. You can just write:

void function ()
{
X x;
// Do some stuff.
// Finalization code called automatically.
}

The adapter or wrapper is needed if your class X offers two related
methods (a specific pair) to which calls should be encapsulated by the
RAII technique.
The methods "begin" and "end" should be kept private as far as
possible because they play their role as a virtual constructor and
destructor.

Your "X_MP_adapter" sketch looks like an approach that is covered by
the class "std::auto_ptr" already.
I am interested in the RAII handling of data structures that are not
concerned with a managed pointer.

Is there some specific problem you're trying to solve? If I've missed
your point, it might help me to understand better if I saw some real
code.

Would you like to look at an other specific implementation?
- http://zthread.sourceforge.net/html/classZThread_1_1Guard.html
- http://zthread.sourceforge.net/html/classZThread_1_1LockedScope.html

I see also use cases besides synchronization in the document "Memory
model for multithreaded C++".
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1680.pdf

I would like to use the class "std::atomic_int" with my suggestion.
Such an integer value will be reset (e.g. 0 as default) in the
destructor.
I guess that the class "std::msync" will become another interesting
candidate for some applications.

Regards,
Markus
 
J

Jonathan Turkanis

There are plans to convert state_saver into independent
Boost lib though AFAIK it didn't started yet.

I remember Robert Ramey mentioning this, but I don't remember if Daryle Walker
responded. Would this library supplant ios_state.hpp? This would be nice for me,
since the io directory would then be empty ;-)

Jonathan
 
P

Pavel Vozenilek

Jonathan Turkanis said:
I remember Robert Ramey mentioning this, but I don't remember if Daryle Walker
responded. Would this library supplant ios_state.hpp? This would be nice for me,
since the io directory would then be empty ;-)
It is looking for volunteer to make it standalone.
Unsolved problem if what if operator= throws
inside destructor.

/Pavel
 
B

Buster

Markus said:
I think that you can ignore the type change because you want to
achieve that a variable will be initalized once and the cleanup will
be automatically performed when its scope has ended.

Guaranteed (modulo 'as-if').
I hope that a compiler does not optimize the resulting instructions
away because it might happen that no of its other methods will be
directly called after its initialization (=> unused variable?).

That wouldn't be an optimization. That would be smashing your program
with a sledgehammer.
 
B

Buster

Markus said:
I try to express the RAII design pattern/idiom in C++ language terms.

I think that the virtual methods are needed so that they can be
overloaded.

"Overridden". You're right, I think, but a real example would help to
convince me. I can't think of a situation where you know at compile time
that there needs to be some RAII done, but you don't know exactly what
the resource is going to be until run time. Enlighten me?
Do you know a wording which would achieve a similar effect with code
generation by the templates?

Sure. Check out the standard C++ library algorithms. Here's a simple
example. Notice there's no code at all corresponding to the Concept.

// Concept: Transmogrifiable.
// If x is an object of a type T which models
// the Transmogrifiable concept, then the
// following expression is valid:
//
// x.transmogrify ();
// Postcondition: x is transmogrified.

// Algorithm: transmogrify_n
// If x is an object of a type T which models
// the Transmogrifiable concept, and n is an integer,
// then the following expression is valid:
//
// transmogrify_n (x, n);
// Postcondition: x is transmogrified n times.

template <typename T, typename Integer>
void transmogrify_n (T & x, Integer n)
{
for (Integer i (0); i < n; ++ i)
{
x.transmogrify ();
}
}
The difference between RAII2 and RAII3 is this:
- RAII2 uses delegation over the variable "_m_p". A method pair can be
selected at run time.
- RAII3 uses private inheritance to specify construction and
destruction as an implementation detail that should not be visible to
the clients of the class. The selction is done at compile time.



The adapter or wrapper is needed if your class X offers two related
methods (a specific pair) to which calls should be encapsulated by the
RAII technique.

Sure. But why not use the constructor and destructor?
The methods "begin" and "end" should be kept private as far as
possible because they play their role as a virtual constructor and
destructor.

Ah-hah. I suppose so.
Your "X_MP_adapter" sketch looks like an approach that is covered by
the class "std::auto_ptr" already.
I am interested in the RAII handling of data structures that are not
concerned with a managed pointer.



Would you like to look at an other specific implementation?
- http://zthread.sourceforge.net/html/classZThread_1_1Guard.html
- http://zthread.sourceforge.net/html/classZThread_1_1LockedScope.html

I see also use cases besides synchronization in the document "Memory
model for multithreaded C++".
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1680.pdf

I would like to use the class "std::atomic_int" with my suggestion.
Such an integer value will be reset (e.g. 0 as default) in the
destructor.
I guess that the class "std::msync" will become another interesting
candidate for some applications.

If it works, go for it. I think you're wasting your time trying to solve
a very general problem, when the solution is easy (write a class with a
constructor and destructor) for any particular instance of the problem.
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top