Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2005-09-15 09:27:02


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

> David Abrahams wrote:
>
>> 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 haven't followed it.
>
>> I am getting frustrated here because it seems to me that you haven't
>> done your homework,
>
> That's why I asked for more time to consider the issue - but you couldn't
> accept that.

No, I can't accept that another release should go out with incorrect
instructions for using the library when you've been told how you can
fix them.

>> 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 only partly true. I start out with what I believe is standard
> conforming code. The I compile it on all my platforms and discover
> problems. Then try to make modifications so that it passes with all the
> compilers and still is standard conforming. BOOST macros play an important
> role here. Sometimes standard conforming code has to be suppresed and
> replaced with other standard conforming code - e.g ar.template
> register_type<my_class>() gets replaced with
> ar.register_type(static_cast<my_class *>(NULL)). (God I hope that's
> conforming.).

That's certainly conforming, but also ugly as all getout. I'd rather
recommend

       ar.register_type((myclass*)0)

  or
       register_type<my_class>(ar)

both of which can be done portably, but you obviously have a different
sense of "ugly."

> In this case I had to confront the question of which namespace
> serializations of standard containers should be placed in and still
> have the code compile and function with all compilers. The first
> solution used either std or boost::serialization depending upon
> configuration macros. It seemed very arbitrary to me.

No surprise. There's never a reason for the library to stick
overloads in std, whether compilers are conforming or not. It can
always put the overloads in boost::serialization as long as it
guarantees they're seen before they're used.

FWIW, if you had used an operator like operator& instead of
"serialize" this could've been much smoother -- all the compilers I
know have at least partial support for ADL that works for the
operators.

> By arranging things so that the serialize(..) template was allways
> called from within the boost::serialization namespace I thought I
> had fixed the problem by permiting the overloads to be found via
> ordinary lookup.

Ordinary lookup doesn't look forward from the template's point of
definition. But I guess you know that by now.

>>> 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.
>
> I was refering to my previous solution.

Then let's get back on topic. You asked for a suggestion about how to
address the issue, I gave you one that was simple for conforming
compilers and slightly more complicated for the broken ones, and you
called it "ugly." I asked you to explain what you didn't like about
it, and that's how we ended up here. Now you're telling me that all
along you've been talking about your previous solution. So what's
wrong with my suggestion?

>> 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)
>
> The problem when one writes code which is to be portable accross compilers -
> say serialization of the std::set .

Yes. I'm not saying there isn't an answer -- the suggestion I gave
you and you rejected works -- I'm just saying it isn't as clean as
you'd probably like.

> Its not enough for it compile and run on my platform - it has to run
> on them all.

That's your choice.

> So one ends up with #if ... all over the place to vary the
> destination namespace depending on whether or not the compiler
> supports ADL - that's ugly.

Again, I'm amazed at what you're saying. The suggestion I gave you
involves no #ifs after the library defines the macro. Let me refresh
your memory.

The library #defines:

  #if BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
  # define BOOST_SERIALIZE(namespace) serialize
  #else
  # define BOOST_SERIALIZE(namespace) namespace::serialize
  #endif

Then the library declares its overloads of boost::serialize for the
standard containers before ever defining a template that calls
serialize. Then users write their serialization functions thus:

  namespace my { namespace ns
  {
    class my_class { ... };

    template <class A>
    void serialize(A&, my_class); // declaration (unused in non-ADL case)
  }}

  // definition
  template <class Archive>
  void BOOST_SERIALIZE(my::ns)(Archive& ar, my::ns::my_class& x)
  {
     ...
  }

>> or stop supporting those broken compilers.
>
> This the crux of the matter right here. From the prespective of
> two-phase lookup - they're all "broken" except CW which can't use
> the most interesting parts of the serilization library because its
> broken in other ways. I made one choice - you would make another.

Probably not. I am not suggesting that you stop supporting those
compilers. I certainly spend enough effort on the same problem in my
own work. I am suggesting that if you are going to keep supporting
them, you probably have to accept a little ugliness. IMO you should
be willing to tolerate it in order to have your broken compiler
support and have your documentation be correct.
>
>> 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 downloaded and installed comeau 4.3 sometime ago specifically in order to
> be able to test this. I still use it to compile all the library modules and
> tests as a double check. They all pass. Alas, they don't link due to some
> collision with the Boost Test library that cropped up after 1.32. Now I'm
> informed that we have the test compile time switches set so supress
> two-phase lookup (or parts of it) so I have no "conforming" compiler
> available. This is a hinderence to addressing problems such as
> this.

It's hard for me to feel very sympathetic. You have the online
compiler. You could find out what the switches are (--no-microsoft
and/or --no-microsoft-bugs) to turn on 2-phase lookup on your local
installation of comeau and do some experiments.

>> 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?!
>
> That's what I'll have to do. FWIW its not that don't believe you're
> correct about the way two-phase lookup works. But I"m curious to
> know why things have worked so well so far.

I don't think not knowing the reason should stop you from fixing a doc
problem.

> Is it because no one is using conforming compilers? Is it because
> common use cases don't trip on this? This is so far unexplained.
> The only way I'll discover this is by "doing my homework" which I
> haven't been able to get to.

That's not the homework I'm referring to.

> Maybe there is a real simple solution here.

That's what I've been trying to tell you. As I wrote,

  Not much more than a small tweak in emphasis would be required.

> The relevent passage in the
> documentation currently states:
> Namespaces for Free Function Overrides
> For maximum portability, include any free functions templates and
> definitions in the namespace boost::serialization. If portability is not a
> concern and the compiler being used supports ADL (Argument Dependent Lookup)
> the free functions and templates can be in any of the following namespaces:
> a.. boost::serialization
> b.. namespace of the archive class
> c.. namespace of the type being serialized
> So if I were to change it to the following would that be satisfactory?
> Namespaces for Free Function Overrides

> If your compiler supports two-phase lookup and ADL (Argument Dependent
> Lookup) the free functions and templates can be in any of the following
> namespaces:
>
> namespace of the archive class
> namespace of the type being serialized

Good, but you should be specific about which free functions and
templates you're talking about.

> If your compiler doesn't support two-phase lookup but it does support ADL
> free functions and templates can be in any of the following namespaces
> a.. boost::serialization
> b.. namespace of the archive class
> c.. namespace of the type being serialized
> If your compiler doesn't support ADL free functions and templates must be in
> the boost::serialization namespace.

well, at least that's almost correct. The associated namespaces of a
type can be more than the one its defined in, but that's OK. I think
it's more complicated than it needs to be; I'd say

      if your compiler supports argument dependent lookup, overload
      serialize in the namespace of the type being serialized;
      otherwise, overload serialize in boost::serialization

> Of course that begs the main question I set out to answer. How to
> make ones code work on all boost platforms

I showed you how in this very message. And that's a repetition of
something I showed you earlier.

> - but we can just leave that to the judgement of the library user.

That would still be a big improvement on the status quo.

-- 
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