DIP44: scope(class) and scope(struct)
H. S. Teoh
hsteoh at quickfur.ath.cx
Tue Aug 27 13:41:47 PDT 2013
On Wed, Aug 28, 2013 at 12:24:43AM +0400, Dmitry Olshansky wrote:
> 27-Aug-2013 18:25, H. S. Teoh пишет:
> >On Tue, Aug 27, 2013 at 04:01:57PM +0400, Dmitry Olshansky wrote:
>
> >
> >What if move(r2) throws? Then res1 won't get cleaned up, because r1 has
> >already been nulled by the move.
> >
>
> Then something is terribly wrong :)
>
> Rule #1 of move should be is that it doesn't throw.
std.algorithm.move is not nothrow. I tried to make it nothrow, and the
compiler told me:
std/algorithm.d(1604): Error: 'object.TypeInfo.destroy' is not nothrow
std/algorithm.d(1596): Error: function 'std.algorithm.move!(DirIteratorImpl).move' is nothrow yet may throw
std/typecons.d(3522): Error: template instance std.algorithm.move!(DirIteratorImpl) error instantiating
std/file.d(2526): instantiated from here: RefCounted!(DirIteratorImpl, cast(RefCountedAutoInitialize)0)
std/algorithm.d(1604): Error: 'object.TypeInfo.destroy' is not nothrow
std/algorithm.d(1596): Error: function 'std.algorithm.move!(Impl).move' is nothrow yet may throw
std/typecons.d(3522): Error: template instance std.algorithm.move!(Impl) error instantiating
std/net/curl.d(2040): instantiated from here: RefCounted!(Impl)
std/algorithm.d(1604): Error: 'object.TypeInfo.destroy' is not nothrow
std/algorithm.d(1596): Error: function 'std.algorithm.move!(Impl).move' is nothrow yet may throw
std/typecons.d(3522): Error: template instance std.algorithm.move!(Impl) error instantiating
std/net/curl.d(2747): instantiated from here: RefCounted!(Impl)
std/algorithm.d(1604): Error: 'object.TypeInfo.destroy' is not nothrow
std/algorithm.d(1596): Error: function 'std.algorithm.move!(Impl).move' is nothrow yet may throw
std/typecons.d(3522): Error: template instance std.algorithm.move!(Impl) error instantiating
std/net/curl.d(3065): instantiated from here: RefCounted!(Impl)
Isn't that scary? This means that the case I presented above is not
hypothetical -- if destroy throws, then you're screwed.
(And yes it's a very very rare case... but would you want to find out
the hard way when a problem that triggers a throw in destroy slips past
QA testing 'cos it's so rare, and shows up in a customer's environment
where it may take hours or days or even months to debug?)
Basically, the only way to be 100% sure is to use scope(this), or the
manual equivalent thereof (see below).
> It just blits the damn data. Even if this is currently broken..
It also calls destroy, which is not nothrow. :)
> At the very least 2-arg version certainly can avoid throw even if T
> has a bogus destructor that goes bottom up on T.init.
>
> BTW same with swap and at least in C++ that's the only way to avoid
> exceptions creeping up from nowhere.
[...]
Well this is kinda getting away from the main point, which is that
scope(this) allows us to hide all these dirty implementation details
under a compiler-implemented solution that makes sure things are always
done right. This is part of the reason I wrote DIP44: although it *is*
possible to manually write code that behaves correctly, it's pretty
tricky, and I doubt many people even realize the potential pitfalls.
Ideally, straightforward D code should also be correct by default.
Anyway, none of this move/nothrow nonsense is needed if we write things
this way:
struct S {
void delegate()[] __cleanups;
Resource res1;
Resource res2;
this() {
scope(failure) __cleanup();
res1 = acquireResource();
__cleanups ~= { res1.release(); };
res2 = acquireResource();
__cleanups ~= { res2.release(); };
}
~this() { __cleanup(); }
private void __cleanup() {
foreach_reverse (f; __cleanups)
f();
}
}
Which is basically what DIP44 is proposing under a nicer syntax.
T
--
"640K ought to be enough" -- Bill G., 1984. "The Internet is not a primary goal for PC usage" -- Bill G., 1995. "Linux has no impact on Microsoft's strategy" -- Bill G., 1999.
More information about the Digitalmars-d
mailing list