|
From: Oncaphillis on 29 Jun 2008 23:58 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 1 Jul 2008 18:33 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 2 Jul 2008 02:10 >> 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 2 Jul 2008 18:28 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 2 Jul 2008 23:54 > 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! ]
|
Next
|
Last
Pages: 1 2 Prev: Access internal methods in compounds? Next: ambiguous call to overloaded function |