Writing a method-functor

D

daniel.w.gelder

I wrote a template class that takes a function prototype and lets you
store and call a C-level function, like this:

inline string SampleFunction(int, bool) {..}

functor<string (int, bool)> myFunctor = SampleFunction;
string result = myFunctor(7, true);

Works great thanks to the help from this group. Here's the guts so far
for two-arity:

struct emptiness {}; // helps unconfuse the compiler
template < typename F , typename E=emptiness > class functor;
// two param specialization
template < typename R, typename A1, typename A2 > class functor< R( A1,
A2 ), emptiness >
{
typedef R(*pt)(A1, A2);
pt _pt;
public:
functor( ) : _pt( 0 ) {}
functor( pt arg ) : _pt( arg ) {}
bool isEmpty() { return (_pt == 0); }
R operator () ( A1 a1, A2 a2 ) const { if (_pt) return _pt( a1, a2 );
Fail(failureError); }
functor& operator = (pt arg) { _pt = arg; return *this; }
};

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?) I started by adding
this to the class:

template <typename O>
void setToMethodWithTarget(O& target, R(O::*method)(A1, A2));

and then this code compiles okay:

struct Shill
{
string Conforming(int, bool) {}
};
Shill shill;
functor<string (int, bool)> myFunctor;
myFunctor.setToMethod<Shill>(shill, &Shill::Conforming);

That's a little syntax-heavy but at least it works. Problem is, how can
I store the method pointer and target object so it can be called in the
operator()()? 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.

Thanks very much in advance

Dan
 
K

Kanenas

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::eek:perator()");
}

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::eek:perator()");
}

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::eek:perator()");
}

};

}

#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>::eek:perator()
(_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>::eek:perator() (_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");
}
 
K

Kanenas

Section 18.4 in Stroustrup, "Function Objects", caught my eye. It
mentions base classes for unary and binary functors, which you may
want to use to make your functors more compatible with standard
library (below is an addition to Functor_base from the previous post
which uses one of these base classes). Adapters bind to a member
function and will call that member on any objects passed to them.
They allow Class::*member(...) to be called as member_(obj, ...). Not
quite what you're looking for, but potentially useful. It appears
these standard library classes only support functors up to arity 2.
Stroustrup explains this is because no standard library algorithm
takes more than an arity 2 function/functor.

Section 18.4.4.2 made me realize my code provided no support for const
member functions. A ConstMemberFunctor class, with function_pointer
type equal to "_Return_type (_Class::*)(_Arg1, _Arg2) const", will do
the job for arity 2 member functions.

To make Functor_base a child of binary_function:

template <typename _Return_type,
typename _Arg1, typename _Arg2>
class Functor_base<_Return_type (_Arg1, _Arg2)>
: public binary_function said:

Kanenas
 
K

Kanenas

[...] Adapters bind to a member
function and will call that member on any objects passed to them.
They allow Class::*member(...) to be called as member_(obj, ...). Not
quite what you're looking for, but potentially useful. [...]

Strike that. Combine an adaper with a binder in a manner similar to:
template <typename _Result_type, typename _Arg1,
typename _Arg2, typename _Class>
binary_function binop_from_ObjAndMeth
(_Class& obj, _Return_type _Class::*pmeth(_Arg1, _Arg2) )
{
return binder_triop_1st(mem_fun2_ref_t(pmeth), obj);
}
and you've produced a functor which calls a method of an object using
the supplied arguments. Note the standard library has no
'binder_triop*' classes, but they'd be easy to create
('binder_triop_1st' should bind the first argument of a trinary
operator, which is what you'd get by applying an adapter to a binary
method).

Kanenas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top