Subject: Re: [boost] Design conventions; passing functors
From: Joachim Faulhaber (afojgo_at_[hidden])
Date: 2009-03-04 16:31:26


2008/12/21 David Abrahams <dave_at_[hidden]>:
>
> on Mon Dec 01 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
>
>> 2008/11/29 David Abrahams <dave_at_[hidden]>:
>>>
>>>>
>>> A good choice that is both forward-looking and compatible with legacy
>>> code is to implement and document move semantics support (or a
>>> swappability requirement), and pass by value unless there's no need for
>>> an eventual copy.
>>
>> looking at library code the 'eventually being copiedness' of
>> functors seems to be a pragmatical criterion for many library
>> authors on how to pass them (boost:: and even std::).
>> If functors are eventually passed to a std algorithm
>> inside the library code (or other copy intensive code), they are
>> exposed as call by value in the libraries interface. If not they
>> are often exposed as call by const reference parameters in the
>> libraries interface.
>
> Interesting.  I'm not sure I see the point, but interesting.
>
>>>
>>>> As written earlier in this thread, I would prefer to pass functors
>>>> const reference, and without additional wrappers (which is done by
>>>> other boost libraries too). I do not call std::algorithms internally.
>>>>
>>>> Please tell me, if there are strong arguments against that.
>>>
>>> Extra copies of rvalues can be expensive, and function objects are often
>>> rvalues.
>>>
>>
>> My own functor passing design head aces have disappeared by my
>> decision *not* to expose the Combine (aggregate) functors in the
>> interface of interval_map member functions at all.
>
> I'm sorry, I have no idea what I just read (and re-read) there. Could
> you rephrase?
>

Sorry for the late answer ... I obviously missed your post over Xmas :(

What I wanted to say was this:

Functors occur in my library (ITL) for the aggregation
of associated values on insertion in interval_maps
(aggregation on overlap):

And I thought about different ways to pass the
aggregating functor like:

template<class Domain, class Codomain, ...>
class interval_map{
 ...
 //(1) My current implementation is this:
 // Combinator is the aggregating functor template,
 // instantiated at compiletime
 template<template<class>class Combinator>
 interval_map& add(const value_type& value_pair)
 { /*Combinator template passed or instantiated somewhere*/ }
 ...
}

or

template<class Domain, class Codomain, ...>
class interval_map{
 //(3) Adaptable functor
 template<template<class>class Combinator>
 interval_map& add(const value_type& value_pair,
                   const Combinator<Codomain>& combine)
 { /*combine functor passed or called somewhere*/ }

All these variants have in common that the functor can
be chosen for the call of the function add.

interval_map<int,int> m;
m.add(interval_value_pair, combiner);

During the discussion I realized that the possibility of
passing the functor to every call of the add function is
*not* desirable. It provides too much flexibility that is
not needed and can lead to unnecessary errors.

To mix the aggregating functors for one interval_map m

m.add(interval_value_pair1, inplace_plus<int>());
m.add(interval_value_pair2, inplace_max<int>());

makes no sense.

So the combine functor should be constant for every
instantiation of an interval_map. (Or at least for every
object). Like the sort order is constant and can not
be changed for every call of insert.

typedef interval_map<int, int, partial_absorber, std::less, inplace_max>
   maximizer_interval_mapT;

In this case I do not have to pass the combine functor
with calls of the add member function.

maximizer_interval_mapT maximap;
...
maximap.add(make_pair(itv, x));
  // on overlap with itv compute maximum with x

see also maximize example:
http://herold-faulhaber.de/boost_itl/doc/libs/itl/doc/html/boost_itl/examples.html#boost_itl.examples.party

>> I am going to instantiate the Combine functor as template
>> parameter for the interval_map class templates only. Similar
>> to the Compare predicate in associative containers,
>> the Combine functor of interval maps is then invariant for any
>> given interval_map instance and can not change for a
>> constructed interval_map object.
>>
>> This way, I think, the interval_map's interface is more easy
>> to use correctly and harder to use incorrectly.
>
> I'm not sure what you're saying.  Are you planning to store an instance
> of the function object in the container?  That is the normal behavior of
> standard containers.
>
I thought about that but I do not like the idea to put more than the
compare order object into the container.

Sorry for the delay
Joachim