|
Boost : |
Subject: Re: [boost] [UUID] PODness Revisited
From: Adam Merz (adammerz_at_[hidden])
Date: 2008-12-25 06:00:47
Vladimir Batov writes:
> OK, I thought some more about those PODs and I admittedly do not see any
> magic in that Poor Old Dude. Scott, you are clearly excited about them, so
> pls help me out here.
While you may not see the "magic" in POD types, I can't fathom what exactly
you have against them either. Do you find them more confusing or harder to
use? Do you find static initialization syntax aesthetically offensive? Is it
your (no offense, but extremely misguided, IMO) lingering impression that POD
types are a legacy of C that should be ignored whenever possible? A list of
examples in favor of making UUID a POD type was presented, and you've argued
against those examples without actually saying what you think the drawback is.
> That's what *I* see (caveat: I admit not knowing much about Boost.MPI and
> Boost.Interprocess requirements and expectations).
Again, no offense intended, but I find it a bit discomfiting that the person
arguing most vocally on this issue would make this admission. Just because you
don't have personal knowledge of a use case where UUID being a POD type would
be greatly beneficial doesn't mean such a use case doesn't exist.
> 1. Boost.MPI efficiency does not seem to rely on PODness. Rather it seem to
> be due to serialization (or rather ability to bypass it).
This isn't technically correct, I think; in MPI's case (though not
Interprocess'), the type must be serializable regardless, but the ideal
efficiency scenario comes from specializing both boost::mpi::is_mpi_datatype
and boost::serialization::is_bitwise_serializable. Note that the documentation
for these traits ([1] and [2], respectively) both specifically mention POD
types -- this is no coincidence.
[1] http://www.boost.org/doc/libs/1_37_0/doc/html/boost/mpi/is_mpi_datatype.html
[2] http://www.boost.org/doc/libs/1_37_0/libs/serialization/doc/traits.html
> I feel, that Andy's UUID class is well-suited to add no-serialization
> transmission functionality when needed (without the rest of us paying for
> that). That is, conceptually Andy's full-fledged UUID does not have that
> limitation.
While this is generally correct, I think you're missing the larger point. In
modern C++, types intentionally created as POD types are often (not always)
done so to absolutely maximize the efficiency of copying that type. The
existance of the is_pod type trait in boost.type_traits/TR1/C++0x reinforces
this -- e.g. in many implementations, std::copy will use memcpy to perform an
ideally efficient copy when is_fundamental<T>::value || is_pod<T>::value.
Additionally, a POD type's synthesized copy constructor is generally merely a
memcpy.
In order to specialize is_bitwise_serializable for a given type, tracking [3]
must be disabled for that type. In practice this means one should avoid using
pointers to that type inside of other serializable types. UUID's "PODness"
would allow the type itself to communicate to users that it is truely a value
type, and is meant to be used as such (i.e., always passed/held by value); one
wouldn't typically pass or hold a pointer to an int because it's so cheap to
copy, so one (hopefully) wouldn't be inclined to do that for another type that,
for all intents and purposes, presents itself as a primitive type.
[3]
http://www.boost.org/doc/libs/1_37_0/libs/serialization/doc/special.html#objecttracking
> If it is a MPI implementation-specific restriction/limitation, I'd expect
> we'd look at addressing it in MPI rather than shaping other classes to match
> it.
This is an unreasonable thought process, IMO. If a type has an good use case
with another library (in this case, UUID with Serialization/MPI/Interprocess),
it's up to the type to conform to the library in an ideal fashion, not the
other way around. E.g., lexical_cast and serialization don't go out of their
way to work with every other type in Boost, but many types in boost have
serialization and lexical_cast support.
> 2. Scott, you correctly mention that most often we "don't want to send UUIDs
> by themselves". The thing is that chances of that bigger class being a POD
> are diminishing dramatically (if not already infinitely close to 0).
This is extremely off base, and points back to your lack of knowledge
regarding MPI, I think. When writing an app/library/algorithm intended for use
in a high-performance parallel context, one goes out of their way to use POD
types extensively, for the sake of performance. Yes, the fact that MPI works
with boost.serialization is nice, but when performance is critical,
memcpy'able types are key; and this is C++, after all -- of course performance
is critical, otherwise why bother? ;-) So the more external (in relation to
the user's code) libraries that work out of the box, the better; I think to
argue that a type such as UUID (which is a low-level, fundamental value type,
and specifically *very* likely to be used in an inter-process context) should
*not* automatically work in an ideal fashion in this scenario, one must have
an *extremely* convincing argument, IMO. And so far, I haven't seen one
presented. ;-)
> 3. As for deployment of an object in shared memory, it does not have to be a
> POD either.
Please take another look at the specific link Scott provided ([4]);
boost::interprocess::message_queue only copies raw bytes between processes, so
for non-POD types generally that requires that an object be binary serialized
before sending. However, for a POD type, binary serialization is a completely
redundant process (read: a complete waste of CPU cycles); one can just send
the bytes of the object directly, and as an added bonus, avoid becoming
dependant on the somewhat heavy serialization library altogether.
Again, the fact that this might be possible even if UUID were not a POD type
is somewhat irrelevant, IMO -- as important as it is to be able to use a type
ideally and efficiently, it is equally as important that a type _communicate
to the user_ its ideal usage. Personally, I would feel as though I was playing
with fire (and in a literal sense, would probably be invoking UB) by taking
the raw bytes underlying a non-POD type and using them with message_queue, to
the extent that I wouldn't bother, as much as it might vex me; when a type is
a POD type, I *know* immediately that it's safe -- no worries!
I want to touch on a few other points as well, were UUID to be a POD type:
1. The default constructor behavior/existance debate would be put to rest. ;-)
2. The efficiency of lexical_cast would be better than *any* default
constructor behavior, regardless of which one was ultimately decided upon.
3. One would typically initialize a UUID in exactly the same manner as though
it had no default constructor, which most people seem to find an acceptable
option.
4. Initializing a nil UUID would become more succinct. Contrast
'uuid id(uuid::nil());' and 'uuid id = {};', or 'id(uuid::nil())' and
'id()' in a constructor initialization list. Assuming any level of
familiarity with aggregates, the latter are much more concise, IMO. (And
C++0x will certainly introduce that familiarity if one doesn't have it
already.)
5. Static initialization has been greatly underrated so far in this
discussion. My first use case for a Boost UUID library would be to replace
some homegrown COM/XPCOM encapsulation code. In dealing with COM/XPCOM, it
is *extremely* common to have hardcoded UUIDs, and *many* of them. Trivial
work though it may be, spending application/library startup time
initializing hundreds/thousands of UUIDs when they could be statically
initialized is senseless.
6. Regarding the potential for uninitialized state: I personally view UUID as
a fundamental, borderline primitive type (others will almost certainly
disagree); uninitialized state is generally understood and accepted for
actual primitive types, so why should it be such a scary concept for UUID?
7. Lastly, to reiterate: this is C++. Every type, every library, every
algorithm should be written with performance and efficiency as primary
considerations. There are demonstrable use cases where UUID can work more
efficiently as a POD type, but no convincing arguments have been presented
in favor of non-PODness. And as Scott and David mentioned, UUID could be
trivially wrapped in a non-POD if one were so inclined, and they'd even get
to choose their own default constructor behavior. ;-)
Apologies for the lengthy post, but the threads regarding UUID thus far have
been also quite lengthy and as I haven't responded yet, I had a few strong
opinions stored up. ;-)
> Best,
> V.
Regards,
Adam Merz (Dodheim in #boost)
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk