Boost logo

Boost :

From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2001-08-29 15:20:49


----- Original Message -----
From: Gennadiy E. Rozental <rogeeff_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Wednesday, August 29, 2001 4:37 PM
Subject: [boost] Re: Proposal: statefull objects

> --- 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.
You're right here.
Vesa's proposal (of an external function) can work.
Alternatively, a custom exception class with a raise() method would also
work.
In the later case, since there won't be any throw expression visible by the
compiler it won't be anything -exception realted- to prevent full inlining.

> 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.
>
Agreed then.

>
>
> >
> > 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.
>
User should use some kind of is initialized. Exception handling is there in
case the user forgets to do that.
Anyway, with debug/release modes we are agreed on this.

>
> > 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?).
>
Could you give me a feasible example in which t==2 is good even without !t
check?

> > 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
>
Yes.

> >
> > > > 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

Here your are wrong.

(s != false )
  calls !lhs.compare(rhs).
  compare() returns false since s is not initialized,
   then operator != returns true.

However, (s != true) will return true just the same.

If s is unitialized, then the expression (s != false) evaluates to 'true'
and the block is entered. Because your implementation uses T's default
constructor, inside the block, s.value() can be ANYTHING, since the compiler
doesn't automaticaly initialize default constructed built-in values.

> 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.

What are the arguments supporting these statemens?

> 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
>
NO. That not's the only thing we can do.
We can offer a semantic that will make harder for the user to make mistakes.

> > 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.
>
It is technically correct that both expressions above are different, and
that a sharp user shoundl't get confused. However, I really don't think that
an average programmer -like me- won't get in trouble since the analogy (!t)
equal (t==0) is too common.

> > > > [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,
>
Well, I tried...

> while
> disadvantages are obvious: additional dereferencing + more complex
> implementation(you need to think about allignment, for example).
Alignment have nothing to do with the pointer semantic.
 It is a problem derived from my intention of removing the default
constructed T value in an unitialized optional.

>And
> I still do not see any flaw with value semantic (I agree to add
> conditional DEBUG/RELEASE check with exception)
>

Fernando Cacciola
Sierra s.r.l.
fcacciola_at_[hidden]
www.gosierra.com


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