|
Boost : |
Subject: [boost] [variant2] never-empty guarantee, again
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2019-04-03 21:46:16
wt., 2 kwi 2019 o 22:59 Peter Dimov via Boost <boost_at_[hidden]>
napisaÅ(a):
> Andrzej Krzemienski wrote:
>
> > Can you show a destructor like that, does a visitatoin on a variant, and
> > that you wouldn't be ashamed to put in your program?
>
> The destructor doesn't have to do visitation. It merely needs to call a
> function f1, which calls a function f2, which calls a function f3, which
> calls a function f4, which does visitation.
>
>
What we've been telling you from the start is that this forces you to
> partition your functions into two classes, one allowed to do visitation,
> the
> other not, then keep track of which is which, never calling the wrong
> class
> in a destructor, or from a catch clause.
>
The fact that I have to partition all functions into two categories isn't
new or surprising. When defining an operation that needs to provide a
strong exception-safety guarantee I need to some use operations that offer
no-fail guarantee. So I need to be aware which operations offer no-fail
guarantee and use only those. I do the partitioning, and I need to be
cautious.
When I define the destructor, again I do not have the liberty to use any
function I can use in other parts of the code. In destructor I can only use
functions that do not signal failure through exceptions if they fail. And
not signalling failures is ugly on its own, so I am inclined to also just
use functions that cannot fail. I have to do the partitioning again.
When I try to go back and recall how the destructors I write look like,
they fall into three categories:
1. Some types just aggregate other types. The implicitly defined destructor
only calls the destructors of subobjects.
2. Sometimes I need to provide a RAII interface for a system resource or a
C component. In that case my implementation uses only ints and pointers and
system calls. When defining the destructor, I have confidence that there
are no unexpected types hidden by templates, or functions with unexpected
types with unpredictable guarantees.
3. When I define a generic component, such as optional<T>, in my
destructors the only operation on the unknown type T is to call its
destructor. Other operations in the destructor are performed on concrete
types with no-fail guarantee such as `bool`. The situation is no different
for std containters: for T, they only call destructors, other operations
are on concrete types: pointers, memory deallocation. No way a variant or
other unexpected type could get there, even indirectly.
I know people are using destructors for other things, like committing the
DB transaction, but this is what I call bad exception handling. Such
implementations are usually incorrect: either you do not report failures in
such case, or you are reporting failures when there is none. Your example
of a destructor, to me, also falls into this category:
template<class T> class monitor
> {
> char const* file_;
> int line_;
> T const& v_;
> T old_;
>
> public:
> explicit monitor( char const* file, int line, T const& v ):
> file_( file ), line_( line ), v_( v ), old_( v ) {}
>
> ~monitor()
> {
> if( v_ != old_ ) {
> std::clog << file_ << ":" << line_
> << ": monitored value changed from "
> << old_ << " to " << v_ << std::endl;
> }
> }
> };
It calls operations on an unknown T that could fail and throw: comparison,
streaming. Streams themselves could throw. Such implementation has other
problems before we even consider a variant.
And, of course, this has nothing to do with visitation, specifically. The
> exception safety guarantee is a global thing. Once you lose basic, you lose
> it everywhere, not just for visit.
>
Why are you mentioning "basic" (guarantee) here? In the vision that I
propose variant offers basic guarantee. It is just that the invariant is
weaker. This is analogous to a (potentially smart) pointer: normally you
can call `*p`, but sometimes (on nullptr) it is UB, so you may need to
check what state it is in.
Regards,
&rzej;
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk