|  | Comment on my adapter concept |  | |
| | | Oncaphillis |  |
| Posted: Mon Jun 30, 2008 2:58 pm Post subject: 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
| Quote: | ,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
| Quote: | ,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
| Quote: | ,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 LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Le Chaud Lapin |  |
| Posted: Wed Jul 02, 2008 9:33 am Post subject: Re: Comment on my adapter concept |  |
On Jun 30, 9:58 am, Oncaphillis <oncaphil...@snafu.de> wrote:
| Quote: | Hello,
I'm working on an adapter for a C plugin API.
|
Why? What is it good for? :)
-Le Chaud Lapin-
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Oncaphillis |  |
| Posted: Wed Jul 02, 2008 5:10 pm Post subject: Re: Comment on my adapter concept |  |
| |  | |
| Quote: | 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 LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Le Chaud Lapin |  |
| Posted: Thu Jul 03, 2008 9:28 am Post subject: Re: Comment on my adapter concept |  |
| |  | |
On Jul 2, 12:10 pm, Oncaphillis <oncaphil...@snafu.de> wrote:
| Quote: | 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 LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Oncaphillis |  |
| Posted: Thu Jul 03, 2008 2:54 pm Post subject: Re: Comment on my adapter concept |  |
| |  | |
| Quote: | 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.
| Quote: | 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 LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Thomas Richter |  |
| Posted: Sat Jul 05, 2008 10:58 am Post subject: Re: Comment on my adapter concept |  |
| |  | |
Oncaphillis schrieb:
| Quote: | 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.
|
But again, it seems to me like a solved problem. All operating systems I'm aware of offer something like this under the concept "shared libraries", and the resolution of the "plugins" is left to an operating system part known as dynamic linker. For Linux, "dlopen" is your friend, and I'm sure win also offers an interface for handling dll's.
Besides, what's the relation of this to the C++ language which would make your question on-topic?
So long, Thomas
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Oncaphillis |  |
| Posted: Sat Jul 05, 2008 10:54 pm Post subject: Re: Comment on my adapter concept |  |
| |  | |
| Quote: | But again, it seems to me like a solved problem. All operating systems I'm aware of offer something like this under the concept "shared libraries", and the resolution of the "plugins" is left to an operating system part known as dynamic linker. For Linux, "dlopen" is your friend, and I'm sure win also offers an interface for handling dll's.
Besides, what's the relation of this to the C++ language which would make your question on-topic?
|
The plugin is actually loaded via a call to dlopen. The style of plugin initialization (stuffing a struct with function pointers), is just the C-style legacy code I have to live with, and my primary idea was, as I already stated, to reduce redundant code in plugins with all these funny tricks (as Le Chaud Lapin) would have said. This approach has become quite complex, therefor my initial question if there might be a way of making it any leaner, while retaining the simplicity and flexibility I think it provides. May be it's valuable or not, but I hope I'm at leats not of-topic since I've used quite a lot of C++-specific code and my approach is heavily template based.
Thanks for your thoughts
O.
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
|
|