Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2005-09-22 07:44:40


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

> David Abrahams wrote:
>
>>> The point is that if one writes a program in
>>> accordance with the requirements stated here, the program will be a
>>> legal program and will compile.
>>
>> But that's not enough. I'm not interested in making an archive that
>> just compiles; I want it to have some sensible semantics. What
>> requirements do I have to fulfill in order to make the archive
>> actually *work*?
>>
>> If you look at the iterator requirements, for example, you'll see that
>> there are semantic requirements in place, not just syntactic ones.
>> These requirements are needed so that the algorithms can say something
>> about what they do. The sort() algorithm wouldn't be able to make any
>> claims about its result if writing into a random access iterator were
>> allowed to be a no-op.
>
> I can see that, but then one has an ostream_iterator which is a forward
> iterator.

No, it's an output iterator.

> It does something quite different from other forward iterators
> such as one associated with a std::list.

No surprise, since it's not a forward iterator. It doesn't need to
meet the semantic guarantees of forward iterator. It still meets the
semantic guarantees of output iterator.

> I'm sure I could dig up other examples. I really did think about
> what one should say about the sematics. And when I considered
> possibilities like an archive used for data logging, it seemed that
> there was less and less I could say about the semantics without
> saying something incorrect. So I moved to saying as little as
> possible.

As I've said, that's not very useful.

>>> Of course, I would expect all archive implementations to call
>>> "serialize" but maybe someone comes up with an archive
>>> implementation that just checks syntax or something like that.
>>> Someone might want to make an archive which only stored/loaded
>>> primitive types. Or someone might make an archive that was
>>> implemented solely in terms of save/load_binary. These would never
>>> call serialize.
>>
>> Do not build in generality that you're not even sure is needed,
>> especially if it costs you your ability to write concepts that tell
>> people how to get sensible results. You can always introduce
>> less-refined concepts that allow these kinds of things and provide
>> weaker semantic guarantees.
>
> My thought was not to build in any requirement that wasn't necessary.

Those requirements _are_ necessary if you want loading and saving to
do anything reasonable.

>>>>> Common Type Definitions in all Archives
>>>>
>>>> This should be a section for the "Archive" concept.
>
> That was my intention. I didn't make a subtitle "Archive Concept" because
> I already had the chapter titled "Archive Concept"

Call the chapter "Archive Concepts," then.

>>> OK what I meant to say is
>>>
>>> A boolean MPL Integral Constant for which the following expression is
>>> true
>>>
>>> boost::mpl::is_equal< A::is_loading, boost::mpl::bool_<true>
>>>>
>>>
>>> <snip>
>>>
>>> How would you suggest I phrase this?
>>
>> I believe that requirement is too strong, and it would be sufficient
>> to say
>>
>> An MPL Integral Constant with a nonzero ::value member
>>
>
> So if somewhere in the implementation I use:
>
> boost::mpl::is_equal< A::is_loading, boost::mpl::bool_<true>

I don't know why you would ever do that when you could just use

  A::is_loading

directly, and there is no mpl::is_equal. I assume you mean equal_to.

> but the archive has A::is_loading defined as boost::mpl::int_<4> it
> will still work? If you say its true, I don't doubt it. But I
> would have hoped for a compile time error here.

In that case http://www.boost.org/libs/mpl/doc/refmanual/equal-to.html
are pretty clear on semantics; you can look this up as easily as I
can.

If you really want to use that test you have to say

   An MPL Integral Constant with a ::value member equal to true

>> register_type is not a member function but a member function
>> template. A template is not a function.
>>
>>> I could change the heading to "Common Operations for all Archives" or
>>> .. your suggestion here.
>>
>> The heading should be "Archive Concept Requirements" with a note that
>> each entry describes an expression that must be valid.
>
> How about just "Requirements" as its already part of a section titled
> "Saving Archive (Concept)"?

Well, as you have pointed out, register_type is required for loading
archives too.

>> Also, I just noticed "along with other information." If I'm writing
>> an archive, how am I supposed to fulfill that requirement? Can I
>> write any arbitrary "other information" I want? Suppose I write a
>> zero-valued char? Suppose I don't write _any_ "other information?"
>
> This "other information" is an implementation detail of the particular
> archive class.

Then it's not a requirement, right?

> The user of any archive implementing the concept doesn't have to
> know what other information is in the archive.

Irrelevant.

> I do believe I'm begining to see what the origin of this problem is.
> I'll address it below after a few details are out of the way.
>>
>>>> You should leave out "template." This is a little tricky, but that
>>>> expression will never be valid in a context where A is not a
>>>> dependent type. I realize you do need it when A is a dependent
>>>> type, but the convention is to use the simpler notation.
>>>
>>> Hmm - all Archives included with the library are templates.
>>
>> Irrelevant.

And incorrect AFAICT.

http://www.boost.org/libs/serialization/doc/archives.html#archive_models

shows a bunch of non-template archive models, doesn't it?
Oh, no it doesn't, those are just freestanding constructor signatures,
which would be invalid if written that way. Ugh.

Okay, so...

>>> So, removing this will be including a "valid expression" which will
>>> fail to compile on all conforming compilers.
>>
>> No.
>>
>> some_archive_template<Whatever> ar;
>> ar.template register_type<T>();
>>
>> is ill-formed.
>>
>> some_archive_template<Whatever> ar;
>> ar.register_type<T>();
>>
>> is well-formed if T is a type and Whatever is a valid template
>> argument to some_archive_template.
>
> I don't see this. I am looking at Appendix C.13.6 of The C++ Programming
> Language by Stroustrup page 858. It seems that he has our exact
> example there:
>
> <begin quote>
> class Memory {
> public:
> template<class T> T* get_new();
> ...
> };
>
> template<class Allocator> void f(Allocator & m){
> int * p1 = m.get_new<int>(); // syntax error: int after less-than
> operator
> int * p2 = m.template get_new<int>(); // explicit qualification
> ...
> }

That's a context in which Allocator is a dependent type. Let me be
perfectly clear. Try compiling this:

       struct foo {};
       void f()
       {
          boost::serialization::text_oarchive<std::ofstream> ar;
          ar.template register_type<foo>();
       }

On a conforming compiler, it will fail. Now remove the template
keyword and try again.

>>>> I find this rather alarming. Haven't I made it abundantly clear
>>>> that the concept documentation has to give at least a minimal and
>>>> complete documentation of what's required of an Archive?
>>>
>>> I don't believe anything so far conflicts with that.
>>
>> Maybe I've been too hasty, but it seemed to me, from the lack of any
>> requirement that the archive do something that interacts with the
>> Serializable concept (and produce sensible semantics) and your
>> reference to a separate section describing how to implement an
>> archive, that you had been trying to make the "Archive Concept"
>> section a "user-only" document, and had put the real requirements
>> elsewhere.
>
> correct. But I didn't think of them as "real requirements", I
> thought of them as "implementation details" from the perspective of
> the user.

Are they requirements or aren't they? It would be conceptually purer
if a model of the Archive concept wasn't required to be derived from a
library-provided class template, but I could live with that derivation
requirement if the other requirements you specified were complete and
meaningful.

>> An algebra's semantic rules describe invariants. The meanings of
>> statements in an algebra is constrained their invariant
>> transformations.
>>
>> a = b ==> b = a
>>
>> is semantic.
>
> As much as I'd like to respond to that it would lead us too far astray

Good; I don't want to talk about algebras anyway.

>> It's not formality that's the problem: it's vagueness,
>> inconsistency, and lack of a practical document that describes what
>> an archive implementor needs to do.
>
> Maybe - But I haven't yet heard from anyone who wants to do this
> but failed because of this.

Matthias did. He only succeeded after getting information from you
that wasn't present in the docs.

> I think what I should have done and what we should do now is:
>
> a) change the title of the section "Archive Concept" back to "Archive User
> Interface"
> or "Archive Interface". and restore the original content which is based
> of function prototypes.
> b) change the title of the section titled "Serializable Concept" back to
> "Serializable types".
> c) The question of formal documentation for the serialization library
> can be considered at leisure.

Grrr!

You're very close to having reasonable concept documentation now; I
don't know why you'd throw that out.

As I've said, "formal" documentation isn't the issue; the question is
whether the docs can be practically used by someone who wants to build
a new archive type.

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