Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2005-09-14 21:10:22


"Robert Ramey" <ramey_at_[hidden]> writes:

> David Abrahams wrote:
>
>> The best thing you could do, IMO, is write a recommendation that
>> works on conforming compilers, e.g.
>>
>> Overload serialize in the namespace of your class.
>>
>
> Oh, that's news to me. I thought that would work only in compilers that
> implemented ADL.

I have to say, Robert, I am completely baffled at your last two
replies. If I didn't know better, I'd say you were just playing
head games with me.

I said "conforming compilers." ADL is a part of the standard. Any
compiler not implementing it is nonconforming. But surely you already
knew that?

> I understood from previous postings that this would entail building
> a set a macros which addressed the varying aspect of conformity.

The macro and usage pattern I suggested was there to address the
nonconforming compilers: the ones that don't implement ADL, and don't
implement 2-phase name lookup. I don't believe there's a compiler
that implements 2-phase name lookup but doesn't implement ADL; that
would be the only case my second suggestion wouldn't cover.

> In 1.32 I had #if ... in order to place stl serializations in either
> the stl namespace or the boost::serialization namespace depending on
> whether the compiler supported ADL and two-phase lookup or neither
> of these things.

I can't see how putting overloads in std:: could be of any use to your
library on compilers that don't support ADL, so I assume you put them
in std:: on conforming compilers and put them in boost::serialization
for the ones that don't conform. Putting new definitions in namespace
std invokes undefined behavior. So it sounds like, depending on the
compiler, you either have a broken compiler (no ADL) or a broken
library (undefined behavior). That is a no-win approach.

How to solve this problem is not a mystery. We just had a long thread
about it entitled "[range] How to extend Boost.Range?" Have you
completely missed it?

I am getting frustrated here because it seems to me that you haven't
done your homework, yet, after asking me to propose solutions, you
resist and call them ugly.

> That was the only way I could get everything to compile on all
> platforms.

It seems to me from what you've been saying that your approach to
solving the serialize( ... ) dispatching/customization problem here
has been to try different configurations of the code until you could
get it to pass select tests on certain platforms, without regard for
standard conformance and theoretical portability. That's a well-known
way to end up with code that doesn't work portably or in
configurations you haven't tried (for example, hmm, let me
think... when the order of headers changes).

> Then generated the requirement to address the issue in the
> documentation with a table on what the user should use. That's what
> I call ugly.

Huh? I didn't propose a table. And, IMO there's nothing particularly
inelegant about using a table in documentation; there's
well-established precedent for it in the standard and Boost.
Certainly documenting what the user should do is a must. So I don't
see what you're unhappy about.

If you don't like having to tell users to do something special on
nonconforming compilers, well, the answer is to either find a clean
idiom that works everywhere (for this particular problem, many have
tried -- I don't believe such an idiom exists) or stop supporting
those broken compilers.

> FWIW - I'm not sure if the example you site would ever come up in
> practice - it certainly hasn't yet. If I understand this correctly,
> the problem would occur if someone writes somethng like:
>
> my_class.hpp
>
> class my_class ...
>
> template<class Archive, my_class>
> void serialize(Archive &ar, my_class & t, cont unsigned int version); //
> declaration only
>
> my_app.cpp
>
> main(...
> {
> my_class mc;
> ...
> ar & mc;
> }
>
> template<class Archive, my_class>
> void serialize(Archive &ar, my_class & t, cont unsigned int version); //
> definition only

No. The problem has nothing to do with the location of definitions.
You can keep all definitions and declarations together, and the
problem still occurs.

  // <boost/serialization/some_file.hpp>
  namespace boost { namespace serialization
  {
    // Something in the serialization library
    template <class T>
    int call_serialize(T const& x)
    {
       serialize(x);
       return 0;
    }
  }}

  // "user_header.hpp"
  namespace me
  {
    class X {};
  }

  // User overloads in boost::serialization, per your recommendation
  namespace boost { namespace serialization
  {
    void serialize(me::X)
    {
       // definition
    }
  }}

  // "user.cpp"
  int y = boost::serialization::call_serialize(me::X());

Read the passage of the standard I quoted to you, and look carefully
at the example above. Do your homework.

> I'm not absolutly sure that this is the only scenario which would
> create problem and that's why I wanted more time to consider it.
>
> If this is the only way the two-phase problem could manifest itself,
> I would guess that in practice it would never be an issue. I don't
> see users doing this. This was what I was getting at when I asked
> why the problem hasn't appeared upto now. Of course I don't really
> know whether this is because so many compilers fail to implement
> two-phase lookup.

Jeez Louise, Robert! This is a well-known problem. It occurs in
practice. There's nothing unique about the serialization library in
this respect. It has a customization point (called "serialize") that
is used from the library without qualification. You expect to find it
via Argument Dependent Lookup, **whose job is to look for overloads in
the associated namespace of the arguments**. If the overload is in
boost::serialization and no arguments are associated with that
namespace, it won't be found via ADL. That leaves ordinary lookup,
which only looks backwards from the templates point-of-definition.
Could that _possibly_ be any clearer?

Lots of generic libraries have a similar dispatching issue to address,
e.g. Boost.Range. There's a nice, free online compiler againist which
you can test your 2-phase problems. I've quoted the standard chapter
and verse. I've given you simple example programs that demonstrate
the problem. I don't know what more I can do... do I need to build a
program that actually uses the serialization library before you'll
believe me?!

>>> b) I'm going to remove the class declaration and function
>>> implementation from the Archive Concept part of the document.
>
>> Are you planning to replace it with anything? It would be pretty
>> silly to have a section called Archive Concept with no concept
>> description.
>
> I meant to leave in the text below the "class" schema. Before I started I
> reviewed again the SGI documents and concluded that that text could be fit
> into the SGI form for concepts. I'm using
> http://www.sgi.com/tech/stl/Container.html as a "typical example". This
> isn't hard - its basically a question of reformatting. I hope that will be
> satisfactory.

Me too. That will depend on your execution :)

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk