Design conventions; passing functors

Hi, I am pondering about functor passing. Functors occur in my library (ITL) e.g. for the aggregation of associated values on insertion in interval_maps (aggregation on overlap): 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*/ } ... } //I like this definition, because it's abstract: // The functor has to be a matching template e.g. inplace_max interval_map<int,int> m; m.add<inplace_max>(some); //(1) template<class Domain, class Codomain, ...> class interval_map{ //(2) The more traditional definition template<class Combinator> interval_map& add(const value_type& value_pair) { /*Combinator type passed or called somewhere*/ } ... m.add<inplace_max<int> >(some); //(2) I prefer (1) but both definitions (1) and (2) are purely static denotations. To have adaptable functors or functors that can have states, like in ... m.add(some, max_under(42)); ... I am afraid I have to say sayonara to definition (1) and use this instead: 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*/ } Now, there seems to be a convention from the STL that functors shall be passed BY VALUE. template<template<class>class Combinator> interval_map& add(const value_type& value_pair, Combinator<Codomain> combine); This is stated in [Sutter & Alexandrecu 2005, C++ Coding Standards] item 89: "Function objects are modeled on function pointers. Like function pointers, the convention is to pass them by value. All of the standard algorithms pass objects by value, and your algorithms should too." I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors. So I browsed through some boost libraries and found that functors are passed by reference there (e.g. accumulators, fusion). Final question: What is the boost standard on passing functors, that I can adapt to? Cheers Joachim

AMDG Joachim Faulhaber wrote:
<snip>
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*/ }
Can't you use: template<class Combinator> interval_map& add(const value_type& value_pair, const Combinator& combine); ? In other words, does Combinator really have to be a template?
Now, there seems to be a convention from the STL that functors shall be passed BY VALUE.
<snip>
I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors.
The assumption is that function objects are not fat. If you have a large function object, it is always possible to create another function object that just stores a reference to the original function object.
So I browsed through some boost libraries and found that functors are passed by reference there (e.g. accumulators, fusion).
Final question: What is the boost standard on passing functors, that I can adapt to?
You can go either way. There is no official Boost policy about this. In Christ, Steven Watanabe

2008/11/6 Steven Watanabe <watanabesj@gmail.com>:
<snip>
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*/ }
Can't you use:
template<class Combinator> interval_map& add(const value_type& value_pair, const Combinator& combine); ? I could
In other words, does Combinator really have to be a template?
No it doesn't. But any Combinator has to combine Codomain values. I feel that this is expressed more precisely in definition (3).
Now, there seems to be a convention from the STL that functors shall be passed BY VALUE.
<snip>
I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors.
The assumption is that function objects are not fat. If you have a large function object, it is always possible to create another function object that just stores a reference to the original function object.
Unfortunately the assumption can not be that all programmers are always aware of the many c++ intricacies. There's a rule stated by Scott Meyers in one of his books: "Make your classes easy to use correctly and difficult to use incorrectly." In common, I would expect, that class objects are passed by reference. So in the struggle to meet the next deadline one could unknowingly code m.add(some, add_if_in(big_set)); where add_if_in contains the a big set which then is copied and again copied with every subsequent functor passing inside add. The perfect programmer won't do that, but this library code is IMO easy to use incorrectly. Joachim

AMDG Joachim Faulhaber wrote:
In other words, does Combinator really have to be a template?
No it doesn't. But any Combinator has to combine Codomain values. I feel that this is expressed more precisely in definition (3).
I disagree. If I am not writing generic code, struct myCombiner { ... operator()(...) const { ... } }; should be perfectly fine. Also, polymorphic function objects should be okay: struct myCombiner { template<class Codomain> ... operator()(...) const {... ] }; In Christ, Steven Watanabe

2008/11/6 Steven Watanabe <watanabesj@gmail.com>:
No it doesn't. But any Combinator has to combine Codomain values. I feel that this is expressed more precisely in definition (3).
I disagree. If I am not writing generic code,
struct myCombiner { ... operator()(...) const { ... } };
should be perfectly fine.
Also, polymorphic function objects should be okay:
struct myCombiner { template<class Codomain> ... operator()(...) const {... ] };
Interesting, thanx for the insight, Joachim

----- Original Message ----- From: "Joachim Faulhaber" <afojgo@googlemail.com> To: <boost@lists.boost.org> Sent: Thursday, November 06, 2008 6:28 PM Subject: Re: [boost] Design conventions; passing functors
2008/11/6 Steven Watanabe <watanabesj@gmail.com>:
I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors.
The assumption is that function objects are not fat. If you have a large function object, it is always possible to create another function object that just stores a reference to the original function object.
Is not this the role of boost::ref?
Unfortunately the assumption can not be that all programmers are always aware of the many c++ intricacies.
There's a rule stated by Scott Meyers in one of his books: "Make your classes easy to use correctly and difficult to use incorrectly."
If the parameter do a copy is is very difficult to use incorrectly, either you function is CopyConstructible and works, or if not it not compiles. If the functor is heavy to copy it is up to the functor designer to forbid copy construction. Optionaly the Functor can implement move semantics, but this is not absolutely needed. In any case to can wrap it with boost::ref. Am I missing something basic? Vicente

----- Original Message ----- From: "Steven Watanabe" <watanabesj@gmail.com> To: <boost@lists.boost.org> Sent: Saturday, November 15, 2008 11:44 PM Subject: Re: [boost] Design conventions; passing functors
AMDG
vicente.botet wrote:
it is always possible to create another function object that just stores a reference to the original function object.
Is not this the role of boost::ref?
std::ref provides a forwarding operator(). boost::ref does not.
Thanks Steven to pointing it out. Do you know why boost::ref does not? Do the author of boost.ref plan to provides the forwarding operator()? Could someone explain me how this works for the Boost.Thread library? >From the Boost.Thread documentation "Launching threads A new thread is launched by passing an object of a callable type that can be invoked with no parameters to the constructor. The object is then copied into internal storage, and invoked on the newly-created thread of execution. If the object must not (or cannot) be copied, then boost::ref can be used to pass in a reference to the function object. In this case, the user of Boost.Thread must ensure that the referred-to object outlives the newly-created thread of execution. " Take in account that Boost.Thread do some kind of move semantics emulation. Thanks in advance, Vicente

on Sat Nov 15 2008, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
AMDG
vicente.botet wrote:
it is always possible to create another function object that just stores a reference to the original function object.
Is not this the role of boost::ref?
std::ref provides a forwarding operator(). boost::ref does not.
But it probably should, eh? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2008/11/15 vicente.botet <vicente.botet@wanadoo.fr>:
----- Original Message ----- From: "Joachim Faulhaber" <afojgo@googlemail.com> To: <boost@lists.boost.org> Sent: Thursday, November 06, 2008 6:28 PM Subject: Re: [boost] Design conventions; passing functors
2008/11/6 Steven Watanabe <watanabesj@gmail.com>:
I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors.
The assumption is that function objects are not fat. If you have a large function object, it is always possible to create another function object that just stores a reference to the original function object.
Is not this the role of boost::ref?
Unfortunately the assumption can not be that all programmers are always aware of the many c++ intricacies.
There's a rule stated by Scott Meyers in one of his books: "Make your classes easy to use correctly and difficult to use incorrectly."
If the parameter do a copy is is very difficult to use incorrectly, either you function is CopyConstructible and works, or if not it not compiles. If the functor is heavy to copy it is up to the functor designer to forbid copy construction. Optionaly the Functor can implement move semantics, but this is not absolutely needed. In any case to can wrap it with boost::ref.
Thanx for the hint to boost::ref
Am I missing something basic?
I have found an article of Scott Meyers on his "most important general design guideline in my arsenal": "Make interfaces easy to use correctly and hard to use incorrectly" (Mietucahatuin ;-) http://www.aristeia.com/Papers/IEEE_Software_JulAug_2004_revised.htm I am still not convinced to pass my functors by value and I think it violates Mietucahatuin. (1) It makes additional assumptions: The client of the software has to be aware, that passing a fat functor causes expensive copies, not only at the point of passing the functor to a library function but also in internal calls. That means she has to have knowledge about implementation details of the library code. That is a violation of the principle of information hiding. (2) The client of the software has to remember complex knowledge in order to use the code correctly: If she wishes to use a rich state she has to remember to implement the functor using the pimpl idiom or move semantics. Yet Scott Meyers writes in the article above, and I think he's right: "But interfaces that rely on clients remembering to do something are easy to use incorrectly." (3) Passing functors by value stands in direct contradiction to the well established rule to pass class obects by (const)-reference. (4) It takes the burden of facilitating correct use away from the libraries author and puts it on the libraries client, which also violates Meyer's Mietucahatuin-rule. Sorry if I've got too apodictic. I am mainly interested in a good decision for my own library code. 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. Cheers Joachim

on Sun Nov 16 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
I have found an article of Scott Meyers on his "most important general design guideline in my arsenal":
"Make interfaces easy to use correctly and hard to use incorrectly" (Mietucahatuin ;-) http://www.aristeia.com/Papers/IEEE_Software_JulAug_2004_revised.htm
I am still not convinced to pass my functors by value and I think it violates Mietucahatuin.
(1) It makes additional assumptions: The client of the software has to be aware, that passing a fat functor causes expensive copies, not only at the point of passing the functor to a library function but also in internal calls. That means she has to have knowledge about implementation details of the library code. That is a violation of the principle of information hiding.
(2) The client of the software has to remember complex knowledge in order to use the code correctly: If she wishes to use a rich state she has to remember to implement the functor using the pimpl idiom or move semantics. Yet Scott Meyers writes in the article above, and I think he's right: "But interfaces that rely on clients remembering to do something are easy to use incorrectly."
Except that there's a well-known convention that function objects and iterators (and probably some other things) should be lightweight-copyable.
(3) Passing functors by value stands in direct contradiction to the well established rule to pass class obects by (const)-reference.
That rule is wrong when objects need to be copied anyway. Pass-by-value + swap (or move) into place is the best practice in that case, because it saves copies of rvalues on known real compilers and costs no extra copies of lvalues.
(4) It takes the burden of facilitating correct use away from the libraries author and puts it on the libraries client, which also violates Meyer's Mietucahatuin-rule.
Sorry if I've got too apodictic. I am mainly interested in a good decision for my own library code.
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.
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. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2008/11/29 David Abrahams <dave@boostpro.com>:
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.
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 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. Cheers Joachim

on Mon Dec 01 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
2008/11/29 David Abrahams <dave@boostpro.com>:
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?
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. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

on Mon Dec 01 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
2008/11/29 David Abrahams <dave@boostpro.com>:
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?
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. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2008/12/21 David Abrahams <dave@boostpro.com>:
on Mon Dec 01 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
2008/11/29 David Abrahams <dave@boostpro.com>:
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/example...
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

Joachim Faulhaber wrpte:
Now, there seems to be a convention from the STL that functors shall be passed BY VALUE.
[...] I do not really understand this convention and feel some resistance to follow it.
That convention only exists because of the forwarding problem. If you pass by const-reference, the object becomes const, the state must then be mutable and the operator() const. If you pass by reference, you cannot take temporaries. If you pass by value, you have no problem, except that you're working with a copy, and thus you should return it so that the state changes can be accessed. The copy may also be a useless cost. The forwarding problem being solved by rvalue references, you may use that if available (boost has a macro).
In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors. So I browsed through some boost libraries and found that functors are passed by reference there (e.g. accumulators, fusion).
lambda used that approach to forward arguments, you had to use make_const to wrap temporaries. I'm not sure how phoenix does it (it doesn't seem to be documented), but I assume it does the same.

2008/11/6 Mathias Gaunard <mathias.gaunard@ens-lyon.org>:
Joachim Faulhaber wrpte:
Now, there seems to be a convention from the STL that functors shall be passed BY VALUE.
[...] I do not really understand this convention and feel some resistance to follow it.
That convention only exists because of the forwarding problem.
If you pass by const-reference, the object becomes const, the state must then be mutable and the operator() const.
If you pass by reference, you cannot take temporaries.
If you pass by value, you have no problem, except that you're working with a copy, and thus you should return it so that the state changes can be accessed. The copy may also be a useless cost.
Thank you for the information! After having found all three kinds of functor passing in boost libraries and the information from Steve Watanabe that there is no official boost policy for that I tend to choose method (2): Passing by const-reference: * It is in line with the standard method of passing objects e.g. [Meyers 98, item 22]. * It supports the claim to design functors as pure functions, the result of which only depends on their arguments (and at most a constant state that was created on construction) [Meyers 01, item 39] [Sutter,Alexandescu 05, item 87]. So the disadvantages of const-reference passing (operator()(..) is const and the functor's state is const) appear to me as a guard against misuse. In addition unintentional copying of fat functors can not happen. So I am happy with that :) Cheers Joachim

on Thu Nov 06 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
Hi,
I am pondering about functor passing. Functors occur in my library (ITL) e.g. for the aggregation of associated values on insertion in interval_maps (aggregation on overlap):
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*/ } ... }
//I like this definition, because it's abstract: // The functor has to be a matching template e.g. inplace_max interval_map<int,int> m; m.add<inplace_max>(some); //(1)
template<class Domain, class Codomain, ...> class interval_map{ //(2) The more traditional definition template<class Combinator> interval_map& add(const value_type& value_pair) { /*Combinator type passed or called somewhere*/ } ...
m.add<inplace_max<int> >(some); //(2)
I prefer (1) but both definitions (1) and (2) are purely static denotations. To have adaptable functors or functors that can have states, like in ...
Try a unary MPL lambda expression here instead: m.add<inplace_max<_> >(some);
m.add(some, max_under(42));
... I am afraid I have to say sayonara to definition (1) and use this instead:
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*/ }
Presumably because you want to support stateful combine functions?
Now, there seems to be a convention from the STL that functors shall be passed BY VALUE.
Ja.
template<template<class>class Combinator> interval_map& add(const value_type& value_pair, Combinator<Codomain> combine);
This is stated in [Sutter & Alexandrecu 2005, C++ Coding Standards] item 89: "Function objects are modeled on function pointers. Like function pointers, the convention is to pass them by value. All of the standard algorithms pass objects by value, and your algorithms should too."
I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors.
Sounds like a job for move semantics.
So I browsed through some boost libraries and found that functors are passed by reference there (e.g. accumulators, fusion).
Look for "move.hpp" headers in various library subdirectories :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2008/11/11 David Abrahams <dave@boostpro.com>:
on Thu Nov 06 2008, "Joachim Faulhaber" <afojgo-AT-googlemail.com> wrote:
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*/ } ... }
//I like this definition, because it's abstract: // The functor has to be a matching template e.g. inplace_max interval_map<int,int> m; m.add<inplace_max>(some); //(1)
template<class Domain, class Codomain, ...> class interval_map{ //(2) The more traditional definition template<class Combinator> interval_map& add(const value_type& value_pair) { /*Combinator type passed or called somewhere*/ } ...
m.add<inplace_max<int> >(some); //(2)
I prefer (1) but both definitions (1) and (2) are purely static denotations. To have adaptable functors or functors that can have states, like in ...
Try a unary MPL lambda expression here instead:
m.add<inplace_max<_> >(some);
This is very smart. It took me a while to get it ;-) So I have alternative implementation (4) now: template<class Domain, class Codomain, ...> class interval_map{ //(4) Using mpl::apply template<class Combinator> interval_map& add(const value_type& value_pair) { ... mpl::apply<Combinator,CodomainT>::type()(x,y); ... } I wonder whether every template template parameter instantiation can be expressed by an equivalent mpl lambda expression in general! // Unary template template parameter expressed via mpl lambda template<class T>struct unary{ void speek(){ cout<<"unary"<<endl; } }; template<class T, template<class>class U> struct unator1{ void speek(){U<T> obj; obj.speek();} }; template<class T, class U> //equivalent to unator1 struct unator2{ void speek(){ mpl::apply<U,T>::type obj; obj.speek(); } }; // Binary template template parameter expressed via mpl lambda template<class T1, class T2>struct binary{ void speek(){ cout<<"binary"<<endl; } }; template<class T1, class T2, template<class,class>class U> struct binator1{ void speek(){U<T1,T2> obj; obj.speek();} }; template<class T1, class T2, class U> struct binator2{ //equivalent to binator1 void speek(){ mpl::apply2<U,T1,T2>::type obj; obj.speek(); } }; // Ternary ... and so on int main(){ unator1<int, unary> untor1; untor1.speek(); unator2<int, unary<_> > untor2; untor2.speek(); binator1<int, int, binary> bintor1; bintor1.speek(); binator2<int, int, binary<_1,_2> > bintor2; bintor2.speek(); //... }
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*/ }
Presumably because you want to support stateful combine functions?
Yes, especially to allow clients of the library to simulate partial function evaluation, that is known from functional languages. Like in partial function evaluation I think it is sufficient to set a state that is then constant, like in: SomeSet my_set; get(my_set); // add_if_in: SomeSet -> (CodomainT x CodomainT) -> CodomainT // first argument partially evaluated m.add(some, add_if_in(my_set));
Now, there seems to be a convention from the STL that functors shall be passed BY VALUE.
Ja.
template<template<class>class Combinator> interval_map& add(const value_type& value_pair, Combinator<Codomain> combine);
This is stated in [Sutter & Alexandrecu 2005, C++ Coding Standards] item 89: "Function objects are modeled on function pointers. Like function pointers, the convention is to pass them by value. All of the standard algorithms pass objects by value, and your algorithms should too."
I do not really understand this convention and feel some resistance to follow it. In addition the call by value implementation can (and will) lead to heavy inefficiency by unaware usage of fat functors.
Sounds like a job for move semantics.
But the job has to be done by the clients of my library code? The functors they write and pass to itl::add have to implement move semantics. Did I get that right? In this case, the burdon of knowing all the c++ intricacies is on the libraries clients not on it's author. I am afraid that is a problem. Thanx for all the answers, Joachim

I used to like template template, but I've decided it's fairly useless. The problem is: template<template<class A>a> does not match a class with a default parameter. So, for example, suppose you have a class where you specify a container class as a parameter. For example: template<typename T, template<class Vec> vec > class my_class ... This won't match my_class<int, std::vector> Because std::vector has a defaulted alloc parameter. This ruins the design (my_class shouldn't need to know anything about default parameters of the container. How could it know what other vector-like classes there could be and what default parameters they might have?)

2008/11/29 Neal Becker <ndbecker2@gmail.com>:
I used to like template template, but I've decided it's fairly useless. The problem is:
template<template<class A>a> does not match a class with a default parameter. So, for example, suppose you have a class where you specify a container class as a parameter. For example:
template<typename T, template<class Vec> vec > class my_class ...
This won't match my_class<int, std::vector>
Because std::vector has a defaulted alloc parameter. This ruins the design (my_class shouldn't need to know anything about default parameters of the container. How could it know what other vector-like classes there could be and what default parameters they might have?)
That's an interesting point, you are making, which I had not realized clearly before. Using template type parameters, there is a much bigger set of possible instance types, vector implementations in your example. With template template parameters the match must be exact: template<class T, // matches only binary vector templates template<class,class>class Vec, class A = std::allocator<T> > class my_class { public: typedef Vec<T,A> vec_type; my_class():_vec(1,static_cast<T>(42)) { std::cout<<"Aswer: "<<_vec[0]<<endl; } private: vec_type _vec; }; void template_default_problem() { my_class<double, std::vector> myInst; } Yet, I did not deliberately become a template template afficionado. They helped me to solve a problem in my library design. I intended to design interval container template parameters as closely as possible to their std::container cousins. Here is a simplified version of interval_set: template<class T, class C = std::less<T>, class A = std::allocator<T> > class interval_set { public: typedef std::set<interval<T>, exclusive_less<interval<T> > A::allocator_template<interval<T> >//error > impl_type; // ^^^^ No language support typedef std::set<T,C,A> atomized_type; // Return the interval set as a set of elements void atomize(atomized_type& atomized); ... private: impl_type impl_set; }; In order to implement things like the atomize function that returns the interval set as a set of elements I needed to instantiate the allocator template for different instance types: interval- and element-types. This is made possible by using template template: template< class T, class C = std::less<T>, template<class>class A = std::allocator
class my_interval_set { public: typedef std::set<interval<T>, exclusive_less<interval<T> > A<interval<T> > //ok now > impl_type; typedef std::set<T,C, A<T> //Same allocator, //different instances > atomized_type; // Return the interval set as a set of elements void atomize(atomized_type& atomized); ... private: impl_type impl_set; }; So far template template params has helped me a lot and, yes, I still like them ;-) But I also realize a growing list of downsides, to which you have added one more item. Cheers Joachim
participants (6)
-
David Abrahams
-
Joachim Faulhaber
-
Mathias Gaunard
-
Neal Becker
-
Steven Watanabe
-
vicente.botet