Archive

Archive for July, 2010

Programmer’s Corner: Resource Guards in C++

July 20th, 2010 No comments

Everyone familiar enough with C++ to call themselves proficient, will have experienced that living with that language becomes a lot easier when following the RAII-idiom frequently. For memory that means using things like ::std::auto_ptr<> or ::boost::shared_ptr<> regularly. For other resources it usually involves writing your own guard class.

Simple enough you might think, but there are a couple of unexpected pitfalls lurking along the way. Today I’d like to talk about some of the peculiarities involved in writing a guard class with transferable ownership in the style of ::std::auto_ptr<>.

To keep things simple, let us assume that we were dealing with resources in the form of postive integer identifiers. Many C-libraries use int in this way, take the BSD sockets API as a prominent example. A first shot at a guard class implementation might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
 
class Guard {
private:
	int resource_;
public:
	Guard(int r)
		:resource_(r)
	{
		::std::cout << this << " Guard Constructor" << ::std::endl;
		::std::cout << this << " *** Guarding " << resource_ << ::std::endl;
	}
	~Guard()
	{
		::std::cout << this << " Guard Destructor" << ::std::endl;
		if(resource_ > 0) {
			::std::cout << this << " *** Free " << resource_ << ::std::endl;
		}
	}
	Guard(Guard const& rhs)
		:resource_(rhs.resource)
	{
		::std::cout << this << " Copy constructor" << ::std::endl;
	}
};
 
int main()
{
	{
		Guard g(42);
	}
	return 0;
}

The Guard will automatically free its associated resource upon destruction. However the current implementation has a dangerous flaw.

	{
		Guard g(42);
		Guard g_copy(g);
	}

Whenever we make a copy of Guard the underlying resource will be released multiple times. Depending on the type of resource this may or may not be bad, but it sure isn’t very pretty. To make this code work as expected, we introduce the concept of transferable ownership: Upon copy-construction the copy relieves the original Guard of resource ownership. Only the copy will be allowed to free the resource, while the orginal object has practically no responsibilities left. This is exactly the concept implemented by ::std::auto_ptr for memory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
 
class Guard {
private:
	int resource_;
public:
	Guard(int r)
		:resource_(r)
	{
		::std::cout << this << " Guard Constructor" << ::std::endl;
		::std::cout << this << " *** Guarding " << resource_ << ::std::endl;
	}
	~Guard()
	{
		::std::cout << this << " Guard Destructor" << ::std::endl;
		if(resource_ > 0) {
			::std::cout << this << " *** Free " << resource_ << ::std::endl;
		}
	}
	Guard(Guard& rhs)
		:resource_(rhs.Detach())
	{
		::std::cout << this << " Copy constructor; Assuming ownership of " 
					<< resource_ << ::std::endl;
	}
private:
	int Detach()
	{
		::std::cout << this << " Detaching " << resource_ << ::std::endl;
		int ret = resource_;
		resource_ = 0;
		return ret;
	}
};
 
int main()
{
	{
		Guard g(42);
		Guard g_copy(g);
	}
	return 0;
}

The modified code behaves as expected: Although two Guard objects are constructed, the guarded resource is only freed once. Note one subtle but important change in the interface: Since the copy-constructor must relieve the original object of its ownership, we can no longer take a const reference for copy-construction. As we will see shortly, this will get us into trouble.

Since this worked so well, let’s take things to the next level. Ever since Grandpa told us about encapsulation we have been a huge fan of it. With our guard class in place, we decide to write a factory method, so that we can hide those ugly int identifiers completely.

class Guard {
	[...]
};
 
class Factory {
public:
	Guard BuildGuard()
	{
		return Guard(42);
	}
};
 
int main()
{
	{
		Factory f;
		Guard g(f.BuildGuard());
	}
	return 0;
}

Can you spot the error? Actually this is a subtle one. It might very well work on your compiler due to excessive RVO, but according to the standard, this is not valid C++. The problem is that Factory::BuildGuard() needs to perform copy-construction from an unnamed temporary. As you may recall, unnamed temporaries are r-values in C++, which imposes certain restrictions. Most importantly, one can not take a non-const reference of an r-value! However, as we have seen earlier, the concept of transferable ownership requires the copy-constructor to take a non-const argument. Deadlocked.

So, how can we resolve this? A careless programmer might make declare Guard‘s resource_ field mutable, thus allowing Detach() to become a const-method, which will in turn allow the copy-constructor to take a const& parameter again. This is of course a very bad idea. Aside from the obvious interface inconsistencies introduced by making Detach() const (PLA anyone?), we lose one of the most useful idioms associated with these kind of containers: If you declare a container with transferable ownership const, you guarantee that the container never loses ownership of its resource. When the resource is fully encapsulated, it further guarantees that the container is the only object owning that resource. With ::std::auto_ptr, this is known as the const auto_ptr-idiom and is one of the main reasons why this container is still so popular, despite its flaws.

Talking about auto_ptr, how does the STL get around this problem? By introducing another proxy class! And that is exactly what we need to do as well. We can not copy-construct from unnamed temporaries, because we cannot transfer ownership from const objects. But what we can do is transfer ownership from an unnamed temporary guard object to another class of which we can copy-construct. Introducing GuardRef:

class GuardRef {
	friend class Guard;
private:
	int resource_;
private:
	GuardRef(int r)
		:resource_(r)
	{
		::std::cout << this << " GuardRef Constructor: " << resource_ << ::std::endl;
	}
public:
	GuardRef(GuardRef const& rhs)
		:resource_(rhs.resource_)
	{
		::std::cout << this << " GuardRef Copy Constructor: " << resource_ << ::std::endl;
	}
	~GuardRef()
	{
		::std::cout << this << " GuardRef Destructor" << ::std::endl;
	}
};

Note that GuardRef stores an integer resource just like Guard does, but it does not free the resource upon destruction. Thus there is no reason to transfer ownership between GuardRefs on copy-construction, and therefore we can now again copy-construct from unnamed temporaries!

All that is missing now is some glue to convert between GuardRef and Guard. We add this in form of a conversion operator and a converting constructor to Guard. Here is the complete code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
 
class Guard;
 
class GuardRef {
	friend class Guard;
private:
	int resource_;
private:
	GuardRef(int r)
		:resource_(r)
	{
		::std::cout << this << " GuardRef Constructor: " << resource_ << ::std::endl;
	}
public:
	GuardRef(GuardRef const& rhs)
		:resource_(rhs.resource_)
	{
		::std::cout << this << " GuardRef Copy Constructor: " << resource_ << ::std::endl;
	}
	~GuardRef()
	{
		::std::cout << this << " GuardRef Destructor" << ::std::endl;
	}
};
 
class Guard {
private:
	int resource_;
public:
	Guard(int r)
		:resource_(r)
	{
		::std::cout << this << " Guard Constructor" << ::std::endl;
		::std::cout << this << " *** Guarding " << resource_ << ::std::endl;
	}
	~Guard()
	{
		::std::cout << this << " Guard Destructor" << ::std::endl;
		if(resource_ > 0) {
			::std::cout << this << " *** Free " << resource_ << ::std::endl;
		}
	}
	Guard(Guard& rhs)
		:resource_(rhs.Detach())
	{
		::std::cout << this << " Copy constructor; Assuming ownership of " 
					<< resource_ << ::std::endl;
	}
	Guard(GuardRef const& ref)
		:resource_(ref.resource_)
	{
		::std::cout << this << " Constructing from GuardRef; Assuming ownership of " 
					<< resource_ << ::std::endl;
	}
	operator GuardRef()
	{
		::std::cout << this << " Converting to GuardRef" << ::std::endl;
		return GuardRef(Detach());
	}
private:
	int Detach()
	{
		::std::cout << this << " Detaching " << resource_ << ::std::endl;
		int ret = resource_;
		resource_ = 0;
		return ret;
	}
};
 
class Factory {
public:
	Guard BuildGuard()
	{
		return Guard(42);
	}
};
 
int main(int argc, char* argv[])
{
	{
		Factory f;
		Guard g(f.BuildGuard());
	}
	return 0;
}

Note how this approach is completely transparent to the user. The compiler is able to automatically deduce the required conversions to GuardRef and back, so the user will just use the factory method without knowing anything about the implementation magic taking place behind the curtains.

Let’s take a look at the output of that example program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x7fffffffbbb0 Guard Constructor
0x7fffffffbbb0 *** Guarding 42
0x7fffffffbbb0 Converting to GuardRef
0x7fffffffbbb0 Detaching 42
0x7fffffffbba0 GuardRef Constructor: 42
0x7fffffffbc20 Constructing from GuardRef; Assuming ownership of 42
0x7fffffffbba0 GuardRef Destructor
0x7fffffffbbb0 Guard Destructor
0x7fffffffbc20 Converting to GuardRef
0x7fffffffbc20 Detaching 42
0x7fffffffbc10 GuardRef Constructor: 42
0x7fffffffbc00 Constructing from GuardRef; Assuming ownership of 42
0x7fffffffbc10 GuardRef Destructor
0x7fffffffbc20 Guard Destructor
0x7fffffffbc00 Guard Destructor
0x7fffffffbc00 *** Free 42

As you can see, we construct a total of three Guard objects and two GuardRefs. Apart from the Guard declared by the user (0x7fffffffbc00) they are all temporary. Your mileage may vary depending on your compiler’s ability to RVO.

In my case, the compiler first created the unnamed object from the return statement of Factory::BuildGuard (lines 1-2). This object is converted to a GuardRef, losing ownership of the resource in the process (l. 3-5). This GuardRef can now be used to construct the Guard that will actually be returned by the factory method (l. 6). After that, the function actually returns: The stack is unwound and both the temporary GuardRef as well as the original Guard are destroyed (l. 7-8). The new guard remains as return value on the stack.
However the return value is also just an unnamed temporary, so in order to assign it to the Guard object declared by the user, we need to perform the whole procedure once more: The unnamed return value is converted to a GuardRef (l. 9-11), which is used to construct the Guard declared by the user (l. 12). After that, both the temporary GuardRef and the returned object on the stack are destroyed (l.13-14), which effectively concludes the process. The final two lines were generated when the user’s guard object went out of scope and finally freed the resource again.

Let us conclude with a brief discussion of the approach. The bad news is that this is the only way to write a robust guard class with the discussed properties in the current C++-standard. The naive solution without GuardRef compiled without warnings on Microsoft’s C++ compiler due to RVO, but that does not change the fact that it is not valid code. GCC refused to compile because of the constructor type mismatch. Unfortunately, introducing the GuardRef will prevent any RVO from happening in both Microsoft’s and the GCC compiler, which is very unfortunate.

But then again, who cares? We can now succesfully apply the RAII idiom to arbitrary resources and the end-user will never have to bother with the implementation details. Sure, construction has just become a lot more complicated, but in the end GuardRef is a very light-weight object and will almost certainly stay that lightweight to all eternity. We could run into a problem if Guard was a complex proxy class with high construction costs. In such a case it would be mandatory to refactor the guarding aspect into a separate class, whose only responsible was guarding and nothing else (which is btw how a good design should have looked like in the first place).
Note that despite the high count of temporary objects, the underlying resource is still just allocated and released once. Since mere object construction/destruction is very cheap in C++, the whole overhead boils down to a couple of additional function calls, which is well justified given the increased comfort and safety to the user of such a class.

If this whole thing just caused you to shake your head in disbelief, don’t despair. This issue has been eliminated, among with a number of other peculiarities, by a feature of the upcoming C++0x standard called r-value references, which will be discussed in a future article.

Categories: Programming