Boost logo

Boost :

Subject: [boost] [UUID] Code for UUIDs as PODs and other changes
From: Scott McMurray ([hidden])
Date: 2008-12-06 04:10:01

I remarked the other day that a UUID has a well-defined, portable
binary format, so I took a bit of time to rearrange things to make the
UUID class a POD and make more use of the generators idea. Along the
way I made a few other changes.

I've uploaded the result (with updated docs) to the vault as :

Some elaboration follows below. Comments appreciated.

~ Scott


As a POD, all the constructors are of course gone. This doesn't
bother me, but I have no idea what others will think. In fact, I like
it better, since I don't think that serialization to and from strings
belongs in the type, and it seems like using a lexical_cast is
perfectly reasonable:

   uuid u = lexical_cast<uuid>("21f7f8de-8051-5b89-8680-0195ef798b6a");

This cuts out a large chunk of code, too.

I don't know if it's useful, but aggregate syntax also opens possibilities:

    uuid u = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */
       0x6b, 0xa7, 0xb8, 0x14,
       0x9d, 0xad,
       0x11, 0xd1,
       0x80, 0xb4,
       0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8

In a possibly controversial move, I changed everything that could
throw exceptions. That's not that much, though. The std::string
parsing code I had already removed, which was the only source of one
exception. The other came from the byte input iterator range
constructor, which I kept as an assign member function (though I made
mutable iterators available, so it's unnecessary). In there I
switched the throw to an assert, assuming that it's reasonable for
people to arrange for there to always be 16 elements as a
precondition, since it's raw construction (which is used into the
generators, for example) rather than something likely to come from
user input.

Include cost:

I took the opportunity to move the stream I/O code out to its own
header, like the serialization code is. This means that the
uuids/uuid.hpp header is fairly light, as the <locale> and <string>
includes are gone. In fact, all that's left are these:

#include <boost/array.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/static_assert.hpp>


I liked the idea of using a generator object for the random UUIDs, so
I wanted to extend that idea. I renamed it to random_generator, on
the theory that random_generator<> isn't strenuous if a user wants the
default PRNG.

    boost::uuids::random_generator<> gen1;
    boost::uuids::uuid u1 = gen1();

    boost::lagged_fibonacci44497 rng;
    boost::uuids::random_generator<boost::lagged_fibonacci44497&> gen2(rng);
    boost::uuids::uuid u2 = gen2();

For name-based, I introduced a sha1_generator class. This is seeded
with a namespace UUID (those from the RFC are provided in
boost::uuids::namespaces), and then has an operator() that takes a
byte input iterator range. It may be interesting to add a boost.range
overload, allowing the generator to be used as a transform functor for

    boost::uuids::sha1_generator gen(boost::uuids::namespaces::dns());
    std::string name = "";
    boost::uuid u = gen(name.begin(), name.end());

Native UUIDs:

I recall that someone in the review wanted an interface to the OS
functionality, and I had been looking at Linux's earlier, so I wrote
up 2 additional generators. e2fsprogs_generator uses the
<uuid/uuid.h> header and libuuid on Linux, and passes all the tests
that random_generator does. windows_generator uses UuidCreate, and is
completely untested.

Variants and Versions:

I thought being able to find out these might be a good idea, so
they're there too.

    enum variant_type {variant_ncs = 0,
                       variant_dce = 1,
                       variant_rfc_4122 = variant_dce,
                       variant_microsoft = 2,
                       variant_other = 3};
    variant_type variant() const;

    enum version_type {version_wrong_variant = -1,
                       version_unknown = 0,
                       version_1 = 1,
                       version_time_based = version_1,
                       version_2 = 2,
                       version_dce_security = version_2,
                       version_3 = 3,
                       version_name_based_md5 = version_3,
                       version_4 = 4,
                       version_random = version_4,
                       version_5 = 5,
                       version_name_based_sha1 = version_5};
    version_type version() const;

Implementation Details:

I switched sha1.hpp over to using int32_t to remove the 4-byte-int
requirement. I also let it handle 512 MiB+ messages (if size_t can),
though that's not a serious concern. I replaced the
*reinterpret_cast<unsigned long*> in random_generator with the kind of
code used in sha1 that doesn't have the endianness dependence or what
I think is an aliasing violation.

Boost list run by bdawes at, gregod at, cpdaniel at, john at