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