Boost logo

Boost :

From: Gennadiy E. Rozental (rogeeff_at_[hidden])
Date: 2001-08-29 14:37:36


--- In boost_at_y..., "Fernando Cacciola" <fcacciola_at_g...> wrote:
> > > [SNIP]
> Actually, my implementation dosen't include any exception logic in
the
> accesor function, so it will be inlined by any compiler with any
inlining
> support.
>
> T const& value() const
> {
> if ( !v )
> error() ;
> return *v ;
> }
>
> As it is documented in the code, the member function error() is
> intentionally not inline, this effectively allows value() to be
fully
> inlined, and since it is only called if the expression !v is false,
the only
> overhead is in the check.
No. You code will still be affected by the presence of function that
throw exceptions. For some compilers difference could be very
significant (not counting additional check - this we can ignore).
  I agree that we can put exception logic in some kind of #ifdef DEBUG
  or if( debug ) guard. And in this case I would agree with you. We
provide an ability for the user to be careless during development and
no runtime price in production.

>
> Furthermore,
>
> It throws ONLY IF THERE ISN'T ANY VALUE to return.
> In your design, it returns a default-constructed value of type T,
but it is
> technically a lie that the value of t is T() since it is actually
has no
> value.
> As a user you CANNOT call value() in an uninitialized optional.
This is
> under any criteria an ERROR, and this is true regardless whether
value()
> throws or returns a fake value.
> In other words, you should aways -one way or another- verify if the
optional
> is initialized before trying to access its value.
One way - user should use some kind of "is initialized" check. No
exception handling.

> Therefore, at the point where you call value() (or any shortcut,
such as
> operator*), the optional MUST be initialized, otherwise there is an
error in
> the program logic.
>
> Do you disagree with the statement that calling value() on a
uninitialized
> optional is under any circunstance an error?
Depends. As I sad for some users t == 2 is good even without !t check
(here we are using operator == instead of value()). But in most cases
I agree (some user would be satisfied with default T() in case if
optional was not initialized - but in this case why to use optional?).

> Assuming that you agree with that, what would happen if the user of
your
> design simply forgets to verify that t is initialized and calls
t.value()?
> Do you think that there is any way for him to recognize that the T()
> returned from value() is actually meaning that 't' isn't
initialized?
>
> Now, if your argument is based on the assumption that calling value
() on an
> initialized optional is an error, but that it is not the
responsability of
> the optional class to signal that, then I would like to know how do
you
> think a program can be robustely constructed if the programmer must
verify
> every expected condition by itslef since the system won't tell him
nothing
> about any violations. Exceptions were added precisly to prevent a
program
> from continue running after such violations have been made.
>
> You DONT NEED TO USE ANY TRY BLOCKS in order to use my optional. An
> exception will be thrown ONLY IF YOU MAKE AN ERROR IN YOUR CODE.
Don't
> throwing such exception will just MASK the error, while throwing
will help
> you recognize that you've made it.
>
> Once again: If you don't forget to verify that the optional is
initialized
> you won't have any exception, but if you do forget it, you don't
really
> wont to let your program keep running because it might be based on
the false
> belief that a particular optional has a value that it actually
hasn't.
What you describing fit to DEBUG/RELEASE scheme

>
> > > 2) if ( t == 2 ) ...
> > >
> > > Again, if t is unitialized the above boolean expression returns
> > false, but
> > > that's a mistake since, for instance, it also returns false for
(t !
> > = 2 )
> > I disagree again. First of all the fact that it will return false
in
> > bath cases is GOOD, because t is NOT EQUAL to 2 - in most cases
it is
> > exactly what you need. If you need to separate uninitialized
case -
> > check it.
> >
> The logical fact that an object which has no value is neither equal
nor
> not-equal to anything dosen't imply that it is a good idea to
design logical
> operators that way.
>
> optional<bool> s = foo() ;
>
> if ( s != false )
> {
> s MUST BE TRUE here, because that's what the semantic of the if
clause is
> implying.
> }
In my version you will get TRUE here because s is NOT equal to false
It could be acceptable or Would you need to separate 3 cases ( false -
 true - uninitialized) instead of 2 ( false - true or uninitialized)
and you would check on !s

> The above code is broken since I forgot to verify that t is
initialized.
>
> Now, if I must write it like:
>
> optional<bool> s = foo() ;
>
> if ( *s != false )
> {
> s MUST BE TRUE here, because that's what the semantic of the if
clause is
> implying.
> And it is guaranteed to be true because if t isn't initialized
this block
> isn't entered since operator*() will throw.
> }
You should not do this. Basicly you whould use *s ONLY when you
really need access to the value like:

bool b = *s;
or (*s).method();

in all other places it better use s itself. Again would you use *s
and s is not initialized it's you problem - the only thing we can
help with - throw exception in debug mode

> The mistake I made (not verifying t) will be caught by the
exception so the
> program preserves its logic.
>
>
> > >
> > >
> > > 3) if (!t)
> > > if ( t == NULL )
> > >
> > > This is confusing, the first expression evaluates whether t is
> > initialized
> > > or not, but the second test its value against the integer 0.
> > >
> > What is confusung? First is check on whether or not t is
initialized,
> > second is strange expression because t is VALUE. It is the same
like
> > having int value to be compard with NULL. If t is optional for
> > pointer value (if anybody would ever need that) than !t and t ==
NULL
> > both will have the same semantic.
>
> And if I write it like:
>
> optional<int> t ;
> if (!t)
> if ( t == 0 )
>
> Don't you think that most people -including yourself- will believe
that both
> expressions have the same meaning?
No. First is check that t is initialized. Second is check that t is
equal or not to 0. Depends what you need appropriate form you will
use.

> > > [SNIP]

> > I think is value semantic is defined well enough.
>
> Value semantic is unapropriate. The reason is that since there is
no value
> that can signal uninitialized state, as soon as the user forgets to
verify
> if the optional is initialized -which is a logic error-, the
program just
> keeps running unaware of the error leading to unexpected behaviour
which can
> be a LOT worse than abnormal termination.
> If you are not yet conviced, I invite you to actually use your
class, with
> your semantic, in working code, for a few months. That's what I
needed to
> convince myself that only pointers support a well
defined 'uninitialized'
> state.

"user forgets" is not a point. Check can be implementated with any
semantic.

>
> > Usage of pointer samentic worse from usage stand point (more
typing),
> True, but we are not talking about a huge amount of additional
typing.
> Rememer that if you use optional carefully you don't need to worry
about any
> exceptions.
> (as much as you don't need to worry about dereferencing null
pointers)
>
> > simplisity stand
> > point (it's more complex to implement)
> It is not that much complex! And it is already implemented :-)
>
> > and perfomance stand point
> > (value semantic basicly is transparent, the only price is
additianal
> > boolean value, while pointer semantic at least also require
> > additional dereferencing + value access method will always be
> > inefficient due to presence of exception).
> >
> The only performance differenece is in the extra check.
> The exception is thrown from a non-inline function.
Even if exception is ifdefed you still have additional dereferencing.

I still do not see any advantaged in pointer semantics, while
disadvantages are obvious: additional dereferencing + more complex
implementation(you need to think about allignment, for example). And
I still do not see any flaw with value semantic (I agree to add
conditional DEBUG/RELEASE check with exception)

>
> > >
> > > Anyway, I realized a few things with my implementation seeing
yours:
> > >
> > > ) T const& *() const ; seems to work just fine. I can't
remember
> > why I
> > > changed it to use the proxy.
> > > ) I lack exceptionn saftey in operator =.
> > > ) I forgot the 'explicit' in the proxy.
> >
> > Summary - I do not see even 1 reason why pointer semantic could be
> > preferred over value semantic in ANY variation.
> >
> In that case feel free to submit your own class. Eventually, the
boost
> formal review process will pick one to be included.
>
> Fernando Cacciola
> Sierra s.r.l.
> fcacciola_at_g...
> www.gosierra.com

Gennadiy.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk