
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