From: Oncaphillis on
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! ]

From: Le Chaud Lapin on
On Jun 30, 9:58 am, Oncaphillis <oncaphil...(a)snafu.de> wrote:
> Hello,
>
> I'm working on an adapter for a C plugin API.

Why? What is it good for? :)

-Le Chaud Lapin-


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Oncaphillis on
>> I'm working on an adapter for a C plugin API.
>
> Why? What is it good for? :)
>

Well it irons your shirts, feeds your kids, walks the
dog and pays the mortgage :-)

It's just a C-Style plugin system where each time a new
plugin is meant to be written a couple of function pointers
have to be initialized to NULL or a valid function pointer.
The whole code is very C++-stylish already. They have
structs mimicking a std::string and things that look
like a streambuf. So I'm covering this all under a
C++-layer.

So e.g. something like

int_plugin(*p) {
p->alloc = my_alloc;
p->free = my_free;

p->do_foo1 = NULL;
p->do_foo2 = my_do_foo2;
...
...
...
}

becomes

int_plugin<M>(*p) {
p->alloc = module_adapter<M>::alloc;
p->free = module_adapter<M>::free;
p->do_foo1 = module_adapter<M>::do_foo1;
...
...
}

Notice that the second function is module independent
"do_foo1" will become NULL whenever the module class
doesn't declare a do_foo1 method. Whereas the first
approach has to be rewritten whenever the module interface
changes. This goes down to the module_traits<M>::has_function::do_xyz
enum. In my example code these values are set explicitly, but
I already check via SFINAE if the corresponding method exists.

Hope that explains why

O.




--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Le Chaud Lapin on
On Jul 2, 12:10 pm, Oncaphillis <oncaphil...(a)snafu.de> wrote:
> >> I'm working on an adapter for a C plugin API.
>
> > Why? What is it good for? :)
>
> Well it irons your shirts, feeds your kids, walks the
> dog and pays the mortgage :-)
>
> It's just a C-Style plugin system where each time a new
> plugin is meant to be written a couple of function pointers
> have to be initialized to NULL or a valid function pointer.
> The whole code is very C++-stylish already. They have
> structs mimicking a std::string and things that look
> like a streambuf. So I'm covering this all under a
> C++-layer.
>
> So e.g. something like
>
> int_plugin(*p) {
> p->alloc = my_alloc;
> p->free = my_free;
>
> p->do_foo1 = NULL;
> p->do_foo2 = my_do_foo2;
> ...
> ...
> ...
>
> }
>
> becomes
>
> int_plugin<M>(*p) {
> p->alloc = module_adapter<M>::alloc;
> p->free = module_adapter<M>::free;
> p->do_foo1 = module_adapter<M>::do_foo1;
> ...
> ...
>
> }
>
> Notice that the second function is module independent
> "do_foo1" will become NULL whenever the module class
> doesn't declare a do_foo1 method. Whereas the first
> approach has to be rewritten whenever the module interface
> changes. This goes down to the module_traits<M>::has_function::do_xyz
> enum. In my example code these values are set explicitly, but
> I already check via SFINAE if the corresponding method exists.
>
> Hope that explains why

First, you should know, it took me a while to figure out the context,
meaning, it might help, while describing your facility, that you
specify an overall architecture. It is not clear [to me at least]
whether the plug-in thing was written in C++, and the other thing was C
++, or vice-versa. Maybe a one-line diagram showing the executable
pieces in Windows or Linux:

[executable thing]---[executable thing]

Which of these ^^^^ is the plug-in, and which language are these
things written in?

Also, but asking what is this good for...yes, we all know the syntax
of C++ and that amusing tricks can be done with templates and
pointers. :) But surely you have a motivation beyond that! How about
a real example? That's what I meant when asking what it is good for.

Just curious,

-Le Chaud Lapin-



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Oncaphillis on
> First, you should know, it took me a while to figure out the context,
> meaning, it might help, while describing your facility, that you
> specify an overall architecture. It is not clear [to me at least]
> whether the plug-in thing was written in C++, and the other thing was C
> ++, or vice-versa. Maybe a one-line diagram showing the executable
> pieces in Windows or Linux:

Sorry for that. The original plugin thingy is written in C and it meant to
stay that way.

> Which of these ^^^^ is the plug-in, and which language are these
> things written in?
>
> Also, but asking what is this good for...yes, we all know the syntax
> of C++ and that amusing tricks can be done with templates and
> pointers. :) But surely you have a motivation beyond that! How about
> a real example? That's what I meant when asking what it is good for.

The real example of what I have in mind actually is the

init_plugin<M>( ::plugin )

line. At least for this part of my API. There's already a wealth of plugins
around for the system with a lot of redundant code lines, like setting
up the plugin struct. Another part not mentioned here is the handling
of these streambuf like C-struct with a very complex read/write syntax
which is screaming for being converted into a basic_iostream.

The point is that I'm trying to eliminate the complexity in the plugin code
by raising the complexity in the library code, which is supposed to stay
hidden for a plugin developers.

Thank you for your reply

O.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]