member function traits
I've been tooling around with passing member function points via
template parameters and have, along the way, worked out a way of
writing traits classes for member function pointers that people may be
interested in.
Basically I am using a library that wants to bind names to functions
with a fixed signature, something like:
void func(libclass *cl, int nargs, char *argv[]);
Note: there is no auxiliary data in this signature so traditional
dispatch methods cannot be used.
Obviously it was originally envisaged that there would be a lot of
functions with different names,
void func1(libclass *cl, int nargs, char *argv[])
{
wrapped_object *wo = (wrapped_object *)cl;
wo->func1(argv[0], argv[1]);
};
void func2(libclass *cl, int nargs, char *argv[])
{
wrapped_object *wo = (wrapped_object *)cl;
wo->func2(argv[0]);
}
libclass *cl = new wrapped_object;
bind(cl, "func1", func1);
bind(cl, "func2", func2);
This is obviously a right royal PITA if you want to bind many member
functions: for each (already coded) member function you need to write
(and compile) a stub for it.
So I started using functions parameterised on member functions,
template<typename class_t, typename ret_t, typename arg0_t, ret_t
(class_t::*fp)(arg0_t) >
void gfunc1(libclass *cl, int nargs, char *argv[])
{
(((class_t *)cl)->*fp)(cast<arg0_t>(argv[0]));
}
bind(cl, "func1", gfunc1<wrapped_class, void, int,
&wrapped_class::func1>);
bind(cl, "func2", gfunc1<wrapped_class, void, int,
&wrapped_class::func2>);
bind(cl, "func3", gfunc1<wrapped_class, void, int,
&wrapped_class::func3>);
This required a bit of effort (and some code generating code) but
eventually worked.
This then left me having to write things like,
bind(cl, "func236", gfunc4<wrapped_class, bool, const std::string &,
int, int, float, &wrapped_class::func236>);
which is (obviously) quite ugly. It is also over defined as the
compiler knows the signature of wrapped_class::func236 so why should I
have to specify it again?
What I really want is somthing like,
template<typename tof_t, typename tof_t::ret_t (typename
tof_t::class_t::*fp)(typename tof_t::arg0_t) >
void gfunc(libclass *cl, int nargs, char *argv[])
{
(((typename tof_t::class_t *)cl)->*fp)(cast<typename
tof_t::arg0_t>(argv[0]));
}
bind(cl, "func1", gfunc4<wrapped_class,
memfun_traits<&wrapped_class::func236>, &wrapped_class::func236>);
where memfun_traits is some manner of traits class that reflects the
signature of wrapped_class::func236
As discussed in this newsgroup, there is no proper type_of in C++ and
the closest you can get (tr1::result_of) is a bit of a bodge.
With all of that in mind, I present, for the delight and elucidation of
all readers, a half baked traits scheme for member functions inspired
by sfinae methods.
#include <iostream>
// an integer wrapped in a type
template<int size> class sigid { char data[size]; };
// definitions of signature IDs for various member function signatures
const int sigid_vi = 1;
const int sigid_vii = 2;
const int sigid_conststr_int = 3;
// a mapping from member function pointer values to member function
signature ids
template<typename class_t> sigid<sigid_vi> generate_sigid(void
(class_t::*)(int));
template<typename class_t> sigid<sigid_vii> generate_sigid(void
(class_t::*)(int, int));
template<typename class_t> sigid<sigid_conststr_int>
generate_sigid(void (class_t::*)(const std::string &, int));
// a set of traits, templated on member function signature ids (i.e.
integers)
template<typename class_t, int tid> class memfun_traits
{
virtual void dont_instantiate_this_template() = 0;
};
// specialisations for the signatures defined above
template<typename _class_t> class memfun_traits<_class_t, sigid_vi>
{
public:
static const int nargs = 1;
typedef void return_t;
typedef int arg0_t;
typedef _class_t class_t;
typedef void (class_t::*rebind)(int);
};
template<typename _class_t> class memfun_traits<_class_t, sigid_vii>
{
public:
static const int nargs = 2;
typedef void return_t;
typedef int arg0_t;
typedef int arg1_t;
typedef _class_t class_t;
typedef void (class_t::*rebind)(int, int);
};
template<typename _class_t> class memfun_traits<_class_t,
sigid_conststr_int>
{
public:
static const int nargs = 2;
typedef void return_t;
typedef const std::string &arg0_t;
typedef int arg1_t;
typedef _class_t class_t;
typedef void (class_t::*rebind)(const std::string &, int);
};
// some macros to sugar the pill
// almost acceptable
#define signature(cl, f) memfun_traits<cl,
sizeof(generate_sigid(&cl::f))>
// abuse that syntax
#define fsignature(cl, f) memfun_traits<cl,
sizeof(generate_sigid(&cl::f))>, &cl::f
// some examples of use
template<typename tof, typename tof::rebind pointer>
void caller1(typename tof::class_t *base, typename tof::arg0_t a)
{
(base->*pointer)(a);
}
template<typename tof, typename tof::rebind pointer>
void caller2(typename tof::class_t *base, typename tof::arg0_t a,
typename tof::arg1_t b)
{
(base->*pointer)(a, b);
}
class poop
{
public:
void f(int i)
{
std::cerr << "Poop::f is called with " << i << "\n";
};
void ff(int i, int b)
{
std::cerr << "Poop::ff is called with " << i << " " << b << "\n";
};
};
int main(int argc, char *argv[])
{
poop p;
caller1<fsignature(poop, f)>(&p, 34);
caller2<fsignature(poop, ff)>(&p, 89, 199);
}
Cons:
You need to specifiy explicitly which signatures you are going to use
There's a lot of (easily mistyped) typing for each signature
Pros:
Once written, the signatures can be used without much typing.
The signature library can be automatically generated
Conclusion:
It *is* possible to write member function traits classes.
--
Charles McLachlan - Metropolis Data Consultants
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]