Boost logo

Boost :

From: Brian McNamara (lorgon_at_[hidden])
Date: 2003-08-31 09:29:33


On Sun, Aug 31, 2003 at 12:34:39AM -0700, Mat Marcus wrote:
> In this post I will put forward a use-case or two to help see whether
> something a little different then the current version of optional
> might be useful. I also begin making a case that a Concept like
> PossiblyUninitializedVariable might be more generally useful than
> OptionalPointee. As I mentioned in the post that started this thread,
...
> std::pair<Iter, Iter> out_edges(Vertex, Graph);
> boost::optional<Iter> begin, end; //now use optional
> Graph g(/*...*/);
> boost::tie(begin, end) = out_edges(v,g); //sorry, doesn't work
> boost::tie(*begin, *end); //sorry, ASSERT && looks too strange
...

First off, let me say I do think this is a compelling use-case.
(As an aside, this is exactly why people in functional languages love
pattern-matching; it's a general facility for deconstructing data into
its constituents, where each portion is a new, named variable.)
I missed the beginning of this thread, but I imagine the motivation is
to avoid having to say
   std::pair<Iter,Iter> p = out_edges(v,g);
   Iter begin = p.first;
   Iter end = p.second;
each and every single time you call out_edges().

As I think someone else in the thread mentioned, there has to be some
explicit call to turn an "optional<T>" into a "T", or else the two
interfaces get muddled together. The same would be true of
PossiblyUninitializedVariable. You've also mentioned that you think
operator*() is inapproprate/ugly for this use.

So, here's an idea for something completely new which maybe helps fit
your requirements. I start with the motivating example:

   PossUninitVar<Iter> begin, end;
   tie( +begin, +end ) = out_edges(v,g);
   for( Iter i = ~begin; i != ~end; ++i ) ...

(Effectively operator~() fetches the value (or asserts if there is none),
whereas operator+() returns a reference to the yet-nonexistent value so
it can be filled in by someone else.)

Now here's a (sloppy, partial) summary of the interface, along with
some of the implementation:

   template <class T>
   struct PossUninitVar {
      PossUninitVar() : init(false) {}
      PossUninitVar( const T& x ) : init(true) {
         new (raw_storage) T(x);
      }
      operator bool() { return init; }
      Proxy operator +() { return unsafe_get(); }
      T& operator~() { return get(); }
   private:
      bool init;
      Something raw_storage; // array of bytes with right size/alignment
      T& get() { if(!init) assert; else /* returns T& from raw storage */ }
      Proxy unsafe_get() { return Proxy(this); }
      class Proxy {
         PossUninitVar* puv;
      public:
         T& operator=( const T& x ) {
            new (puv->raw_storage) T(x);
            puv->init = true;
            return puv->get();
         }
      };
   };
      
Hopefully you get the idea. I think this meets your wish list.
Making tie() interact properly with the Proxy might be hard.

Just some ideas; refine, reject, whatever, as you please...

-- 
-Brian McNamara (lorgon_at_[hidden])

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