Boost logo

Boost :

From: Rob Stewart (stewart_at_[hidden])
Date: 2005-07-13 14:33:36


From: "Jost, Andrew" <Andrew_Jost_at_[hidden]>
> > From: boost-bounces_at_[hidden]
> > [mailto:boost-bounces_at_[hidden]] On Behalf Of Fernando Cacciola
>
> Here is a real example. Imagine a researcher with many instruments
[snip long example indicating that using Boost.Optional for data
collection works fine, but for using or reporting that
information, Boost.Optional falls short]
>
> Okay. This works, but what about that ugly call to loadPoint? The
> problem is that the library function is expecting a signal value, not a
> Boost.Optional! Boost.Optional helped us manage data collection, but
> does not help us call trend::loadPoint. Let's look at another example.

The library can just as easily expect a Boost.Optional.

> Say I'm working on a project that persists some configuration data.
[snip another example that suggests Boost.Optional is fine for
collecting but not reporting information]
>
> This illustrates the same problem as before, namely that it requires us
> to translate an empty Boost.Optional into something else. This

Yep.

> translation point is where I see the need for guaranteed object
> delivery, for it seems that Boost.Optional and guaranteed object
> delivery are two ends of the same pipeline: at the head, we obtain (or
> fail to obtain) a value, and in the process escape the need for a signal
> value; at the tail, we use the values we've obtained, possibly in a
> context that EXPECTS signal values. Requires them. So far we've used
> only the trinary operator to perform this translation, but is there a
> better way?

The fundamental issue seems to be that you want an object or
function that automagically produces a value, even if one wasn't
otherwise available.

[snip recognition that dual_state was ill-conceived due to
reliance on default construction]

> In light of this, a better implementation of guaranteed delivery would
> make use of template function objects that I'll call "adapters". The
> role of an adapter would be to evaluate a given optional object (I'll
> refer to anything resembling Boost.Optional as an "optional" object)
> into either the valid object it contains, or a default value, which is
> probably defined in the adapter's template specification or constructor.
> Adapters separate our optional objects from the defaults we might want
> to use in place of uninitialized T objects. Note that they are
> incompatible with noncopyable T objects.
>
> Using adapters, the call to trend::loadPoint in the first example might
> look like this:
>
>
> // -- begin
> // call trend::loadPoint
> opt_dp::adapter f(-1);
> for( data_set::iterator p = d.begin(); p != d.end(); ++p ) {
> tr.loadPoint( f(*p) );
> }
> // -- end

Your version, with Boost.Optional was like this:

> for( data_set::iterator p = d.begin(); p != d.end(); ++p ) {
> tr.loadPoint( *p ? p->get() : -1 );
> }

(I think you could change "p->get()" to "**p" if you like, BTW.)

The latter is simpler and seems far more direct and, therefore,
readable. I don't see that your adapter has added any value.

> The second example would look like this:
>
>
> // -- begin
> // assume cf_out is an open ofstream
> param_map_value::adapter f("<undef>");
> for( param_map::iterator p = pMap.begin(); p != pMap.end(); ++p ) {
> cf_out << p->first << " " << f(p->second) << endl;
> }
> // -- end

Your version, with Boost.Optional, looked like this:

> for( param_map::iterator p = pMap.begin(); p != pMap.end(); ++p ) {
> cf_out << p->first << " " << ( p->second ? p->second.get() :
> "<undef>" ) << endl;

That packs a lot into a single statement. I might write it like
this:

   std::string const undef("<undef>");
   for (param_map::iterator p(pMap.begin()), end(pMap.end());
      p != end; ++p)
   {
      std::string const & value(p->second ? *p->second : undef);
      cf_out << p->first << ' ' << value << std::endl;
   }

Does either version really warrant an "adapter?"

> With a little creativity, programmers could make further use of the STL
> to craft strikingly elegant solutions. It seems to me adapters would
> make a useful and appropriate addition to our toolbox, improving the
> flexibility with which we can access copyable T objects contained in an
> optional container.

I don't really see the value as presented thus far. There is a
tiny amount of syntactic sugar, but is that warranted?

> But, why do I keep saying optional instead of Boost.Optional? To see,
> we must think about how an adapter interacts with the optional object on
> which it operates. I won't spell out the fairly obvious point that
> adapters should return references rather than whole T objects, but if
> this is so, then an adapter that operates on an empty optional object
> must ask that optional to, first, construct a new T object, then return
> a reference to that new object. Based on this, we can say a few things
> about an optional object, X, on which an adapter operates:
>
> (a) X must have valid EMPTY and FULL states.
> (b) There must exist for X a valid EMPTY state, S, in which X
> privately holds an object, x, of type T.
> (c) If X is EMPTY (including state S), then altering or
> replacing the internal x without setting the state to FULL is a "const
> this" operation.

Your requirements are flawed. The adapter should hold an
instance of type T as provided by the constructor. Then, if the
Boost.Optional has no value, the adapter can return it's default
instance. IOW, the adapter shouldn't impose its needs on the
optional type. Boost.Optional would then work fine, though I
still question the value of your adapter.

-- 
Rob Stewart                           stewart_at_[hidden]
Software Engineer                     http://www.sig.com
Susquehanna International Group, LLP  using std::disclaimer;

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