Re: glas Digest, Vol 25, Issue 1

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: glas Digest, Vol 25, Issue 1

Robert P. Goddard
Karl,

I'm generally supportive of your idea of using free functions and traits
in place of member functions and member typedefs. However, I caution
against doing away with the latter. The Standard Library provides a good
example (as it often does): The default definition for
std::iterator_traits is, in part,

template <class Iterator>
struct iterator_traits {
    typedef typename Iterator::value_type value_type;
};

That way the developer of an iterator has a choice: Provide a
specialization of iterator_traits, or provide a nested typedef and let
the default iterator_traits pick it up. The latter is often easier, in
part because nested typedefs can come from a base class. The former is
sometimes necessary, such as non-class types or classes developed by
someone else.

The same principle applies to free functions versus member functions: A
reasonable default definition of size() could be

template <class V>
VectorExpression<V>::size_type size( const V& size )
{
    return V.size();
}

Again, the developer of a new VectorExpression class has a choice:
Overload the free size() function or provide a member function. The
latter is often preferred, e.g. when it's a virtual function, and the
former is sometimes necessary, e.g. when it's a non-class or you don't
own it.

In both cases, the free version is the preferred interface for users.
The member version is provided primarily for developers, who can be the
same users wearing different hats.

--
Robert P. Goddard, Senior Physicist
Applied Physics Laboratory, University of Washington
1013 NE 40th St., Seattle, WA 98105-6698
206-543-1267; FAX 206-543-6785; cell 206-718-4417
[hidden email]


[hidden email] wrote:

> Send glas mailing list submissions to
> [hidden email]
>
> To subscribe or unsubscribe via the World Wide Web, visit
> http://lists.boost.org/mailman/listinfo.cgi/glas
> or, via email, send a message with subject or body 'help' to
> [hidden email]
>
> You can reach the person managing the list at
> [hidden email]
>
> When replying, please edit your Subject line so it is more specific
> than "Re: Contents of glas digest..."
>
>
> The glas archives may be found at: http://lists.boost.org/MailArchives/glas/
>
> Today's Topics:
>
>    1. Re: proposals (Karl Meerbergen)
>
>
> ----------------------------------------------------------------------
>
> Message: 1
> Date: Wed, 04 Apr 2007 20:10:51 +0200
> From: Karl Meerbergen <[hidden email]>
> Subject: Re: [glas] proposals
> To: Generic Linear Algorithm Software <[hidden email]>
> Message-ID: <[hidden email]>
> Content-Type: text/plain; charset="iso-8859-1"
>
> Hi,
>
> I am preparing a proposal for an extendable backend system and concepts
> for vectors (and scalars to some extent).
>
> One of the design decisions I would like to make is to use free
> functions as much as possible.  I think that free functions would make
> life alot easier and extensions more straightforward. I will explain
> later in detail.
>
> Does someone have a strong objection against
> size(v) instead of v.size(), stride(v) instead of v.stride() ?
>
> Similarly, one could argue that meta functions are preferred to members,
> e.g.
> size_type<V>::type rather than V::size_type.
>
> However, concept C++ suggests to organize this in a slightly different
> way, e.g.
> VectorExpression<V>::size_type
> where VectorExpression is a concept.
> In pure C++, VectorExpression would be a traits blob.
> Therefore, I prefere the latter notation to meta functions with a single
> type member.
>
> Best,
>
> Karl
> -------------- next part --------------
> A non-text attachment was scrubbed...
> Name: Karl.Meerbergen.vcf
> Type: text/x-vcard
> Size: 339 bytes
> Desc: not available
> Url : http://lists.boost.org/MailArchives/glas/attachments/20070404/5b83a4eb/attachment.vcf 
>
> ------------------------------
>
> _______________________________________________
> glas mailing list
> [hidden email]
> http://lists.boost.org/mailman/listinfo.cgi/glas
>
> End of glas Digest, Vol 25, Issue 1
> ***********************************
>  

_______________________________________________
glas mailing list
[hidden email]
http://lists.boost.org/mailman/listinfo.cgi/glas
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: glas Digest, Vol 25, Issue 1

Karl Meerbergen-2
Hi Robert,

Thanks for the suggestion. I already had this in mind for the traits
classes, but I could consider this too for the free functions indeed.

However, 'default' function implementations are not (yet) allowed in
Concept C++, so, therefore I would only do this for typedefs (i.e. traits).

Karl


Robert P. Goddard wrote:

>Karl,
>
>I'm generally supportive of your idea of using free functions and traits
>in place of member functions and member typedefs. However, I caution
>against doing away with the latter. The Standard Library provides a good
>example (as it often does): The default definition for
>std::iterator_traits is, in part,
>
>template <class Iterator>
>struct iterator_traits {
>    typedef typename Iterator::value_type value_type;
>};
>
>That way the developer of an iterator has a choice: Provide a
>specialization of iterator_traits, or provide a nested typedef and let
>the default iterator_traits pick it up. The latter is often easier, in
>part because nested typedefs can come from a base class. The former is
>sometimes necessary, such as non-class types or classes developed by
>someone else.
>
>The same principle applies to free functions versus member functions: A
>reasonable default definition of size() could be
>
>template <class V>
>VectorExpression<V>::size_type size( const V& size )
>{
>    return V.size();
>}
>
>Again, the developer of a new VectorExpression class has a choice:
>Overload the free size() function or provide a member function. The
>latter is often preferred, e.g. when it's a virtual function, and the
>former is sometimes necessary, e.g. when it's a non-class or you don't
>own it.
>
>In both cases, the free version is the preferred interface for users.
>The member version is provided primarily for developers, who can be the
>same users wearing different hats.
>
>  
>

_______________________________________________
glas mailing list
[hidden email]
http://lists.boost.org/mailman/listinfo.cgi/glas

Karl.Meerbergen.vcf (352 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: glas Digest, Vol 25, Issue 1

Bert Rodiers
In reply to this post by Robert P. Goddard
Hello Robert, Karl,

On 05/04/07, Robert P. Goddard <[hidden email]> wrote:
> Karl,
>
> I'm generally supportive of your idea of using free functions and traits
> in place of member functions and member typedefs. However, I caution
> against doing away with the latter. The Standard Library provides a good
> example (as it often does):

<snip>

It is interesting that you refer to the STL for this. I was just
reading the document "Notes on Programming", by Alexander Stepanov (
http://www.stepanovpapers.com/notes.pdf ). Stepanov is the designer
and original implementer of the Standard Template Library, and on page
21 he writes

<quote>
While we could make a member function to return length, it is better
to make it a
global friend function. If we do that, we will be able eventually to
define the same
function to work on built-in arrays and achieve greater uniformity of
design. I made
size into a member function in STL in an attempt to please the
standard committee. I
knew that begin, end and size should be global functions but was not
willing to risk
another fight with the committee. In general, there were many
compromises of which I
am ashamed. It would have been harder to succeed without making them,
but I still get a
metallic taste in my mouth when I encounter all the things that I did
wrong while
knowing full how to do them right. Success, after all, is much
overrated. I will be
pointing to the incorrect designs in STL here and there: some were
done because of
political considerations, but many were mistakes caused by my
inability to discern
general principles.)
</quote>

Given the feelings of the original STL-designer on this matter it
seems better to me not to take it as an example (except when you want
to achieve the same look & feel as STL).

David Abrahams and Aleksey Gurtovoy make a similar point in their book
"C++ Template Metaprogramming: Concepts, Tools, and Techniques from
Boost and Beyond" about the usage of traits in the STL:

<quote>
The traits templates in the standard library all follow the "multiple
return values" model. We refer to this kind of traits template as a
"blob," because it's as though a handful of separate and loosely
related metafunctions were mashed together into a single unit. We will
avoid this idiom at all costs, because it creates major problems.

First of all, there's an efficiency issue: The first time we reach
inside the iterator_traits for its ::value_type, the template will be
instantiated. That means a lot of things to the compiler, but to us
the important thing is that at that point the compiler has to work out
the meaning of every declaration in the template body that happens to
depend on a template parameter. In the case of iterator_traits, that
means computing not only the value_type, but the four other associated
types as welleven if we're not going to use them. The cost of these
extra type computations can really add up as a program grows, slowing
down the compilation cycle. Remember that we said type computations
would get much more interesting? "More interesting" also means more
work for your compiler, and more time for you to drum your fingers on
the desk waiting to see your program work.

Second, and more importantly, "the blob" interferes with our ability
to write metafunctions that take other metafunctions as arguments. To
wrap your mind around that, consider a trivial runtime function that
accepts two function arguments:

   template <class X, class UnaryOp1, class UnaryOp2>
   X apply_fg(X x, UnaryOp1 f, UnaryOp2 g)
   {
       return f(g(x));
   }



That's not the only way we could design apply_fg, though. Suppose we
collapsed f and g into a single argument called blob, as follows:

   template <class X, class Blob>
   X apply_fg(X x, Blob blob)
   {
       return blob.f(blob.g(x));
   }



The protocol used to call f and g here is analogous to the way you
access a "traits blob": to get a result of the "function," you reach
in and access one of its members. The problem is that there's no
single way to get at the result of invoking one of these blobs. Every
function like apply_fg will use its own set of member function names,
and in order to pass f or g on to another such function we might need
to repackage it in a wrapper with new names.

"The blob" is an anti-pattern (an idiom to be avoided), because it
decreases a program's overall interoperability, or the ability of its
components to work smoothly together. The original choice to write
apply_fg so that it accepts function arguments is a good one, because
it increases interoperability.

When the callable arguments to apply_fg use a single protocol, we can
easily exchange them:

   #include <functional>
   float log2(float);

   int a = apply_fg(5.Of,    std::negate<float>(), log2);
   int b = apply_fg(3.14f,   log2, std::negate<float>());



The property that allows different argument types to be used
interchangeably is called polymorphism; literally, "the ability to
take multiple forms."



To achieve polymorphism among metafunctions, we'll need a single way
to invoke them. The convention used by the Boost libraries is as
follows:

   metafunction-name<type-arguments...>::type



>From now on, when we use the term metafunction, we'll be referring to
templates that can be "invoked" with this syntax.
</quote>

Regards,
Bert
_______________________________________________
glas mailing list
[hidden email]
http://lists.boost.org/mailman/listinfo.cgi/glas
Loading...