[uuid] uuid::create() function

Other than the current approach (which I agree is not acceptable), I see 6 alternatives to the uuid::create() function. I believe all of them have been mentioned before. 1. Make the random number generator a static object. Document the function as not thread safe. This will be fast, but easy to use incorrectly (e.g. in a multi-threaded program). 2. Make the random number generator a static object and protect calls to it with a mutex. This is a thread safe solution, but it will be slow because of the mutex. 3. Make the random number generator live in thread local storage. This will be a thread safe solution, but requires Boost.Thread. 4. Use the current seed as the random numbers directly. Will this solution provide 'good' uuids? That is, is this source of random numbers good enough? 5. Create a function object (that has the random number generator as a member) that produces uuids. Is this too complicated? i.e.: class uuid_generator { public: uuid_generator() { engine.seed(get_seed()); } uuid operator() const; private: Engine& engine; }; 6. Create a CAS/lock-free random number generator. This is hard to implement. I am favoring number 4, unless it will not produce good uuids. Then I favor number 5. I guess I am looking for direction as to how to proceed. Andy.

Andy wrote:
4. Use the current seed as the random numbers directly. Will this solution provide 'good' uuids? That is, is this source of random numbers good enough?
You'll need to perform statistical tests to be sure, but: 1. On POSIX systems where /dev/urandom is available, it should be close to the best, at least in theory; 2. Under a typical use case where the application only generates one UUID, it should be as good as any other RNG seeded with its entire state, and should be better than a RNG seeded with a 32 bit seed; 3. In the multiple UUIDs case, I expect it to be competitive with any RNG, even though it only keeps 160 bits of state (another 160 bits - the old value of 'rd' - can also contribute, depending on the compiler and the use pattern).

"Peter Dimov" <pdimov@mmltd.net> wrote in news:013b01c7a6cf$feb64e30$6407a80a@pdimov2:
Andy wrote:
4. Use the current seed as the random numbers directly. Will this solution provide 'good' uuids? That is, is this source of random numbers good enough?
You'll need to perform statistical tests to be sure, but:
1. On POSIX systems where /dev/urandom is available, it should be close to the best, at least in theory;
2. Under a typical use case where the application only generates one UUID, it should be as good as any other RNG seeded with its entire state, and should be better than a RNG seeded with a 32 bit seed;
3. In the multiple UUIDs case, I expect it to be competitive with any RNG, even though it only keeps 160 bits of state (another 160 bits - the old value of 'rd' - can also contribute, depending on the compiler and the use pattern).
Thanks! This is the assurance I needed. Andy.

Andy said: (by the date of Mon, 4 Jun 2007 20:32:35 +0000 (UTC))
"Peter Dimov" <pdimov@mmltd.net> wrote in news:013b01c7a6cf$feb64e30$6407a80a@pdimov2:
Andy wrote:
Thanks! This is the assurance I needed.
Andy, that's the opinion of one person. Personally I'm not planning to use random UUID, but only named ones. But you should bear in mind that there may be someone strongly opposing to this approach to use seed. For that reason I suggest that you still investigate the second reasonable option: "5. Create a function object with member RNG [...]". It might work even better... -- Janek Kozicki |

Janek Kozicki wrote:
Andy said: (by the date of Mon, 4 Jun 2007 20:32:35 +0000 (UTC))
"Peter Dimov" <pdimov@mmltd.net> wrote in news:013b01c7a6cf$feb64e30$6407a80a@pdimov2:
Andy wrote:
Thanks! This is the assurance I needed.
Andy, that's the opinion of one person.
It is more than opinion, although it would certainly help if a probability/stats expert takes a look at the code.

Andy wrote:
4. Use the current seed as the random numbers directly. Will this solution provide 'good' uuids? That is, is this source of random numbers good enough?
Thanks! This is the assurance I needed.
Andy.
Would this solution be much slower than using a static mt19937 engine? Sorry to keep harping on speed, but I see a significant use case for the UUID library in distributed systems generating lots of unique identifiers independently. If it is significantly slower without generating "better" random numbers than mt19937, then I would suggest a big note in the documentation recommending the uuid::create(engine) function for single-threaded applications. Dave Jenkins

On 04/06/07, Andy <atompkins@fastmail.fm> wrote:
I am favoring number 4, unless it will not produce good uuids. Then I favor number 5.
I guess I am looking for direction as to how to proceed.
Andy.
I prefer number 5. All you need do then is provide a get_seed() method that generates a decent seed; with number 4 it might well take a lot more time to implement and test a good source of random numbers and then it might add a extra dimension to maintaining the library. It seems to me that Boost.Random exists then it should be used by default as the source of random number generators unless, of course, what it provides is not good enough but I presume that is not the case. Jos -- Jos Hickson Software Engineer RawFlow Inc Old Pump House | 19 Hooper Street | London E1 8BU International: +44 (0)207 480 4220 Fax: +44 (0)207 481 4343 URL: www.rawflow.com *** RawFlow: Peer Streaming for All ***

Jos Hickson wrote:
On 04/06/07, Andy <atompkins@fastmail.fm> wrote:
I am favoring number 4, unless it will not produce good uuids. Then I favor number 5.
I guess I am looking for direction as to how to proceed.
Andy.
I prefer number 5. All you need do then is provide a get_seed() method that generates a decent seed; with number 4 it might well take a lot more time to implement and test a good source of random numbers and then it might add a extra dimension to maintaining the library. It seems to me that Boost.Random exists then it should be used by default as the source of random number generators unless, of course, what it provides is not good enough but I presume that is not the case.
Number 5 is a good solution for batch UUID generation, assuming that the RNG is seeded well. That said, I should note that a RNG is not really a source of randomness. Sure, it can be used to produce a sequence that appears sufficiently random for most computing purposes. For UUID generation, however, and specifically in the "one UUID per program" case, a RNG contributes exactly zero random bits. If you have a seed that contains K bits of entropy, and you generate from that seed an UUID by using a RNG, the UUID will contain exactly K bits of entropy. To put it differently, the probability that two seeds collide - 2^-K - is equal to the probability that two UUIDs collide, because equal seeds generate equal UUIDs. In other words, if you have a "decent" 32 bit seed, you're looking at 2^-32 probability of collisions, no matter how many RNGs you use as an intermediate layer. If you have a decent 128 bit seed, you can use it as an UUID as is, and passing it through a RNG will not change anything. And conversely, if you don't have a good source of randomness to use as a seed, you will not be able to generate a good UUID, so it's not possible to eliminate the "extra dimension".

On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
On 04/06/07, Andy <atompkins@fastmail.fm> wrote:
I am favoring number 4, unless it will not produce good uuids. Then I favor number 5.
I guess I am looking for direction as to how to proceed.
Andy.
I prefer number 5. All you need do then is provide a get_seed() method that generates a decent seed; with number 4 it might well take a lot more time to implement and test a good source of random numbers and then it might add a extra dimension to maintaining the library. It seems to me that Boost.Random exists then it should be used by default as the source of random number generators unless, of course, what it provides is not good enough but I presume that is not the case.
Number 5 is a good solution for batch UUID generation, assuming that the RNG is seeded well.
That said, I should note that a RNG is not really a source of randomness. Sure, it can be used to produce a sequence that appears sufficiently random for most computing purposes. For UUID generation, however, and specifically in the "one UUID per program" case, a RNG contributes exactly zero random bits. If you have a seed that contains K bits of entropy, and you generate from that seed an UUID by using a RNG, the UUID will contain exactly K bits of entropy. To put it differently, the probability that two seeds collide - 2^-K - is equal to the probability that two UUIDs collide, because equal seeds generate equal UUIDs.
In other words, if you have a "decent" 32 bit seed, you're looking at 2^-32 probability of collisions, no matter how many RNGs you use as an intermediate layer. If you have a decent 128 bit seed, you can use it as an UUID as is, and passing it through a RNG will not change anything. And conversely, if you don't have a good source of randomness to use as a seed, you will not be able to generate a good UUID, so it's not possible to eliminate the "extra dimension".
Understood. However, if the best implementation of uuid::create() depends on which of two usage patterns is assumed ("batch" or "one per program") maybe a better approach is to have both uuid_generator (for batch purposes) and uuid::create() (for the one-per-program usage) with the generator seeded using the randomness that uuid::create() uses. Jos

Jos Hickson wrote:
Understood. However, if the best implementation of uuid::create() depends on which of two usage patterns is assumed ("batch" or "one per program") maybe a better approach is to have both uuid_generator (for batch purposes) and uuid::create() (for the one-per-program usage) with the generator seeded using the randomness that uuid::create() uses.
We can consider dropping the two forms of uuid::create entirely and only provide uuid_generator. The discussion has demonstrated that they have some non-obvious pitfalls (one is slower than expected, the other assumes that the user has supplied a good seed and this is unlikely). class uuid_generator { unsigned rd_[5]; mt19937 en_; uuid_generator() { init rd_; seed en_ with rd_[4]; } operator() { generate a 128-bit number using en_; xor it with rd_[0..3]; return a uuid based on it; } }; seems to address both issues.

On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
Understood. However, if the best implementation of uuid::create() depends on which of two usage patterns is assumed ("batch" or "one per program") maybe a better approach is to have both uuid_generator (for batch purposes) and uuid::create() (for the one-per-program usage) with the generator seeded using the randomness that uuid::create() uses.
We can consider dropping the two forms of uuid::create entirely and only provide uuid_generator. The discussion has demonstrated that they have some non-obvious pitfalls (one is slower than expected, the other assumes that the user has supplied a good seed and this is unlikely).
class uuid_generator { unsigned rd_[5]; mt19937 en_;
uuid_generator() { init rd_; seed en_ with rd_[4]; }
operator() { generate a 128-bit number using en_; xor it with rd_[0..3]; return a uuid based on it; } };
seems to address both issues.
Yes, that's probably a better idea but if both versions of create() are dropped then should there be some means of providing a different engine for those who really want to? Jos

"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706050741r3dfff77fqc5f26a5fb9a6adac@mail.gmail.com:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
Understood. However, if the best implementation of uuid::create() depends on which of two usage patterns is assumed ("batch" or "one per program") maybe a better approach is to have both uuid_generator (for batch purposes) and uuid::create() (for the one-per-program usage) with the generator seeded using the randomness that uuid::create() uses.
We can consider dropping the two forms of uuid::create entirely and only provide uuid_generator. The discussion has demonstrated that they have some non-obvious pitfalls (one is slower than expected, the other assumes that the user has supplied a good seed and this is unlikely).
class uuid_generator { unsigned rd_[5]; mt19937 en_;
uuid_generator() { init rd_; seed en_ with rd_[4]; }
operator() { generate a 128-bit number using en_; xor it with rd_[0..3]; return a uuid based on it; } };
seems to address both issues.
Yes, that's probably a better idea but if both versions of create() are dropped then should there be some means of providing a different engine for those who really want to?
Jos
Yes. Change uuid_generator to be a template template <typename Engine> class uuid_generator { unsigned rd_[5]; Engine en_; uuid_generator() { init rd_; seed en_ with rd_[4]; } uuid operator() { generate a 128-bit number using en_; xor it with rd_[0..3]; return a uuid based on it; } }; But, how does the xor part help? It is just for the case where we are only producing one uuid? Also it is possible that one chooses to use a random number generator that can't be seeded (for example based on /dev/urandom), and I'm not sure how this fits in. Actually, this could just be a specialization. Andy.

Andy wrote: ...
template <typename Engine>
We can supply a default here: template<class Engine = mt19937> It's still slightly less convenient to use uuid_generator<> g; and I'm not quite sure why one would want to replace the default engine, but it's good enough, I guess.
class uuid_generator { unsigned rd_[5]; Engine en_;
uuid_generator() { init rd_; seed en_ with rd_[4]; }
uuid operator() { generate a 128-bit number using en_; xor it with rd_[0..3]; return a uuid based on it; } };
But, how does the xor part help? It is just for the case where we are only producing one uuid?
The xor part just varies the sequence produced by the RNG by rd_[0..3]. The RNG seeded by the 32-bit value rd_[4] can produce 2^32 distinct UUID sequences. With the xor part - and assuming that the contents of rd_[] are random for simplicity - it will produce 2^32 * 2^128 distinct UUID sequences. The statistical properties of the UUID sequence will still correspond to the original 2^32 source, but this doesn't matter for our purposes since UUID's or their bits are not cross-checked for correlations with one another, only collisions matter. In short, the above is a relatively easy way to approximate a RNG seeded with the entire contents of rd_ instead of throwing 4/5 of it away. You said earlier that you had a problem with the iterator range seed, and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway. It also has the advantage of being "obviously" at least as random as just using the Engine, so this should help alleviate people's concerns. :-)

On 6/5/07, Peter Dimov <pdimov@mmltd.net> wrote:
template <typename Engine>
We can supply a default here:
template<class Engine = mt19937>
It's still slightly less convenient to use
uuid_generator<> g;
Perhaps the commonly used: template<typename Engine> class basic_uuid_generator { ... }; typedef basic_uuid_generator<mt19937> uuid_generator; would fit nicely here? -- Caleb Epstein

On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
and I'm not quite sure why one would want to replace the default engine, but it's good enough, I guess.
uuid_generator<random_device> seems like a reasonable desire to me, particularly if someone implemented it for windows (using RtlGenRandom as was mentioned in the other thread, perhaps). ~ Scott McMurray

On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed? So for seeding one could do something like: uuid_generator() { init rd; numBytesInSeed = numBytesNeededFor(Engine::max_value() - Engine::min_value()); en_.seed(rd, rd + numBytesInSeed); } Regards, Jos

Jos Hickson wrote:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed?
The seed for mt19937 is 624 words.

On 06/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed?
The seed for mt19937 is 624 words. Aah, a bit bigger then!

"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706060823v59628c64xd1bcda61e9fd5f48@mail.gmail.com:
On 06/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed?
The seed for mt19937 is 624 words. Aah, a bit bigger then!
I have been thinking about this for awhile now, and maybe I should use Peter's suggestion and only support random number generators that can be seeded with a single number. And xor with rd_. I have been playing with another solution. I wrapped the seed function in a class that models the random number generator concept. Then used Boost.Iterators' function output iterator, and feed it into the seed function that takes iterators of the random number generator. // not that this is not quite complete class seed_rng { public: seed_rng() : rd_index_(5) {} unsigned operator()() { if (rd_index_ >= 5) { sha1_random_digest_(); // fill rd_ with new data rd_index_ = 0; } return rd_[rd_index_++]; } private: sha1_random_digest_(); unsigned rd_[5]; int rd_index_; }; template <typename Engine=boost::mt19937> class uuid_generator { public: uuid_generator() { boost::seed_rgn e; boost::generator_iterator<boost::seed_rng> begin(&e); boost::generator_iterator<boost::seed_rnd> end; en_.seed(begin, end); } boost::uuid operator()() { // get random data from en_ // return uuid based on this data } private: Engine en_; }; Now this works fine, the slow seed_rng is used to fully seed the random number generator used. It can be specialized for boost::random_device since boost::random_device does not have a seed function. Andy.

On Jun 11, 2007, at 8:33 PM, Andy wrote:
"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706060823v59628c64xd1bcda61e9fd5f48@mail.gmail.com:
On 06/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed?
The seed for mt19937 is 624 words. Aah, a bit bigger then!
I have been thinking about this for awhile now, and maybe I should use Peter's suggestion and only support random number generators that can be seeded with a single number. And xor with rd_.
Please don't! As mentioned before that would only give 2^32 different UUIDs. The best solution is just to allow any generator producing values of a specified type (e.g. unsigned int or unsigned char) to be used with create(), and leave the initialization of the generator to the user. Matthias

On 11/06/07, Andy <atompkins@fastmail.fm> wrote:
"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706060823v59628c64xd1bcda61e9fd5f48@mail.gmail.com:
On 06/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed?
The seed for mt19937 is 624 words. Aah, a bit bigger then!
I have been thinking about this for awhile now, and maybe I should use Peter's suggestion and only support random number generators that can be seeded with a single number. And xor with rd_.
IMHO such a restriction would be to the detriment of the library whereas the solution below would seem a lot better to me. Really what we need is a Boost.Seed library to compliment Boost.Random so that anybody like yourself using the latter doesn't have to spend all this time wondering how to seed things generically.
I have been playing with another solution. I wrapped the seed function in a class that models the random number generator concept. Then used Boost.Iterators' function output iterator, and feed it into the seed function that takes iterators of the random number generator.
// not that this is not quite complete class seed_rng { public: seed_rng() : rd_index_(5) {}
unsigned operator()() { if (rd_index_ >= 5) { sha1_random_digest_(); // fill rd_ with new data rd_index_ = 0; } return rd_[rd_index_++]; } private: sha1_random_digest_();
unsigned rd_[5]; int rd_index_; };
template <typename Engine=boost::mt19937> class uuid_generator { public: uuid_generator() { boost::seed_rgn e; boost::generator_iterator<boost::seed_rng> begin(&e); boost::generator_iterator<boost::seed_rnd> end; en_.seed(begin, end); }
boost::uuid operator()() { // get random data from en_ // return uuid based on this data } private: Engine en_; };
Now this works fine, the slow seed_rng is used to fully seed the random number generator used. It can be specialized for boost::random_device since boost::random_device does not have a seed function.
Andy.

On 12/06/07, Jos Hickson <jos.hickson@gmail.com> wrote:
On 11/06/07, Andy <atompkins@fastmail.fm> wrote:
"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706060823v59628c64xd1bcda61e9fd5f48@mail.gmail.com:
On 06/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
Jos Hickson wrote:
On 05/06/07, Peter Dimov <pdimov@mmltd.net> wrote:
[snip] ... and if Engine is a template parameter, we probably need to support engines that only take a 32 bit seed anyway.
Isn't it the case that the maximum seed size is related (equal?) to the difference between the max and min values produced by a particular engine and also that all the engines in Boost Random provide the iterator version of seed?
The seed for mt19937 is 624 words. Aah, a bit bigger then!
I have been thinking about this for awhile now, and maybe I should use Peter's suggestion and only support random number generators that can be seeded with a single number. And xor with rd_.
IMHO such a restriction would be to the detriment of the library whereas the solution below would seem a lot better to me.
I should point out that "the solution below" refers to your use of boost::generator_iterator and not to my trite comment about a Boost.Seed library!
Really what we need is a Boost.Seed library to compliment Boost.Random so that anybody like yourself using the latter doesn't have to spend all this time wondering how to seed things generically.
I have been playing with another solution. I wrapped the seed function in a class that models the random number generator concept. Then used Boost.Iterators' function output iterator, and feed it into the seed function that takes iterators of the random number generator.
// not that this is not quite complete class seed_rng { public: seed_rng() : rd_index_(5) {}
unsigned operator()() { if (rd_index_ >= 5) { sha1_random_digest_(); // fill rd_ with new data rd_index_ = 0; } return rd_[rd_index_++]; } private: sha1_random_digest_();
unsigned rd_[5]; int rd_index_; };
template <typename Engine=boost::mt19937> class uuid_generator { public: uuid_generator() { boost::seed_rgn e; boost::generator_iterator<boost::seed_rng> begin(&e); boost::generator_iterator<boost::seed_rnd> end; en_.seed(begin, end); }
boost::uuid operator()() { // get random data from en_ // return uuid based on this data } private: Engine en_; };
Now this works fine, the slow seed_rng is used to fully seed the random number generator used. It can be specialized for boost::random_device since boost::random_device does not have a seed function.
Andy.

"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706120226n717d457fueca21627444a90d3@mail.gmail.com: < snip >
Really what we need is a Boost.Seed library to compliment Boost.Random so that anybody like yourself using the latter doesn't have to spend all this time wondering how to seed things generically.
I think that the seed class would be a good addition to Boost.Random. < snip >
template <typename Engine=boost::mt19937> class uuid_generator { public: uuid_generator() { boost::seed_rgn e; boost::generator_iterator<boost::seed_rng> begin(&e); boost::generator_iterator<boost::seed_rnd> end; en_.seed(begin, end); }
boost::uuid operator()() { // get random data from en_ // return uuid based on this data } private: Engine en_; };
Now this works fine, the slow seed_rng is used to fully seed the random number generator used. It can be specialized for boost::random_device since boost::random_device does not have a seed function.
Andy.
So now I have this, and I like it, but I have been unable to use boost::enable_if, and boost::disable_if to enable/disable the constructors.
template <typename Engine = boost::mt19937> class basic_uuid_generator { public: // only enable if engine can not be seeded // boost::random_device does not need seeding basic_uuid_generator(/*enable only if Engine=boost::random_device*/) {} // enable if engine can be seeded // seed engine basic_uuid_generator(/*enable if Engine != boost::random_device*/) { boost::seed_rgn seed; boost::generator_iterator<boost::seed_rng> begin(&seed); boost::generator_iterator<boost::seed_rng> end; en_.seed(begin, end); } // assume they have already seeded e and make a copy // maybe we should store a reference to e? explicit uuid_generator(Engine const& e) : en_(e) {} boost::uuid operator()() { ... } priavte: Engine en_; }; typedef basic_uuid_generator<> uuid_generator;

On 13/06/07, Andy <atompkins@fastmail.fm> wrote:
"Jos Hickson" <jos.hickson@gmail.com> wrote in news:fc03d05a0706120226n717d457fueca21627444a90d3@mail.gmail.com:
< snip >
Really what we need is a Boost.Seed library to compliment Boost.Random so that anybody like yourself using the latter doesn't have to spend all this time wondering how to seed things generically.
I think that the seed class would be a good addition to Boost.Random.
< snip >
template <typename Engine=boost::mt19937> class uuid_generator { public: uuid_generator() { boost::seed_rgn e; boost::generator_iterator<boost::seed_rng> begin(&e); boost::generator_iterator<boost::seed_rnd> end; en_.seed(begin, end); }
boost::uuid operator()() { // get random data from en_ // return uuid based on this data } private: Engine en_; };
Now this works fine, the slow seed_rng is used to fully seed the random number generator used. It can be specialized for boost::random_device since boost::random_device does not have a seed function.
Andy.
So now I have this, and I like it, but I have been unable to use boost::enable_if, and boost::disable_if to enable/disable the constructors.
template <typename Engine = boost::mt19937> class basic_uuid_generator { public:
// only enable if engine can not be seeded // boost::random_device does not need seeding basic_uuid_generator(/*enable only if Engine=boost::random_device*/) {}
// enable if engine can be seeded // seed engine basic_uuid_generator(/*enable if Engine != boost::random_device*/) { boost::seed_rgn seed; boost::generator_iterator<boost::seed_rng> begin(&seed); boost::generator_iterator<boost::seed_rng> end; en_.seed(begin, end); }
// assume they have already seeded e and make a copy // maybe we should store a reference to e? explicit uuid_generator(Engine const& e) : en_(e) {}
boost::uuid operator()() { ... }
priavte: Engine en_; };
typedef basic_uuid_generator<> uuid_generator;
I might be wrong here but I believe that as enable_if relies on SFINAE then the template parameter it depends on needs to be a template parameter of the constructor or method itself rather than of the enclosing class. As the class can be instantiated with any template parameter then both versions of the constructor will be available. I think you would need to specialise the class itself for boost::random_device. Jos

Andy wrote:
I have been playing with another solution. I wrapped the seed function in a class that models the random number generator concept. Then used Boost.Iterators' function output iterator, and feed it into the seed function that takes iterators of the random number generator.
The Boost.Random engines seem to provide a seed overload that takes a generator, so it would be easier to just use that. But you should also keep in mind that this approach will result in 125 calls to sha1_random_digest each time an uuid_generator<> is created. This might not be acceptable for the typical case where the user only needs one UUID.

On Jun 4, 2007, at 7:18 PM, Andy wrote:
Other than the current approach (which I agree is not acceptable), I see 6 alternatives to the uuid::create() function. I believe all of them have been mentioned before.
... 5. Create a function object (that has the random number generator as a member) that produces uuids. Is this too complicated? i.e.: class uuid_generator { public: uuid_generator() { engine.seed(get_seed()); } uuid operator() const; private: Engine& engine; }; ...
In order to allow users with special needs full control, the ability to provide an engine to create is essential to have - at least as one option. I'm thinking of 100'000+-node BlueGene machines and planned new 1'000'000+ -CPU 10-petaflop machines. Getting good UUIDs on such machines might need special considerations, and by having the option to pass an engine these could be taken care of. Matthias
participants (8)
-
Andy
-
Caleb Epstein
-
Dave Jenkins
-
Janek Kozicki
-
Jos Hickson
-
Matthias Troyer
-
me22
-
Peter Dimov