14,934,216 members
Articles / Programming Languages / C++
Article
Posted 10 Jun 2005

116.2K views
105 bookmarked

# Yet Another Generalized Functors Implementation in C++

Rate me:
An article on generalized functors implementation in C++. Generalized functor requirements, existing implementation problems and disadvantages are considered. Several new ideas and problem solutions together with the compete implementation are suggested.

## Introduction

Generalized functors are important and powerful design artifacts. An excellent introduction on generalized functors idiom, its application and one of the best implementations can be found in [1]. I'll try to retell the primary ideas, concepts and implementation details below with a strong emphasis on the latter as expected from the article title.

So let's discover the generalized functors' goals first. Suppose we have the following C++:

```void f2(int, int) {...}
struct A {
void f2(int, int) {...}
};
struct B {
void f2(int, int) {...}
void f2const(int, int) const {...}
};
struct F {
void operator()(int, int) {...}
};

void (*pf2)(int, int) = &f2;
void (A::*paf2)(int, int) = &A::f2;
void (B::*pbf2)(int, int) = &B::f2;
F f;```

Collective notion for `pf2` (pointer to `static` function), `paf2` and `pbf2` (pointers to member functions), `f` (an instance of a class containing corresponding `operator()`) is C++ callable entity that means an entity to which functor-call `operator ()` can be applied:

```pf2(1, 2);
A a;
B* pb = new B;
(a.*paf2)(1, 2);
(pb->*pbf2)(1, 2);
f(1, 2);```

What if we want to treat all of them in some generic way, e.g. push them in some "container" and make all the above calls at once via a single method call of such a "container"? The first way to implement this is to develop a special "container" class. Another one is to design a universal adapter class capable of storing and making calls to all the callable entities above. Then these adapter class instances can be stored in the std containers as usual. The latter way is more generic because this adapter class can be used in other applications. It is the class that is called generalized functor. The following pseudocode introduces this:

```typedef Functor<
...
template arguments that transfer in some way
the return type and types of all parameters
of operator() - void and int, int for this example
...
> Functor2;                // (0)
Functor2 fun1(&f2);        // (1)
Functor2 fun2(&a, &A::f2); // (2)
Functor2 fun3(pb, &B::f2); // (2)
Functor2 fun4(f);          // (3)
fun1(1, 2);                // (4)
fun2(1, 2);                // (4)
fun3(1, 2);                // (4)
fun4(1, 2);                // (4)
// fun1(1);                // (5)
// fun2(1, 2, 3);          // (5)
// fun3(1, true);          // (5)
// bool r = fun4(1, 2);    // (5)
Functor2 fun;              // (6)
fun = fun1;                // (7)
fun = fun2;                // (7)
fun = fun3;                // (7)
fun = fun4;                // (7)
// -------------------------------------------
class FunSet
{
typedef std::vector<Functor2> FV;
FV fv_;
public:
FunSet& operator+=(Functor2 const& f)
{
fv_.push_back(f);             // (8)
return *this;
}
void operator()(int i1, int i2) const
{
FV::const_iterator end(fv_.end());
for(FV::const_iterator i = fv_.begin(); i != end; ++i)
(*i)(i1, i2);
}
};
FunSet fs;
fs += fun1;
fs += fun2;
fs += fun3;
fs += fun4;
fs(1, 2);```

The above example is simple but has several important issues. First, it shows an application of generalized functors and the power together with the convenience that is achieved. Particularly, the fact that all fun, `fun1`-`fun4` are of the same type (`Functor2`) gives a simple way to solve the above problem of "container" for different callable entities. Second, this example implicitly contains the requirements for a possible generalized functor implementation. We'll frequently refer to them later so let's emphasize:

 R1. Lines 1-3 imply a universal support for all kinds of callable entities and demand corresponding ctors. R2. Lines 4 and 5 imply the function call of the underlying callable entity instance to be type safe. This means that if the lines (5) are uncommented, they should not be compilable since they try to do a call either with inappropriate number or types of arguments or with inappropriate return type. R3. Lines 6-8 imply full support of value semantics, i.e. the need for proper default ctor, operator=, copy ctor and dtor. This is important and very convenient, taking into account C++ forbids assignment and conversions for raw callable entities of different kinds.

There are additional issues touched at line 0. They concern some other important implementation details:

 D1. The above listing shows an example of functor for callable entities with the function-call `operator ()` taking two `int`s as parameters and returning `void`. Other parameters and return types should be supported of course that leads to a question about the way of parameterization of a generalized functor template. D2. But what about the different counts of the arguments of the underlying function-call `operator ()`? Of course, this should be supported, the problem is how to implement this as clear as possible. This is not easy in C++ as it will be shown later.

## Known implementations

The two known implementations of the generalized functors are provided by Loki [2] and boost [3] libraries (the latter is proposed for C++ standard [4]). Providing full generalized functor semantics and functionality, they are completely different in internal implementation, from the concepts to the resulting complexity. Of course, there are other implementations as well. Going through the mentioned R1, R2, R3, D1 and D2 issues, we'll build our own one.

Let's consider D1 issues now. There are two main ways to parameterize the generalized functor template. The first uses function types, and the second uses a pair of return type and typelist containing the types of all the arguments of the corresponding function-call `operator ()`:

```template <class F> class Functor;              (a)
//and
template <class R, class TList> class Functor; (b)```

to use them as:

```typedef Functor<void (*)(int, int)> Functor2;            (a)
//and
typedef Functor<void, TYPELIST_2(int, int)> Functor2;    (b)
typedef Functor<void, CreateTL<int, int>::Type> Functor2;```

Note: In case (b), `TYPELIST` and `CreateTL` are a macro [1] and a meta-template respectively, used to generate typelists.

Case (a) (used in boost) looks more elegant, but has several drawbacks. Functions with the same signature can have different modifiers. Cv-qualifiers (`const` and `volatile`) are the first important examples. They can be applied only to member functions, thus:

`typedef Functor<void (*)(int, int) const> Functor2;`

declaration will be rejected by the C++ compiler because the corresponding function type is invalid. So the question arises - how to create a generalized functor instance for `B::f2const` function in this case? It is possible of course, but at the cost of unnecessary code complication spoiling the initial elegance. Other examples are `__cdecl`, `__stdcall`, `__fastcall` (both for non-member and member functions) and implicit `__thiscall` (for member functions) modifiers. Yes, they are all non-standard extensions provided by compiler vendors, but some of the vendors are too important to ignore. The problem is that the function types with different modifiers are of different types, the same can be said about the functors instantiated with them. Summing up, the function type is a too low-level entity to use for parameterization of such a high level entity as generalized functors.

Cases (b) are free from the above disadvantages though they look a bit less elegant. But we can fight for elegance, providing functor creation functions for example. With their help we can bring the client code to the following nice syntax:

```FunSet fs;
fs += MakeFunctor(f2);
fs += MakeFunctor(A::f2, &a);
fs += MakeFunctor(B::f2, &b);```

To implement `MakeFunctor` functions we need mapping between the function type and its return type and the typelist containing types of all its arguments, which can be defined using a common traits idiom:

```template <typename F> struct FunTraits;
//... partial specializations for different numbers
//    of arguments of the function types ...
template <typename R, typename P1, P2>
struct FunTraits<R (*)(P1, P2)>
{
typedef NullType ObjType;
typedef R ResultType;
typedef P1 Parm1;
typedef P2 Parm2;
typedef TYPELIST_2(P1, P2) TypeListType;
};
//... specializations for types with __cdecl,
//    __stdcall etc. modifies can be here ...
template <class O, typename R, P1, P2>
struct FunTraits<R (O::*)(P1, P2)>
{
typedef O ObjType;
typedef R ResultType;
typedef P1 Parm1;
typedef P2 Parm2;
typedef TYPELIST_2(P1, P2) TypeListType;
};
//... specializations for all combinations of
//    cv-qualified types should be here ...```

The functor creation helper functions are very simple to implement now:

```template <typename F> inline
Functor<typename FunTraits<F>::ResultType,
typename FunTraits<F>::TypeListType> MakeFunctor(F fun)
{
return Functor<typename FunTraits<F>::ResultType,
typename FunTraits<F>::TypeListType>(fun);
}
template <typename MF, class P> inline
Functor<typename FunTraits<MF>::ResultType,
typename FunTraits<MF>::TypeListType>
MakeFunctor(MF memfun, P const& pobj)
{
return Functor<typename FunTraits<MF>::ResultType,
typename FunTraits<MF>::TypeListType>(pobj, memfun);
}```

Let's move to requirement R1 considerations. You can readily observe that a generalized functor class template is parameterized by the types completely unrelated to the types of the objects that are passed to its ctors. This means that those ctors should be defined as member templates:

```template <class R, class TList>
class Functor
{
public:
// ctor for non-member functions and arbitrary functors
template <typename F> Functor(F const& fun) { ... }
// ctor for member functions
template <typename P, typename MF> Functor(P const& pobj,
MF memfn)
{ ... }
...
};```

Only the ctors know the callable entity type and it gets lost after the ctors exit. But some operations still require the callable entity's type knowledge, e.g. functor copying, assignment and destruction operations. The `operator()` should also forward the call to the underlying callable entity instance according to its actual type. So template ctors should store the callable entity type information in some "universal form", which will allow treating them later in some polymorphic way. The most straightforward solution is as follows:

```template <class R, class TList>
class Functor
{
struct FunImplBase
{
virtual R call_(...params evaluated from TList...) = 0;
...
};
template <typename F>
class FunctorImpl : public FunImplBase
{
F fun_;
public:
FunctorImpl(F const& fun) : fun_(fun) {}
virtual R call_(...params evaluated from TList...)
{ ... use params to make a call to fun_ ... }
...
};
template <typename P, typename MF>
class MemberFnImpl : public FunImplBase
{
P pobj_;
MF memfun_;
public:
MemberFnImpl(P const& pobj, MF memfun) : pobj_(pobj),
memfun_(fun) {}
virtual R call_(...params evaluated from TList...)
{ ... use params to make a call to memfun_ of pobj_ ... }
...
};
FunImplBase *pimpl_;
public:
// ctor for non-member functions and arbitrary functors
template <typename F> Functor(F const& fun)
{
pimpl_ = new FunctorImpl<F>(fun);
}
// ctor for member functions
template <typename P, typename MF> Functor(P const& pobj,
MF memfn)
{
pimpl_ = new MemberFnImpl<P, MF>(pobj, memfn);
}
...
R operator(...params evaluated from TList...)
{
return pimpl_->call_(... params ...);
}
...
};```

In the above example, an abstract base class `FunImplBase` defines all the operations depending on the different callable entity types. `FunctorImpl` and `MemberFnImpl` are its concrete successors implementing those operations for non-member functions together with arbitrary functors and member functions respectively. Generalized functor template itself incorporates a pointer to `FunImplBase`, initializes it in the corresponding ctors and uses it where it is needed. It should be noted that the above example conceptually resembles Loki's functor template.

Let's address R2 and D2 issues now. Using typelists you can pack an arbitrary number of different types into one type and this is great for many generic algorithms. But this very arbitrary number of types should be fixed in the typelists design (and usually is chosen high enough to fit all uses). A similar problem - C++ has no facility to manipulate function parameter's count and so you can't treat them in a generic way. In our case having some `TypeList<T1,T2,T3,...Tn>` type of arbitrary length would be nice to generate the corresponding function e.g. `operator()(T1,T2,T3,...Tn)`. This would completely solve the problem of type safe function-call. But as we already said this can't be done in C++ and we have to find out roundabout ways. There are two ways. In the following first way we can declare a `Functor` class template and then define partial specializations for all possible count of types in the `operator()`:

```template <class R, class TList> class Functor;
template <class R> class Functor<R, TYPELIST_0(P1)> { ... };
template <class R, class P1> class Functor<R, TYPELIST_1(P1)>
{ ... };
template <class R, class P1, class P2>
class Functor<R, TYPELIST_2(P1, P2)> { ... };
...```

But in this case each specialization also contains all the code independent of the `operator()` arguments count. One can try to factor out this code in some way into the base classes for example (as boost does) etc. But the result does not seem to be good.

In the second way we define a single front-end functor template and simply overload all possible `operator()`:

```template <class R, class TList> class Functor
{
...
operator()()
operator()(T1)
operator()(T1,T2)
...
operator()(T1,T2,...Tn)
...
operator()(T1,T2,T3,...Tmax)
};```

This solves the problem but introduces some potential bugs. If we try to use the resulting generalized functor template as in the opening code listings and uncomment the commented line 5, the compiler will gaily compile them guaranteeing runtime crash! The solution is to take `FunImplBase`, `FunctorImpl` and `MemberFnImpl` declarations out of the `Functor` template and define specializations for all possible count of types in the `call_` functions. See an example for `FunImplBase`:

```template <class R, class TList> struct FunImplBase;
... partial specializations for different numbers of arguments ...
template <class R, typename P1, P2>
struct FunImplBase<R, TYPELIST_2(P1, P2)>
{
virtual R call_(P1 p1, P2 p2) = 0;
...
};```

There are a number of specializations here, but each one contains only one suitable `call_` function. So the code for line 5 in the opening code listing is not compilable now. Though the compiler can find a suitable `operator()` in the `Functor` template, it fails to find a suitable `call_` function in a corresponding `FunImplBase` specialization. But as you may notice, we've got back to the necessity of partial specializations - not for the `Functor` template itself but for some helper classes. This is a bad news. The good and the more important ones are that the specializations contain as little unnecessary code (independent from the count of arguments of the `call_` function) as possible. Loki's implementation follows this way and achieves very good code factoring.

We've considered how generalized functor requirements and implementation detail issues (R1, R2, D1 and D2) can be solved and how they are solved in the existing libraries. We'll leave the requirement R3 issues untouched since they do not introduce any new ideas and are rather easy to implement.

## Going forward

In the previous section, we discussed the generalized functor requirements, design problems and possible solutions. We've also got to a principle implementation usually used in the existing libraries. In this section, we'll try to investigate and analyze its disadvantages and remove them.

One of the disadvantages which can be discovered from the previous section examples is the need of heap allocation for `FunctorImpl` and `MemberFnImpl` instances. This is a squander - both from the point of view of speed and memory consumptions (because all general-purpose allocators allocate memory with some granularity wasting some space). Keeping this in mind, Loki provides for its functor template the optimized custom small object allocator. Boost implementation contains a trick that allows to refuse heap allocation for generalized functors for non-member functions, but rolls down to heap allocation with generalized functors in other cases.

How about refusing heap allocation in all cases? The possible solution is to incorporate a buffer of some fixed (but customizable) size into the functor class template itself and use it as a pool for memory allocation. This idea exploits the fact that for all callable entity kinds, only a few bytes of memory are needed to store the internal data. Thus with a functor for a `static` function only the function pointer (4 bytes on 32-bit systems) should be stored. With functor to a member function a pointer to the target object instance and a pointer-to-member for a function to be called are needed. The former takes 4 bytes again, while the size of the latter can vary from 4 to 20 bytes (see an excellent study here [5]). Internal data size for an arbitrary functor can't be predicted beforehand, but we know from our experience that functors are usually designed to have only a few data members or no members at all.

This idea can be expressed in the following way:

```template <class R, class TList, unsigned int size = 4 * sizeof(void*)>
class Functor
{
...
struct Typeless {
char buffer_[size];
template <class T, class V> T* init(V const& v)
{ new(&buffer[0]) T(v); }
template <typename T> inline T const& get()
const { return *reinterpret_cast<T const*>(&buffer[0]); }
template <typename T> inline T& get()
{ return *reinterpret_cast<T*>(&buffer[0]); }
}
Typeless val_;
FunImplBase<R, TList> *pimpl_;
public:
...
// ctor for non-member functions and arbitrary functors
template <typename F> Functor(F const& fun)
{
pimpl_ = val_.init<FunctorImpl<F> >(fun);
}
...
};```

Functors designed in the above way, waste some space in the case of least memory-consuming functors for `static` functions. But, firstly, when using heap allocation, some memory wastes occur due to the allocation granularity. And, secondly, the profit is admirable - all functor operations involved in copy-by-value support (ctors, dtor, `operator=`) work with a giddy speed. These operations are intensively used in important generalized functor applications based on functor chaining and binding.

For the remaining minority of cases when `sizeof(T) > size` we should provide in `Typeless::init()` switching to normal allocator. The simplest way to do this is the following:

```template <class R, class TList,
unsigned int size = 4 * sizeof(void*)>
class Functor
{
...
struct Typeless {
char buffer_[size];
template <class T, class V> T* init(V const& v)
{ new(&buffer[0]) T(v); }
template <typename T> inline T const& get() const
{ return *reinterpret_cast<T const*>(&buffer[0]); }
template <typename T> inline T& get()
{ return *reinterpret_cast<T*>(&buffer[0]); }
}
template <typename T>
struct ByValue
{
template <typename V> inline static T* init(Typeless& val,
V const& v) { return val.template init<T>(v); }
inline static T const& get(Typeless const& val)
{ return val.get<T>(); }
inline static T& get(Typeless& val)
{ return val.get<T>(); }
};
template <typename T>
struct NewAlloc
{
template <typename V> inline static T* init(Typeless& val,
V const& v) { return *val.template init<T*>(new T(v)); }
inline static T const& get(Typeless const& val)
{ return *val.get<T const*>(); }
inline static T& get(Typeless& val)
{ return *val.get<T*>(); }
};
template <typename T>
struct SelectStored
{
typedef typename Select<
sizeof(T)<=sizeof(Typeless),
ByValue<T>,
NewAlloc<T>
>::Result Type;
// Select meta-template returns
// an appropriate type depending on its first argument
};
struct Stored
{
template <typename T, typename V> inline T* init(V const& v)
{ return SelectStored<T>::Type::init(val_, v); }
template <typename T> inline T const& get() const
{ return SelectStored<T>::Type::get(val_); }
template <typename T> inline T& get()
{ return SelectStored<T>::Type::get(val_); }
Typeless val_;
};
Stored val_;
FunImplBase *pimpl_;
public:
...
// ctor for non-member functions and arbitrary functors
template <typename F> Functor(F const& fun)
{
pimpl_ = val_.init<FunctorImpl<F> >(fun);
}
...
};```

In the above code snippet, `val_` is used either to store the value of `T` entirely, or to store only the pointer to the newly-allocated value. Note: `sizeof(T)<=sizeof(Typeless)` expression used in the `Select` meta-template is not enough and should be improved by adding alignment calculations (see [6] or [3]).

Digging in we discover one more disadvantage. After introducing `val_`, `pimpl_` pointer becomes redundant! We always "almost" know that - it is equal to either `&val_` or incorporated into `val_` itself. So, if we could make such a choice we could throw `pimpl_` away. Remember that each instance of a class containing `virtual` functions (either own or inherited ones) maintains an invisible pointer to the `virtual` methods table which directs the `virtual` function calls towards the right function of the right class. If we could deal with it directly and take it out from `FunctorImpl` and `MemberFnImpl` instances into the `Functor` template, we could make all the needed calls to the right `FunctorImpl` or `MemberFnImpl` functions and additionally save 4 bytes! Of course, we couldn't do it dealing with C++ virtual function mechanism immediately. But we could simulate this mechanism. This is a known and a rather powerful technique (see [6] or [7] for example). For our case it could be applied in the following way:

```template <class R, class TList,
unsigned int size = 4 * sizeof(void*)>
class Functor
{
...
struct FunImplBase
{
struct VTable
{
R (*call_)(Functor const&,
...params evaluated from TList...);
...
};
// VTable vtbl_;
};
template <typename F>
class FunctorImpl : public FunImplBase
{
F fun_;
public:
FunctorImpl(F const& fun) : fun_(fun) {}
static R Call(Functor const& f,
...params evaluated from TList...)
{
FunctorImpl const& this_ =
f.val_.template get<FunctorImpl const>();
... use this_ and params to make a call to fun_ ...
}
...
};
template <class P, class MF>
class MemberFnImpl : public FunImplBase
{
P pobj_;
MF memfun_;
public:
MemberFnImpl(P const& pobj, MF memfun) :
pobj_(pobj), memfun_(fun) {}
static R Call(Functor const& f,
...params evaluated from TList...)
{
MemberFnImpl const& this_ =
f.val_.template get<MemberFnImpl const>();
... use this_ and params to make ...
... a call to memfun_ of pobj_ ...
}
...
};
...
Stored val_;
typename FunImplBase::VTable* vptr_;
public:
...
// ctor for non-member functions and arbitrary functors
template <typename F> Functor(F const& fun)
{
FunImplBase* pimpl_ = val_.init<FunctorImpl<F> >(fun);
// throw away pimpl, we don't need it in
// this implementation
static typename FunImplBase::VTable vtbl =
{
&FunctorImpl::Call,
...
};
vptr_ = &vtbl;
}
R operator(...params evaluated from TList...)
{
return vptr_->call_(*this,
...params evaluated from TList...);
}
...
};```

Note: This is not a plane translation of the previous example with `virtual` functions, this is an adapted translation, e.g. pointer to `FunImplBase` is thrown away and is replaced by a pointer to a function table, `FunctorImpl::Call` function etc. accepts `Functor` instance reference as the first argument (not a `FunctorImpl` pointer that could be in plane translation). The result seems to be acceptable.

Let's switch to R2 and D2 issues now. We can notice that when calling `FunctorImpl::Call` and `MemberFnImpl::Call` from `operator()`, all its arguments should always be copied. Because, this is a call via a function pointer (as with virtual function call as well by the way) the compiler could not apply any optimizations. So what if we pack `operator()` arguments into a tuple and pass it to `FunctorImpl::Call` or `MemberFnImpl::Call`? Arguments copying overhead will remain the same, but `FunctorImpl` and `MemberFnImpl` could become independent from `operator()` arguments count. It is not shown in the previous listings but in addition to the `Call` functions `FunctorImplBase`, `FunctorImpl` and `MemberFnImpl` should also contain functions for value semantics support. So their independence from `operator()` arguments count evidently improves the code:

```template <class TList> struct CallParms;
... partial specializations for typelists with different length ...
template <typename P1,
typename P2> struct CallParms<TYPELIST_2(P1, P2)>
{
typedef InstantiateH<tTYPELIST_2(P1, P2)> ParmsListType;
static inline ParmsListType Make(P1 p1, P2 p2)
{ ... make ParmsListType tuple from p1 and p2 ... }
};
template <typename CallType, typename R,
class TList> struct FunctorCall;
//... partial specializations for different numbers
//    of arguments of the function types ...
template <typename CallType, typename R, P1, P2>
struct FunctorCall<CallType, R, TYPELIST_2(P1, P2)>
{
typedef InstantiateH<tTYPELIST_2(P1, P2)> ParmsListType;
template <class Fun> static inline
R Call(Fun const& fun, ParmsListType& parms)
{
return fun(... parameters unpacked from parms ...);
}
template <class PObj> static inline R Call(PObj const& pobj,
CallType memfun, ParmsListType& parms)
{
return (
(*pobj).*memfun)(... parameters unpacked from parms ...);
}
};
...
template <class R, class TList,
unsigned int size = 4 * sizeof(void*)>
class Functor
{
...
typedef typename CallParms<TList>::
ParmsListType ParmsListType;
struct FunImplBase
{
struct VTable
{
R (*call_)(Functor const&, ParmsListType parms);
...
};
// VTable vtbl_;
};
template <typename F>
class FunctorImpl : public FunImplBase
{
F fun_;
public:
FunctorImpl(F const& fun) : fun_(fun) {}
static R Call(Functor const& f, ParmsListType parms)
{
FunctorImpl const& this_ =
f.val_.template get<FunctorImpl const>();
return FunctorCall<T, R, TList>::Call(this_.fun_,
parms);
}
...
};
template <class P, class MF>
class MemberFnImpl : public FunImplBase
{
P pobj_;
MF memfun_;
public:
MemberFnImpl(P const& pobj, MF memfun) :
pobj_(pobj), memfun_(fun) {}
static R Call(Functor const& f, ParmsListType parms)
{
MemberFnImpl const& this_ =
f.val_.template get<MemberFnImpl const>();
return FunctorCall<T, R, TList>::Call(this_.pobj_,
this_.memfun_, parms);
}
...
};
...
Stored val_;
typename FunImplBase::VTable* vptr_;
public:
...
// ctor for non-member functions and arbitrary functors
template <typename F> Functor(F const& fun)
{
FunImplBase* pimpl_ = val_.init<FunctorImpl<F> >(fun);
// throw away pimpl, we don't need it in this
// implementation
static typename FunImplBase::VTable vtbl =
{
&FunctorImpl::Call,
...
};
vptr_ = &vtbl;
}
... evaluate Parm1 type from TList ...
... evaluate Parm2 type from TList ...
...
inline R operator()() const
{
return vptr_->call_(*this, CallParms<TList>::Make());
}
inline R operator()(Parm1 p1) const
{
return vptr_->call_(*this, CallParms<TList>::Make(p1));
}
inline R operator()(Parm1 p1, Parm2 p2) const
{
return vptr_->call_(*this,
CallParms<TList>::Make(p1, p2));
}
...
};```

Note: In the above listing `InstantiateH` - is a kind of `GenScatterHierarchy<>` template in Loki used here as a tuple generation engine (see [1] for details).

Packing `operator()` arguments into the tuple and passing them in internal calls can lead to additional benefits that exceed the bounds of `Functor` implementation itself. E.g. we can add the following overloaded `operator()`:

```template <class R, class TList,
unsigned int size = 4 * sizeof(void*)>
class Functor
{
... as above ...
inline R operator()(ParmsListType& parms) const
{
return vptr_->call_(*this, parms);
}
};```

and use it to pass `parms` reference around two or more functors. This can simplify the implementation and even improve the efficiency of functor chaining and binding support. We could even dream of named parameters in the `Functor`'s `operator()` calls - the feature not supported by C++ for simple function-calls.

## Conclusions

We've considered generalized functor requirements, existing implementation problems and disadvantages. While trying to solve them we've introduced several ideas, which combined together lead to an implementation that is rather different from the known ones and seems to be more clear and efficient.

## Source update (14-Dec-2005)

Generalized functors binding is added. Check out FunBind.h in the source files given with the article.

## References

• [1]. Modern C++ Design: Generic Programming and Design Patterns Applied (Addison-Wesley, 2001, ISBN 0201704315), A.Alexandrescu
• [2]. Loki Library Project
• [3]. boost Library Project Home
• [4]. Generalized Function Pointers, H.Sutter
• [5]. Member Function Pointers and the Fastest Possible C++ Delegates, D.Clugston
• [6]. Generic<Programming>: Discriminated Unions (II), A.Alexandrescu
• [7]. Generic<Programming>: Discriminated Unions (III), A.Alexandrescu

A list of licenses authors might use can be found here

## Share

 Web Developer Russian Federation
No Biography provided

 First Prev Next
 alignment issue frahmog10-Nov-13 22:52 frahmog 10-Nov-13 22:52
 My vote of 5 arif111114213-Jan-13 10:01 arif1111142 13-Jan-13 10:01
 Please, can you help me understand probably simple thing? I am tired searching for samples :( DanteLV21-Oct-09 10:39 DanteLV 21-Oct-09 10:39
 Doesn't compile properly on gcc 3.4.4 Ben Schleimer2-Jan-06 1:40 Ben Schleimer 2-Jan-06 1:40
 Re: Doesn't compile properly on gcc 3.4.4 Aleksei Trunov3-Jan-06 6:03 Aleksei Trunov 3-Jan-06 6:03
 How ? JimD.999913-Dec-05 12:06 JimD.9999 13-Dec-05 12:06
 Re: How ? Aleksei Trunov14-Dec-05 3:17 Aleksei Trunov 14-Dec-05 3:17
 Re: How ? JimD.999914-Dec-05 5:08 JimD.9999 14-Dec-05 5:08
 Using Allocator Dinesh Ahuja16-Oct-05 17:55 Dinesh Ahuja 16-Oct-05 17:55
 Re: Using Allocator Aleksei Trunov14-Dec-05 4:20 Aleksei Trunov 14-Dec-05 4:20
 Some interesting ideas here. Don Clugston20-Jun-05 15:15 Don Clugston 20-Jun-05 15:15
 Re: Some interesting ideas here. Aleksei Trunov22-Jun-05 11:55 Aleksei Trunov 22-Jun-05 11:55
 Re: Some interesting ideas here. [modified] JaeWook Choi20-Dec-06 6:10 JaeWook Choi 20-Dec-06 6:10
 Re: Some interesting ideas here. Aleksei Trunov24-Dec-06 22:35 Aleksei Trunov 24-Dec-06 22:35
 Nice Alex Mol13-Jun-05 20:44 Alex Mol 13-Jun-05 20:44
 Re: Nice Anonymous14-Jun-05 3:36 Anonymous 14-Jun-05 3:36
 Re: Nice Aleksei Trunov14-Jun-05 3:48 Aleksei Trunov 14-Jun-05 3:48
 Re: Nice Alex Mol14-Jun-05 5:15 Alex Mol 14-Jun-05 5:15
 Last Visit: 31-Dec-99 18:00     Last Update: 22-Jun-21 20:42 Refresh 1