DIP44: scope(class) and scope(struct)
Dmitry Olshansky
dmitry.olsh at gmail.com
Tue Aug 27 05:01:57 PDT 2013
27-Aug-2013 01:30, H. S. Teoh пишет:
> On Sun, Aug 25, 2013 at 12:18:27AM +0200, Tobias Pankrath wrote:
>> On Saturday, 24 August 2013 at 20:11:14 UTC, H. S. Teoh wrote:
>>> How would you use RAII to solve this problem? If I have a class:
>>>
>>> class C {
>>> Resource1 res1;
>>> Resource2 res2;
>>> Resource3 res3;
>>> this() {
>>> ...
>>> }
>>> }
>>>
>>> How would you write the ctor with RAII such that if it successfully
>>> inits res1 and res2, but throws before it inits res3, then only res1
>>> and res2 will be cleaned up?
>>
>> Like someone else already proposed: Using a wrapper type W that
>> releases the resources in it's destructor. W.init wouldn't release
>> anything. So by definition (if I recall the rules correctly) every
>> new instance of C would have res3 = Resource3.init prior to it's
>> constructor called.
>>
>> Now make sure that a) res3's destructor gets called (check) b)
>> res3's destructor may be called on Resource3.init. That would be a
>> new rule similar to 'no internal aliasing' and c) that every
>> constructor of C either sets res3 to a destructable value or does
>> not touch it at all ('transactional programming').
> [...]
>
> But don't you still need to manually cleanup in the case of ctor
> failure? AFAIK, the dtor is not invoked on the partially-constructed
> object if the ctor throws. So you'd still have to use scope(failure) to
> manually release the resource.
>
> To prove my point, here is some sample code that (tries to) use RAII to
> cleanup:
>
> import std.stdio;
>
> struct Resource {
> int x = 0;
> this(int id) {
> x = id;
> writefln("Acquired resource %d", x);
> }
> ~this() {
> if (x == 0)
> writefln("Empty resource, no cleanup");
> else
> writefln("Cleanup resource %d", x);
> }
> }
>
> struct S {
> Resource res1;
> Resource res2;
> Resource res3;
>
> this(int) {
> res1 = Resource(1);
> res2 = Resource(2);
> assert(res1.x == 1);
> assert(res2.x == 2);
>
> throw new Exception("ctor failed!");
> res3 = Resource(3); // not reached
> assert(res3.x == 3); // not reached
> }
> }
>
> void main() {
> try {
> auto s = S(123);
> } catch(Exception e) {
> writeln(e.msg);
> }
> }
>
> Here is the program output:
>
> Acquired resource 1
> Empty resource, no cleanup
> Acquired resource 2
> Empty resource, no cleanup
> ctor failed!
>
> As you can see, the two resources acquired in the partially-constructed
> object did NOT get cleaned up. So, RAII doesn't work in this case.
Fixed?
> auto r1 = Resource(1);
> auto r2 = Resource(2);
> assert(res1.x == 1);
> assert(res2.x == 2);
>
> throw new Exception("ctor failed!");
> auto r3 = Resource(3); // not reached
> assert(res3.x == 3); // not reached
res1 = move(r1);
res2 = move(r2);
res3 = move(r3);
Exception-safe code after all has to be exception safe.
Unlike in C++ we have no explicit initialization of members (and strict
order of this) hence no automatic partial cleanup.
--
Dmitry Olshansky
More information about the Digitalmars-d
mailing list