
Great in theory, but I think it'll break lexical_cast<uuid>("21f7f8de-8051-5b89-8680-0195ef798b6a"), since the lexical_cast wants to default-construct the target type into which it will read.
Yes, the default lexical_cast behavior requires the default constructor. Having said that I've been routinely specializing lexical_cast to optimize away the overhead. Something like namespace boost { template<> inline std::string lexical_cast<std::string> (std::string const& v) { return v; } template<> inline std::string lexical_cast<std::string> (char const* const& v) { return v; } template<> inline std::string lexical_cast<std::string> (bool const& v) { return v ? "yes" : "no"; } } If we do that for boost::uuid, then the user gets the familair interface and we have our class clean and minimal. Having said that I am honestly unsure about uuid id = lexical_cast<uuid>(str); as it seems already covered by template<typename InputIterator> uuid(InputIterator, InputIterator); I certainly want the explicitness of "string str = lexical_cast<string>(id);" though. Still, keeping the bidirectional nature of lexical_cast is probably important for the user. Therefore, I'd *probably* have namespace boost { template<> inline boost::uuid lexical_cast<std::string> (std::string const& v) { return boost::uuid(v.begin(), str.end()); } }
template <typename ByteInputIterator> uuid(ByteInputIterator begin, ByteInputIterator end);
Can we take it one step further and replace it with a more generic: template<class ForwardReadableRange> uuid(ForwardReadableRange const& range) { typedef typename boost::range_iterator< ForwardReadableRange
::type Iterator;
Iterator begin = boost::begin(range); Iterator end = boost::begin(end); ... the rest is the same. } That way uuid id(string) is supported without the need for an explicit "uuid::uuid(string const&)" constructor as other containers as well.
// valid expression // generator() - result type must be convertible to a uuid template <typename Generator> explicit uuid(Generator & generator);
I feel that the "generator() - result type must be convertible to a uuid" requirement is somewhat excessive. Given the rest of the code it results in a generator having to know and even be able to create boost::uuid. That seems like an unnecessary coupling. I think generators should know *nothing* about uuid and certainly *not* create those. Instead, the generators should provide the necessary *ingredients* but it'd be uuid (well, its uuid(generator) constructor) actually creating an uuid instance. That might be as straghtforward as template<class Generator> uuid(Generator const& gen) { std::pair<Iterator, Iterator> range = gen(); ... something that uuid(begin, end) or uuid(range) does. } That way, the requirements for a generator are looser as all it needs is to provide a range. More so, the design maintains the locality of uuid creation and destruction -- those are created and destructed in the conventional way, i.e. by the uuid constructor and destructor (rather than created by a generator but destroyed by the uuid::~uuid()). As Scott noted template<class Generator> uuid(Generator const& gen) constructor is somewhat redundant. However, I'd personally like to keep it as it communicates to the user one of the main ways of creating a uuid.
bool is_nil() const; operator unspecified_bool_type() const; // return !is_nil();
It's a minor point but I'd ask whay we need both? The reason I greatly favor "bool" converter is that it is idiomatic and does *not* introduce a new vocabulary. That is important from the usability point of view as any new vocabulary is obviously only usable *after* one learnt it. Why ask the user to learn something (like "is_nil") if he can get by without? Thanks, Vladimir.