Comment on my adapter concept
Hello,
I'm working on an adapter for a C plugin API. Plugins which are loaded
as dynamical libraries are expected to provide a function which fills
a struct of function pointers to service routines. Two of these
functions are expected to allocate and deallocate the plugins private
data. The C-Style "base class" of this data is defined as.
struct plugin_data {
PLUGIN_DATA;
};
where PLUGIN_DATA is a chunk of data neccessary for plugin
maintainance. A plugin structure might look like:
struct plugin {
void * (*alloc)(); // alloc pd_data
void (*free)(void *pd); // free pd_data
int (*do_foo1)( void *pd,foo * ); // do some with foo and pd_data
int (*do_foo2)( void *pd,foo * ); // do some more with foo and pd_data
int (*do_foobar)( void *pd,foo *,bar *); // do some with foo and bar
and pd_data
};
and after it is properly set up the main application code might do
something like:
if( (pd = p.alloc())!=NULL) {
if(p.do_foo1!=NULL) {
(*p.do_foo1)(pd,&f);
} else{
printf("Skipping do_foo1\n");
}
p.free(pd);
}
I'd like to transform the alloc/free functions into CTors and DTors of
a module class and the calling via function pointers into method
invokations and came up with the following:
My plugin_data is a templated struct which holds a module object M like:
template<M>
struct plugin_data {
PLUGIN_DATA;
M module;
}
Types of plugin function pointers and module member function pointers
are collected in traits structs accordingly like
template<class P>
struct plugin_traits {
typedef void * (*alloc_fptr_t)();
typedef void (*free_fptr_t)(void *);
typedef int (*foo_fptr_t)(void *,::foo *);
typedef int (*foobar_fptr_t)(void *,::foo *,::bar *);
};
module_traits also contains an enum which indicates which methods are
actually implemented by the concrete module. The existence of the
corresponding method will be determined via SFINAE
template<class M>
struct module_traits {
typedef M module_t;
typedef int (M:: * foo_fptr_t )(Foo & );
typedef int (M:: * foobar_fptr_t)(Foo &, Bar &);
struct has_function {
enum {
do_foo1 = true,
do_foo2 = true,
do_foobar= false
};
};
For each function pointer I implemented a corresponding struct which
provides a static function pointer "ptr" which is eiher NULL or points
to a static member function "go" that translates plugin_data struct
into a module reference and calls the appropriate module method via a
member function pointer. All of these adapters which share the same
signature share the same base class. So a group of functions like
int do_foo1( ::foo *)
int do_foo2( ::foo *)
is represented by
template<class D,class MT,class PT,bool B>
struct foo_adapter_base;
template<class D,class MT,class PT>
struct foo_adapter_base<D,MT,PT,true> {
private:
typedef typename MT::module_t module_t;
protected:
static int go( void *p, ::foo * f ) {
plugin_data<module_t> *pd = (plugin_data<module_t> *)p;
Foo foo(f);
return (pd->module.*D::mfptr)(foo);
}
private:
foo_adapter_base();
public:
static const typename PT::foo_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::foo_fptr_t foo_adapter_base<D,MT,PT,true>::ptr = D::go;
template<class D, class MT,class PT>
struct foo_adapter_base<D,MT,PT,false> {
static const typename PT::foo_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::foo_fptr_t foo_adapter_base<D,MT,PT,false>::ptr=NULL;
The static method "go" allocates objects encapsulating the argument
pointer and calls a method within the module which is expected to be
held within the static variable "mfptr" of D. If the last template arg
is true "ptr" points to the function D::go. If D does not provide such a
static function this should default to the "go" function of the base
class since concrete adapters should provide themself as D.
This allows static overwritin via CRTP.
A concrete adapter for int do_foo1(Foo &) would look like:
template<class M,class MT=module_traits<M>,class PT=plugin_traits< ::plugin
,bool B=MT::has_function::do_foo1>
struct do_foo1_adapter : public foo_adapter_base<
do_foo1_adapter<M,MT,PT,B>,MT,PT,B > {
};
template<class M,class MT,class PT>
struct do_foo1_adapter<M,MT,PT,true> : public foo_adapter_base<
do_foo1_adapter<M,MT,PT,true>, MT,PT,true > {
static typename MT::foo_fptr_t mfptr;
};
template<class M,class MT,class PT>
typename MT::foo_fptr_t do_foo1_adapter<M,MT,PT,true>::mfptr = &M::do_foo1;
So it just has to define "mfptr" if B is true, which is taken from the
module_traits::has_function struct. Otherwise it can be empty.
The alloc adapter would just create an instance of plugin_data<M> and
return the result.
In the end you may setup the plugin struct with:
p->init = init_adapter<my_module>::ptr;
p->free = free_adapter<my_module>::ptr;
p->do_foo1 = do_foo1_adapter<my_module>::ptr;
So, if you managed to read all of this you might want to share your
thoughts about this concept. Every comment will be hightly
appreciated. Is there a way to make it leaner ? Are there any pitfalls
aspecially with regard to the C/C++ calling conventions etc.
I've also attached a self contained program which compiles/runs fine
here under Fedora 8 with g++ 4.3.1 but since I used a lot of template magic
it would be great if someone finds the time to squeeze it thought
h(is|er) compiler.
Thank you very much indeed.
<snip>
#include <iostream>
extern "C" {
#define PLUGIN_DATA int id
struct foo {};
struct bar {};
struct plugin {
void * (*alloc)();
void (*free)(void *p);
int (*do_foo1)( void *pd,foo * );
int (*do_foo2)( void *pd,foo * );
int (*do_foobar)( void *pd,foo *,bar *);
};
}
struct Foo {
Foo(::foo *) {
}
};
struct Bar {
Bar(::bar *) {
}
};
class my_module {
public:
int do_foo1(Foo &) {
std::cerr << "my_module::do_foo1" << std::endl;
return 0;
}
int do_foo2(Foo &) {
std::cerr << "my_module::do_foo2" << std::endl;
return 0;
}
};
template<class M>
struct plugin_data {
PLUGIN_DATA;
M module;
};
template<class M>
struct module_traits {
typedef M module_t;
typedef int (M:: * foo_fptr_t )(Foo & );
typedef int (M:: * foobar_fptr_t)(Foo &, Bar &);
struct has_function {
enum {
do_foo1 = true,
do_foo2 = true,
do_foobar= false
};
};
};
template<class P>
struct plugin_traits {
typedef void * (*alloc_fptr_t)();
typedef void (*free_fptr_t)(void *);
typedef int (*foo_fptr_t)(void *,::foo *);
typedef int (*foobar_fptr_t)(void *,::foo *,::bar *);
};
// ALLOC
//
template<class D,class MT,class PT >
struct alloc_adapter_base {
private:
typedef typename MT::module_t module_t;
protected:
static void * go() {
plugin_data<module_t> * p;
try {
std::cerr << "init::go" << std::endl;
p=new plugin_data<module_t>();
} catch(std::exception & ex) {
std::cerr << "FAILED TO ALLOC" << std::endl;
return NULL;
}
return p;
}
private:
alloc_adapter_base();
public:
static const typename PT::alloc_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::alloc_fptr_t alloc_adapter_base<D,MT,PT>::ptr = D::go;
template< class M, class MT = module_traits<M>, class PT =
plugin_traits< ::plugin > >
struct alloc_adapter : public alloc_adapter_base<
alloc_adapter<M,MT,PT>,MT,PT> {
};
// FREE
//
template<class D,class MT,class PT >
struct free_adapter_base {
private:
typedef typename MT::module_t module_t;
protected:
static void go( void *p ) {
plugin_data<module_t> *pd = (plugin_data<module_t> *)p;
delete pd;
}
private:
free_adapter_base();
public:
static const typename PT::free_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::free_fptr_t free_adapter_base<D,MT,PT>::ptr = D::go;
template< class M, class MT = module_traits<M>, class PT =
plugin_traits< ::plugin > >
struct free_adapter : public free_adapter_base< free_adapter<M,MT,PT>,MT,PT>
{
};
// FOO
//
template<class D,class MT,class PT,bool B>
struct foo_adapter_base;
template<class D,class MT,class PT>
struct foo_adapter_base<D,MT,PT,true> {
private:
typedef typename MT::module_t module_t;
protected:
static int go( void *p, ::foo * f ) {
plugin_data<module_t> *pd = (plugin_data<module_t> *)p;
Foo foo(f);
return (pd->module.*D::mfptr)(foo);
}
private:
foo_adapter_base();
public:
static const typename PT::foo_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::foo_fptr_t foo_adapter_base<D,MT,PT,true>::ptr = D::go;
template<class D, class MT,class PT>
struct foo_adapter_base<D,MT,PT,false> {
static const typename PT::foo_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::foo_fptr_t foo_adapter_base<D,MT,PT,false>::ptr=NULL;
// DO FOO1
//
template<class M,class MT=module_traits<M>,class PT=plugin_traits< ::plugin
,bool B=MT::has_function::do_foo1>
struct do_foo1_adapter : public foo_adapter_base<
do_foo1_adapter<M,MT,PT,B>,MT,PT,B > {
};
template<class M,class MT,class PT>
struct do_foo1_adapter<M,MT,PT,true> : public foo_adapter_base<
do_foo1_adapter<M,MT,PT,true>, MT,PT,true > {
static typename MT::foo_fptr_t mfptr;
};
template<class M,class MT,class PT>
typename MT::foo_fptr_t do_foo1_adapter<M,MT,PT,true>::mfptr = &M::do_foo1;
// DO FOO2
//
template<class M,class MT=module_traits<M>,class PT=plugin_traits< ::plugin
,bool B=MT::has_function::do_foo2>
struct do_foo2_adapter : public foo_adapter_base<
do_foo2_adapter<M,MT,PT,B>,MT,PT,B > {
};
template<class M,class MT,class PT>
struct do_foo2_adapter<M,MT,PT,true> : public foo_adapter_base<
do_foo2_adapter<M,MT,PT,true>, MT,PT,true > {
private:
typedef foo_adapter_base< do_foo2_adapter<M,MT,PT,true>, MT,PT,true >
super;
private:
static int go( void *p,::foo *f) {
std::cerr << " >> Custom do_foo2_adapter" << std::endl;
int i = super::go(p,f);
std::cerr << " << Custom do_foo2_adapter" << std::endl;
return i;
}
static typename MT::foo_fptr_t mfptr;
friend class foo_adapter_base< do_foo2_adapter<M,MT,PT,true>, MT,PT,true
;
};
template<class M,class MT,class PT>
typename MT::foo_fptr_t do_foo2_adapter<M,MT,PT,true>::mfptr = &M::do_foo2;
// FOOBAR
//
template<class D,class MT,class PT,bool B>
struct foobar_adapter_base;
template<class D,class MT,class PT>
struct foobar_adapter_base<D,MT,PT,true> {
private:
typedef typename MT::module_t module_t;
protected:
static int go( void *p, ::foo * f, ::bar *b ) {
plugin_data<module_t> *pd = (plugin_data<module_t> *)p;
Foo foo(f);
Bar bar(f);
return (pd->module.*D::mfptr)(foo,bar);
}
private:
foobar_adapter_base();
public:
static const typename PT::foobar_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::foobar_fptr_t foobar_adapter_base<D,MT,PT,true>::ptr =
D::go;
template<class D, class MT,class PT>
struct foobar_adapter_base<D,MT,PT,false> {
static const typename PT::foobar_fptr_t ptr;
};
template<class D,class MT,class PT>
const typename PT::foobar_fptr_t
foobar_adapter_base<D,MT,PT,false>::ptr=NULL;
// DO FOO BAR
//
template<class M, class MT=module_traits<M>, class
PT=plugin_traits< ::plugin >, bool B=MT::has_function::do_foobar >
struct do_foobar_adapter : public foobar_adapter_base<
do_foobar_adapter<M,MT,PT,B>,MT,PT,B> {
};
template<class M, class MT, class PT >
struct do_foobar_adapter<M,MT,PT,true> : public foobar_adapter_base<
do_foobar_adapter<M,MT,PT,true>,MT,PT,true> {
static typename MT::foobar_fptr_t mfptr;
};
template<class M, class MT, class PT >
typename MT::foobar_fptr_t do_foobar_adapter<M,MT,PT,true>::mfptr =
&M::do_foobar;
// PLUGIN SETUP
template<class M>
void plugin_setup(plugin * p) {
p->alloc = alloc_adapter<M>::ptr;
p->free = free_adapter<M>::ptr;
p->do_foo1 = do_foo1_adapter<M>::ptr;
p->do_foo2 = do_foo2_adapter<M>::ptr;
p->do_foobar = do_foobar_adapter<M>::ptr;
};
extern "C" {
int main() {
struct plugin p;
struct foo f;
struct bar b;
plugin_setup<my_module>(&p);
void *pd;
if( (pd = p.alloc())!=NULL) {
if(p.do_foo1!=NULL) {
(*p.do_foo1)(pd,&f);
} else{
printf("Skipping do_foo1\n");
}
if(p.do_foo2!=NULL) {
(*p.do_foo2)(pd,&f);
} else{
printf("Skipping do_foo2\n");
}
if(p.do_foobar!=NULL) {
(*p.do_foobar)(pd,&f,&b);
} else{
printf("Skipping do_foobar\n");
}
p.free(pd);
}
}
};
</snip>
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]