|
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