Re: [boost] [Review] UUID library (mini-)review starts today, November 23rd

... Hmm, I looked at some other value types in boost and this is what I found.
boost::gregorian::date() creates an 'not_a_date_time'. One can exclude this constructor with a #define. boost::posix_time::ptime() is the same as above boost::function() is initialized as empty std::string() is initialized as empty boost::numeric::interval() the created interval is the singleton zero boost::quaternion() initialized each component/member to the default values for their type (i.e. zero for floating numbers) boost::optional() is uninitialized boost::rational() is 'zero'
Some usages are legitimate (like an empty std::string is still a string and boost::rational() default initialization to 0). Some usages are IMHO mildly confusing (like boost::function()) as there is a clear understanding that it has to have some function, i.e. no-function is clearly invalid. Some others are IMHO unfortunate and quite confusing -- does boost::gregorian::date() mean 'not_a_date_time' or boost::gregorian::date() means 'now'? My view is that the interface needs to be intuitive. With the date I would not probably use the default constructor at all as it is not immediately clear what it does. Instead I'd have boost::date::invalid() and boost::date::now(). Deploying the default constructor for something apart from default construction is a hack unfortunately abused quite often. It does not make it right though.
... And, what should the default constructor do? Create a null uuid or a unique one?
It feels like you are struggling to associate the default constructor with something. :-) You know, you really do not have to. The default constructor has quite a clear meaning. If a class has no functionality matching that meaning, that class should not be defining the default constructor and should not be hijacking that constructor for anything else. Convenient? Sure. Good API? Hmm, I am not sure -- a good interface should be saying what it means.
... This is what I want to prevent as well. I'm concerned about the user who does: boost::uuid u; // this create a unique uuid and does not realize that they just ran a computational intensive function when they didn't need or want to.
I am not sure what there is not to realize? The language is clear that "boost::uuid u" calls the default constructor... whatever that constructor does (intensive or not). Treating object creation as it were built-in types (i.e. created uninitialized/invalid) is wrong, encourages the wrong programming practice and expectations and should not be encouraged.
... Fair. These people are likely either doing:
boost::uuid u = get_uuid_from_somewhere(); // eg from database
or
boost::uuid u; get_uuid_from_somewhere(u); // eg from database
The second case would create a unique uuid unless they remembered to do: boost::uuid u(boost::uuid::null());
There is a big difference between #1 and #2 and the language is lucidly clear what the difference is -- #1 does just one step and creates and object from another object; #2 does 2 steps -- creates an object with the default constructor and then applies operator=(). Painting over that difference and encouraging user's ignorance of that important subtlety (for a novice) won't do anyone any good.
It sounds very easy for one to forget. This doesn't change symentics, it is just less efficient.
It *does* change semantics and it does change execution flow and the language cannot be any clearer on that. If you mean #1, then instead of making #2 to behave like #1, I'd prohibit #2 altogether by not providing the default constructor. Having said that to *me* "boost::uuid id;" makes sense as I read it as the language describes -- I create a UUUID with the default constructor. My interpretation of it is that I trust the implementer to do the right thing for me -- the user. I'd say, if that interpretation is subtle and really controversial, just get rid of the default constructor altogether. In the end, it's just my view. I am not prepared to die fighting for it. :-) Thanks, Vladimir.

On Wed, 3 Dec 2008 09:15:56 +1000, Vladimir.Batov@wrsa.com.au said:
... Hmm, I looked at some other value types in boost and this is what I found.
boost::gregorian::date() creates an 'not_a_date_time'. One can exclude this constructor with a #define. boost::posix_time::ptime() is the same as above boost::function() is initialized as empty std::string() is initialized as empty boost::numeric::interval() the created interval is the singleton zero boost::quaternion() initialized each component/member to the default values for their type (i.e. zero for floating numbers) boost::optional() is uninitialized boost::rational() is 'zero'
Some usages are legitimate (like an empty std::string is still a string and boost::rational() default initialization to 0). Some usages are IMHO mildly confusing (like boost::function()) as there is a clear understanding that it has to have some function, i.e. no- function is clearly invalid. Some others are IMHO unfortunate and quite confusing -- does
boost::gregorian::date() mean 'not_a_date_time' or boost::gregorian::date() means 'now'?
My view is that the interface needs to be intuitive. With the date I would not probably use the default constructor at all as it is not immediately clear what it does. Instead I'd have boost::date::invalid() and boost::date::now(). Deploying the default constructor for something apart from default construction is a hack unfortunately abused quite often. It does not make it right though.
... And, what should the default constructor do? Create a null uuid or a unique one?
It feels like you are struggling to associate the default constructor with something. :-) You know, you really do not have to. The default constructor has quite a clear meaning. If a class has no functionality matching that meaning, that class should not be defining the default constructor and should not be hijacking that constructor for anything else. Convenient? Sure. Good API? Hmm, I am not sure -- a good interface should be saying what it means.
I also wonder about this usefulness of the default constructor for boost::uuids::uuid. Maybe it should just be left out.
... This is what I want to prevent as well. I'm concerned about the user who does: boost::uuid u; // this create a unique uuid and does not realize that they just ran a computational intensive function when they didn't need or want to.
I am not sure what there is not to realize? The language is clear that "boost::uuid u" calls the default constructor... whatever that constructor does (intensive or not). Treating object creation as it were built-in types (i.e. created uninitialized/invalid) is wrong, encourages the wrong programming practice and expectations and should not be encouraged.
I understand when a default constructor is called. I am not sure everyone will expect the same thing from the default constructor for boost::uuids::uuid (null or unique).
... Fair. These people are likely either doing:
boost::uuid u = get_uuid_from_somewhere(); // eg from database
or
boost::uuid u; get_uuid_from_somewhere(u); // eg from database
The second case would create a unique uuid unless they remembered to do: boost::uuid u(boost::uuid::null());
There is a big difference between #1 and #2 and the language is lucidly clear what the difference is -- #1 does just one step and creates and object from another object; #2 does 2 steps -- creates an object with the default constructor and then applies operator=(). Painting over that difference and encouraging user's ignorance of that important subtlety (for a novice) won't do anyone any good.
My point was that in both #1 and #2 the user didn't want to generate a unique uuid.
It sounds very easy for one to forget. This doesn't change symentics, it is just less efficient.
It *does* change semantics and it does change execution flow and the language cannot be any clearer on that. If you mean #1, then instead of making #2 to behave like #1, I'd prohibit #2 altogether by not providing the default constructor. Having said that to *me* "boost::uuid id;" makes sense as I read it as the language describes -- I create a UUID with the default constructor. My interpretation of it is that I trust the implementer to do the right thing for me -- the user. I'd say, if that interpretation is subtle and really controversial, just get rid of the default constructor altogether.
In the end, it's just my view. I am not prepared to die fighting for it. :-)
Nor am I.
Thanks, Vladimir.
Regards, Andy Tompkins

... And, what should the default constructor do? Create a null uuid or a unique one?
It feels like you are struggling to associate the default constructor with something. :-) You know, you really do not have to. The default constructor has quite a clear meaning. If a class has no functionality matching that meaning, that class should not be defining the default constructor and should not be hijacking that constructor for anything else. Convenient? Sure. Good API? Hmm, I am not sure -- a good interface should be saying what it means.
I also wonder about this usefulness of the default constructor for boost::uuids::uuid. Maybe it should just be left out.
Keep in mind you can't put a uuid into a std container if it isn't default constructible. Regards Hartmut

AMDG Hartmut Kaiser wrote:
I also wonder about this usefulness of the default constructor for boost::uuids::uuid. Maybe it should just be left out.
Keep in mind you can't put a uuid into a std container if it isn't default constructible.
Yes you can. The standard only requires that types stored in containers by CopyConstructible and Assignable. (23.1) In Christ, Steven Watanabe
participants (4)
-
Andy Tompkins
-
Hartmut Kaiser
-
Steven Watanabe
-
Vladimir.Batov@wrsa.com.au