On 9 Apr 2005 16:19:40 -0700,

da*************@gmail.com wrote:

I wrote a template class that takes a function prototype and lets you

store and call a C-level function, like this:

[...]struct emptiness {}; // helps unconfuse the compiler

template < typename F , typename E=emptiness > class functor;

My solution didn't seem to need parameter E/struct emptiness (compiled

with gcc 3.3.2), even with arity 0 functors. What am I missing about

their use?

[...]I love how simple it is to use. OK, so, what I want to do now is expand

the same class to support method functions, including a target object

of course (because otherwise what can you call?)
[...]Problem is, how can

I store the method pointer and target object so it can be called in the

operator()()?

Initially, I thought of various designs, including using a common base

class for the object pointer and discarding type information, but only

one worked (described below).

Has anyone ever seen that done? What I'm after here,

clearly to make should I, is to not have to define the specific class

in the functor's constructor. As long as the method fits the prototype,

it should be storable by the functor.

It's acceptable to have to specify the target's class manually in

setToMethod<>, but it's NOT acceptable to have to specify it in the

actual operator()() or in the constructor. To sum up.

Given the requirements, the only approach I could get to work (source

at end of message) was to create a secondary method functor class

(class MethodFunctor) which took the class as a template argument.

MethodFunctor descends from a class with virtual operator(). The

Functor class holds a pointer (named pfunc) to the base of

MethodFunctor (so Functor knows nothing of classes or method

pointers), and my version of setToMethodWithTarget stores a pointer to

a dynamically allocated MethodFunctor in pfunc. As this design

started down the road of a base functor class, I moved the

functionality of your "class functor" into "class FunctionFunctor",

made class Functor a union of MethodFunctor and FunctionFunctor, and

made all three classes descend from an abstract base Functor_base.

I think something like class MethodFunctor is necessary under the

requirements, but you can change the class hierarchy.

Example 1: replace MethodFunctor with a specialization of your

class functor which accepts a class as a template parameter:

template < typename R, typename A1, typename A2, typename C >

class functor< R C::*( A1,A2 ), emptiness >

: base functor_base<R ( A1,A2 ), emptiness>

This is in addition to the non-method functor you already have and a

common base. class functor could then be altered, perhaps storing a

pointer to a base of the method functor (something like that would be

necessary as the non-method functor cannot know the class of the

method outside of setToMethodWithTarget).

Example 2: Merge the functionality of FunctionFunctor into

Functor. Functor would hold 2 pointers, a Functor_base* (to hold a

pointer to a MethodFunctor) and a non-method function pointer (same

usage as _pt in your class functor, or pfunc in FunctionFunctor)

Why is MethodFunctor (or something like) needed? Functor cannot keep

track of the type of either an object or a method, but must (directly

or indirectly) store an object pointer and a method pointer. Either

Functor stores a pointer to a common base class (which would restrict

classes to descendants of the base and restrict class methods to

virtual methods of the base class) or some class needs to keep track

of the types.

Another approach I was curious about requires changing the

requirements. A functor is generally used to turn a normal function

into an object. Why not use functors solely for normal functions and

for classes make the class a functor class? You couldn't call

arbitrary methods with this approach or mix functor for non-method

functions and functor classes, but that may not be an issue.

Thanks very much in advance

You're welcome. I hope the code below works for you (or at least is

worth the thanks).

The functor implementation has been tested with functor_tst.cc below,

but nothing beyond that. I've probably missed something, somewhere,

so you'll need to pick it apart and run your own tests.

Kanenas

/* Functor.h */

#ifndef FUNCTOR_H

#define FUNCTOR_H

#include <stdexcept>

namespace Fun {

struct null_function_error : public std::runtime_error {

null_function_error(std::string arg="")

: std::runtime_error(arg)

{}

};

namespace {

//what is emptiness needed for?

struct emptiness {};

}

template <typename _Func_type> class Functor_base;

template <typename _Func_type> class MethodFunctor;

template <typename _Func_type> class FunctionFunctor;

template <typename _Func_type> class Functor;

template <typename _Return_type,

typename _Arg1, typename _Arg2>

class Functor_base<_Return_type (_Arg1, _Arg2)> {

public:

/*

A function_pointer is the type of the function a functor

contains. For a FunctionFunctor, it's as it is here. For an

MethodFunctor, it's a pointer to a method. For a Functor (which

is a union type for descendants of Functor_base [MethodFunctor

and FunctionFunctor]), it's a pointer to Functor_base.

*/

typedef _Return_type (*function_pointer)(_Arg1, _Arg2);

virtual Functor_base* clone() const = 0;

virtual ~Functor_base() {}

virtual _Return_type operator()(_Arg1, _Arg2) const = 0;

/*

if valid() is false, calling operator() will throw a

null_function_error.

*/

virtual bool valid() const = 0;

protected:

};

/*

A MethodFunctor keeps track of an object and a method, and will

call the method on the object.

*/

template <typename _Return_type, typename _Arg1,

typename _Arg2, typename _Class>

class MethodFunctor<_Return_type _Class::*(_Arg1, _Arg2)>

: public virtual Functor_base<_Return_type (_Arg1, _Arg2)>

{

public:

typedef _Return_type (_Class::*function_pointer)(_Arg1, _Arg2);

typedef Functor_base<_Return_type (_Arg1, _Arg2)> Base;

MethodFunctor(_Class* po=0, function_pointer meth=0)

: pobj(po), pmeth(meth) {}

MethodFunctor(_Class& obj, function_pointer meth)

: pobj(&obj), pmeth(meth) {}

virtual Base* clone() const {return new MethodFunctor(*this);}

virtual bool valid() const { return pobj && pmeth; }

virtual _Return_type operator()(_Arg1 a1, _Arg2 a2) const {

if (valid())

return (pobj->*pmeth) (a1, a2);

throw null_function_error("MethodFunctor::operator()");

}

MethodFunctor& operator=(_Class* po)

{ pobj = po; return *this; }

MethodFunctor& operator=(_Class& obj)

{ pobj = &obj; return *this; }

MethodFunctor& operator=(function_pointer pm)

{ pmeth = pm; return *this; }

MethodFunctor& set(_Class* po, function_pointer pm)

{ pobj = po; pmeth = pm; return *this; }

MethodFunctor& set(_Class& obj, function_pointer pm)

{ pobj = &obj; pmeth = pm; return *this; }

private:

_Class* pobj;

function_pointer pmeth;

};

/*

A FunctionFunctor keeps track of a non-method function (or static

method, same difference) and will call the function.

*/

template <typename _Return_type, typename _Arg1, typename _Arg2>

class FunctionFunctor<_Return_type (_Arg1, _Arg2)>

: public virtual Functor_base<_Return_type (_Arg1, _Arg2)>

{

public:

typedef _Return_type (*function_pointer)(_Arg1, _Arg2);

typedef Functor_base<_Return_type (_Arg1, _Arg2)> Base;

FunctionFunctor(function_pointer pf=0)

: pfunc(pf) {}

virtual Base* clone() const {return new FunctionFunctor(*this);}

virtual bool valid() const { return pfunc; }

virtual _Return_type operator()(_Arg1 a1, _Arg2 a2) const {

if (valid())

return pfunc(a1, a2);

throw null_function_error("FunctionFunctor::operator()") ;

}

private:

function_pointer pfunc;

};

/*

A Functor is a union of the children of Functor_base

(currently MethodFunctor, ObjectFunctor and Functor), can

contain any of them and will call the stored functor. Though

you could store a pointer to a Functor in a Functor, all you will

get is wasted cycles and memory.

*/

template <typename _Return_type, typename _Arg1, typename _Arg2>

class Functor<_Return_type (_Arg1, _Arg2)>

: public virtual Functor_base<_Return_type (_Arg1, _Arg2)>

{

typedef FunctionFunctor<_Return_type (_Arg1, _Arg2)> FuncFunctor;

public:

typedef _Return_type Return_type;

typedef Functor_base<_Return_type (_Arg1, _Arg2)> Base;

typedef Base* function_pointer;

typedef FunctionFunctor<_Return_type (_Arg1, _Arg2)> FuncFunctor;

/*

plain_function_pointer is used in Functor methods which make

pfunc point to a plain (non-method) function.

*/

typedef typename FuncFunctor::function_pointer

plain_function_pointer;

private:

/*

pmeth will point to a (dynamically allocated) MethodFunctor or

FunctionFunctor. a Functor is solely responsible for freeing

what pfunc points to, so whatever pfunc points to must be

a unique object. That's why Functor_base has the

clone() method (and why you'll see it used so often).

*/

function_pointer pfunc;

public:

Functor(plain_function_pointer pf=0) : pfunc(0)

{ if (pf) pfunc = new FuncFunctor(pf); }

Functor(function_pointer pf) : pfunc(pf ? pf->clone() : 0) {}

Functor(const Functor& from)

: pfunc(from.pfunc ? from.pfunc->clone() : 0) {}

virtual Base* clone() const {return new Functor(pfunc);}

virtual ~Functor() { delete pfunc; }

Functor& operator= (plain_function_pointer pf) {

if (pf) { delete pfunc; pfunc = new FuncFunctor(pf); }

return *this;

}

Functor& operator= (function_pointer pf)

{ delete pfunc; pfunc = pf->clone(); return *this; }

Functor& operator= (const Base& from) {

if (from.valid()) { delete pfunc; pfunc = from.clone(); }

return *this;

}

//copy constructor

Functor& operator=(const Functor& from) {

if (from.pfunc)

{ delete pfunc; pfunc = from.pfunc->clone(); }

return *this;

}

template <typename _Class>

Functor& setToMethod

(_Class& obj, _Return_type (_Class::*method)(_Arg1, _Arg2))

{

pfunc = new MethodFunctor

<_Return_type _Class::*(_Arg1, _Arg2)>

(&obj, method);

return *this;

}

template <typename _Class>

Functor& setToMethod

(_Class* pobj, _Return_type (_Class::*method)(_Arg1, _Arg2))

{

pfunc = new MethodFunctor

<_Return_type _Class::*(_Arg1, _Arg2)>

(pobj, method);

return *this;

}

virtual bool valid() const { return pfunc && pfunc->valid(); }

virtual _Return_type operator() (_Arg1 arg1, _Arg2 arg2) const {

/* could call valid() rather than testing pfunc,

but this way allows *pfunc to throw null_function_error

w/ message identifying the type which threw it.

*/

if (pfunc)

return pfunc->operator()(arg1, arg2);

throw null_function_error("Functor::operator()");

}

};

}

#endif

/* functor_tst.cc: test Functor implementation */

#include <iostream>

using std::cout;

using std::endl;

#include "Functor.h"

template <typename _Num>

_Num add(_Num l, _Num r)

{ return l+r; }

template <typename _Num>

_Num mult(_Num l, _Num r)

{ return l*r; }

struct Foo {

Foo() : i(0) {}

int foo(int l, int r) { return i=r-l; }

int i;

};

struct Bar {

Bar() : i(0) {}

int bar(int l, int r) { return ++i; }

Bar& operator= (int _i) { i = _i; return *this; }

int i;

};

template <typename _Arg>

struct Test {

int fails, tests;

Foo foo;

Bar bar;

Fun::Functor<_Arg (_Arg, _Arg)> functor;

Test(_Arg l, _Arg r) {operator() (l, r);}

void operator() (_Arg l, _Arg r, _Arg expected,

const std::string& name);

void test (_Arg l, _Arg r, _Arg expected, const std::string& name);

void operator() (_Arg l, _Arg r);

};

int main() {

Test<int> itest(1,2);

cout << "fails/tests: " << itest.fails << '/'

<< itest.tests << endl;

return 0;

}

template <typename _Arg>

void Test<_Arg>::operator()

(_Arg l, _Arg r, _Arg expected,

const std::string& name)

{

_Arg val=functor(l, r);

++tests;

if (val != expected) {

++fails;

cout << name << '(' << l << ", " << r << ") == " << val

<< ", expected " << expected << endl;

}

}

template <typename _Arg>

void Test<_Arg>::test

(_Arg l, _Arg r, _Arg expected,

const std::string& name)

{

operator()(l, r, expected, name);

}

template <typename _Arg>

void Test<_Arg>::operator() (_Arg l, _Arg r) {

fails=0;

tests=0;

functor=add;

(*this)(l, r, add(l,r), "add");

functor=mult;

(*this)(l, r, mult(l,r), "mult");

functor.setToMethod(foo, &Foo::foo);

(*this)(l, r, foo.foo(l, r), "foo.foo");

Fun::MethodFunctor<_Arg Foo::*(_Arg, _Arg)>

foofun(foo, &Foo::foo);

functor = foofun;

(*this)(l, r, foo.foo(l, r), "foo.foo");

bar = 0;

Fun::MethodFunctor<_Arg Bar::*(_Arg, _Arg)>

barfun(bar, &Bar::bar);

functor = barfun;

(*this)(l, r, 1, "bar.bar");

//test that altering barfun won't alter functor

Bar bar2;

barfun.set(bar2, &Bar::bar);

(*this)(l, r, 2, "bar.bar");

//now we'll alter bar2 via functor

functor = barfun;

(*this)(l, r, 1, "bar.bar");

}

--

Kanenas