
Hi, I've some questions regarding boost.extension which I think is currently prepared to review by Mariano Gabriel Consoni as one of the SoC Projects. We want to use a plugin loading mechanism in one of our projects and have some ideas currently not included in the boost.extensions library. - Some kind of versioning of interfaces and implementations. The idea is to give an interface (whose implementation is loaded as an extension) a version, which can be later compared to the version of the loaded implementation. As one of boost.extension's goal is NOT to modify a class-header to make it loadable, this should possibly be an optional feature. Another option would be to give non versioned interfaces always version 0 or 1. - It would be nice, if the plugin-loader provides functionality to "switch" the interface of an instance of an implementation. IIRC, the zone object knows, which interfaces are implemented by a loadable class. (when the dll is loaded, the export-function registers all the possible interfaces of one class to the zone). With that information it should be possible to determine if a given instance of a loaded class can be casted to another interface. (I think that casting has to be done in the loaded extension, as this is the only place where the layout it known. ) Are there any plans to include above mentioned functionality in boost.extension? Thanks in advance, Stephan

Versioning can be done very simply using the Boost.Extension library. This will be described in greater detail in the documentation soon, but you can get a hint as to how it's done from the examples. For part 2, this can actually be done using RTTI - just dynamic_cast it. But yes, this could be done using a similar mechanism to the versioning above. A factory can contain arbitrary information about the class that it makes loadable. This information could be a version ID, a list of interfaces it supports, etc. So yes, this functionality is already available, and yes, it will (soon I hope) be documented. For your second question, it would be possible to provide an auxiliary class that could make this even simpler - we'll take it into consideration. Thanks! Jeremy On 5/9/07, Stephan Diederich <stephan.diederich@googlemail.com> wrote:
Hi,
I've some questions regarding boost.extension which I think is currently prepared to review by Mariano Gabriel Consoni as one of the SoC Projects.
We want to use a plugin loading mechanism in one of our projects and have some ideas currently not included in the boost.extensions library.
- Some kind of versioning of interfaces and implementations. The idea is to give an interface (whose implementation is loaded as an extension) a version, which can be later compared to the version of the loaded implementation. As one of boost.extension's goal is NOT to modify a class-header to make it loadable, this should possibly be an optional feature. Another option would be to give non versioned interfaces always version 0 or 1.
- It would be nice, if the plugin-loader provides functionality to "switch" the interface of an instance of an implementation. IIRC, the zone object knows, which interfaces are implemented by a loadable class. (when the dll is loaded, the export-function registers all the possible interfaces of one class to the zone). With that information it should be possible to determine if a given instance of a loaded class can be casted to another interface. (I think that casting has to be done in the loaded extension, as this is the only place where the layout it known. )
Are there any plans to include above mentioned functionality in boost.extension?
Thanks in advance, Stephan _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Hi Jeremy and Mariano, On 5/29/07, Jeremy Pack <rostovpack@gmail.com> wrote:
Versioning can be done very simply using the Boost.Extension library. This will be described in greater detail in the documentation soon, but you can get a hint as to how it's done from the examples.
Thanks. That helped. I'm doing exactly what you suggested, plus something additional I'd like to share. (The idea is actually taken from CrystalSpace). In the interface class I'm defining an method to retrieve the current version of the interface. This method gets an "always inline" attribute. When exporting the class through boost.extension, I'm calling that method to store the current Interface version in the Info-struct of boost.extension. Now I've a wrapper around the factory map, which compares the stored version in the plugin with the current version of the interface. That way, I can change the interface class and increase the version number without getting bad surprises.
For part 2, this can actually be done using RTTI - just dynamic_cast it.
Works, thanks! I thought it wouldn't be possible to dynamic_cast a type from another library, but it seems to work... Any ideas if this can cause problems? Another thing I recently discovered is some kind of class -> library mapping. This is especially useful for debugging purposes... Did you think about that? Thanks for your help! Cheers, Stephan

On 6/29/07, Stephan Diederich <stephan.diederich@googlemail.com> wrote:
Hi Jeremy and Mariano,
On 5/29/07, Jeremy Pack <rostovpack@gmail.com> wrote:
Versioning can be done very simply using the Boost.Extension library. This will be described in greater detail in the documentation soon, but you can get a hint as to how it's done from the examples.
Thanks. That helped. I'm doing exactly what you suggested, plus something additional I'd like to share. (The idea is actually taken from CrystalSpace). In the interface class I'm defining an method to retrieve the current version of the interface. This method gets an "always inline" attribute. When exporting the class through boost.extension, I'm calling that method to store the current Interface version in the Info-struct of boost.extension. Now I've a wrapper around the factory map, which compares the stored version in the plugin with the current version of the interface. That way, I can change the interface class and increase the version number without getting bad surprises.
That is a good idea - it certainly shouldn't be hard to add those extra classes (they'd be optional). Once you feel the code is ready, why don't you post some samples here. If it looks like it will fit well, it could certainly be worth adding. If you want us to try and write the code to do the same type of thing, we can try that as well - but it won't be done as soon.
For part 2, this can actually be done using RTTI - just dynamic_cast it.
Works, thanks! I thought it wouldn't be possible to dynamic_cast a type from another library, but it seems to work... Any ideas if this can cause problems?
If I recall correctly, there is the possibility of problems using an older (much older) version of Borland C++. I read about it on some Borland help site once while designing the library. I think I've written it in a way that will overcome even those problems - but we won't find out until we've run the unit tests on more machines. Thus far, we've found no issues on any compiler. Another thing I recently discovered is some kind of class -> library
mapping. This is especially useful for debugging purposes... Did you think about that?
This is one of the items on our "To Do Someday" list - but we're not sure how soon we'll get time to work on it. A few other people have expressed interest in it as well, so we'll see what we can do. The initial version of the library (October 2006) actually did have this sort of mapping, but I haven't rewritten it for this latest implementation (which has changed substantially). Jeremy

On 6/29/07, Jeremy Pack <rostovpack@gmail.com> wrote:
On 5/29/07, Jeremy Pack <rostovpack@gmail.com> wrote:
Versioning can be done very simply using the Boost.Extension library. [snip] In the interface class I'm defining an method to retrieve the current version of the interface. This method gets an "always inline" attribute. [snip] That is a good idea - it certainly shouldn't be hard to add those extra classes (they'd be optional). Once you feel the code is ready, why don't you
On 6/29/07, Stephan Diederich <stephan.diederich@googlemail.com> wrote: post some samples here. If it looks like it will fit well, it could certainly be worth adding. If you want us to try and write the code to do the same type of thing, we can try that as well - but it won't be done as soon.
Here's the plot: We need something to get the version compiled into the plugin: #ifdef WIN32 #define FORCE_INLINE __forceinline #else //gcc for now #define FORCE_INLINE __attribute__((always_inline)) #endif The idea is to have a major number for binary incompatibilities, and a minor version, if only a virtual method is added at the end of the interface. In the second case, the user of an older interface can still use a newer implementation. //first simple InterfaceVersion class class InterfaceVersion{ public: InterfaceVersion(unsigned short f_major, unsigned short f_minor): m_major(f_major), m_minor(f_minor){} bool isCompatible(const InterfaceVersion& fcr_other){ if(m_major == fcr_other.m_major && m_minor >= fcr_other.m_major) return true; else return false; } private: unsigned short m_major; unsigned short m_minor; }; //this is just an extractor for convenience template <class Interface> class InterfaceTraits{ static FORCE_INLINE InterfaceVersion getVersion(){ return Interface::InterfaceTraits::getVersion(); } }; //here we go with the interface. //That struct definition could also be done through a simple macro //(e.g. INTERFACE_VERSION(1,0)) struct MyInterface{ class InterfaceTraits{ static FORCE_INLINE InterfaceVersion getVersion(){ return InterfaceVersion(1,0); } }; virtual void doSomething() = 0; }; //somewhere in a cpp where we define and export the class: void inititalize_plugin(boost::extension::factory_map& z){ z.add<MyImplementation, MyInterface, InterfaceVersion>(InterfaceTraits<MyImplementation>::getVersion()); } One problem I see: is the getVersion() call always inlined? Now on the "client side" we have something like CheckedPluginloader loader; loader.addSingleLibrary("libmylib.so"); boost::shared_ptr<MyInterface> p = loader.createImplementation<MyInterface>(); //inside createImplemention the current version of MyInterface is compared to the version stored in //the factory_map like this: template <class Interface> CheckedPluginloader::createImplementation(){ typedef std::list<boost::extensions::factory<T, InterfaceVersion> > tFactoryList; tFactoryList & factory_list = m_factory_map.get<T, InterfaceVersion>(); //try to create one of them if there are several for (typename tFactoryList::iterator current_factory = factory_list.begin(); current_factory != factory_list.end(); ++current_factory) { if(current_factory->get_info().isCompatible(T::InterfaceTraits::getVersion())){ boost::shared_ptr<T> p(current_factory->create()); if(p) return p; } } return boost::shared_ptr<T>(); }
For part 2, this can actually be done using RTTI - just dynamic_cast it.
Works, thanks! I thought it wouldn't be possible to dynamic_cast a type from another library, but it seems to work... Any ideas if this can cause problems?
If I recall correctly, there is the possibility of problems using an older (much older) version of Borland C++. I read about it on some Borland help site once while designing the library. I think I've written it in a way that will overcome even those problems - but we won't find out until we've run the unit tests on more machines. Thus far, we've found no issues on any compiler. That sounds comfortable. I read about problems with any_cast where typeid is used to compare the type in any and the type to cast to (http://tinyurl.com/38ztc4), but actually I couldn't reproduce them with VC8, darwin and gcc.
I think the above solution with InterfaceVersions could also be used with the dynamic_cast. Simple dynamic casting (without help of boost::extension) can also lead to problems where the implementation is outdated. Here the factory_map can be used: It stores all of the interfaces (and versions) a class implements and it can check against the stored version when "switching" the interface. Thanks for thinking about this. Cheers, Stephan

On 7/2/07, Stephan Diederich <stephan.diederich@googlemail.com> wrote:
On 6/29/07, Jeremy Pack <rostovpack@gmail.com> wrote:
On 6/29/07, Stephan Diederich <stephan.diederich@googlemail.com> wrote:
On 5/29/07, Jeremy Pack <rostovpack@gmail.com> wrote:
[snip]
For part 2, this can actually be done using RTTI - just dynamic_cast it.
Works, thanks! I thought it wouldn't be possible to dynamic_cast a type from another library, but it seems to work... Any ideas if this can cause problems?
If I recall correctly, there is the possibility of problems using an older (much older) version of Borland C++. I read about it on some Borland help site once while designing the library. I think I've written it in a way that will overcome even those problems - but we won't find out until we've run the unit tests on more machines. Thus far, we've found no issues on any compiler. That sounds comfortable. I read about problems with any_cast where typeid is used to compare the type in any and the type to cast to (http://tinyurl.com/38ztc4), but actually I couldn't reproduce them with VC8, darwin and gcc.
Hi Jeremy, today I was able to reproduce them. I had to switch from cmake to qmake (on linux, gcc-4.1), and with that my compiler options switched, too. I think I figured out two things, to make the above work. First, the executable needs to be linked with "-rdynamic". With that flag enabled, dlopen merges the weak symbols from the loaded library with the ones from the executable. (including rtti-information). Second, the call to dlopen a library should be passed RTLD_GLOBAL. With that flag enabled, symbols from this library are candidates for merging (which again seems to remove doubled rtti symbols). Only with that set I was able to dynamic_cast "between" shared libraries. Maybe its worth mentioning that in the docs of boost::extension. Do you have an idea how this is done on windows? Cheers, Stephan

Stephan Diederich said: (by the date of Wed, 11 Jul 2007 21:52:32 +0200)
First, the executable needs to be linked with "-rdynamic". With that flag
I confirm that. To write my own plugin loading library on linux I had to use -rdynamic flag almost everywhere (for plugins too!).
Second, the call to dlopen a library should be passed RTLD_GLOBAL. With that
However i don't need to do anything with regard to RTLD_GLOBAL -- Janek Kozicki |

On 7/11/07, Janek Kozicki <janek_listy@wp.pl> wrote:
Stephan Diederich said: (by the date of Wed, 11 Jul 2007 21:52:32 +0200)
First, the executable needs to be linked with "-rdynamic". With that flag
I confirm that. To write my own plugin loading library on linux I had to use -rdynamic flag almost everywhere (for plugins too!).
Second, the call to dlopen a library should be passed RTLD_GLOBAL. With that
However i don't need to do anything with regard to RTLD_GLOBAL
I gave it a try with another testcase. It is attached to this mail, but is a bit clumsy. There are 3 directories: src (with the main app), lib1 and lib2. (the tests are handmade) The first test is to create a boost::any in lib1 and try to extract it in main. This works, as long as the executable is linked with -rdynamic. (the lib doesn't need it). dlopen flags don't matter, too. The second testcase is to create a boost::any in lib1, and try to extract it in lib2. This works, as long as the first lib is opened with RTLD_GLOBAL flag. If the executable is linked _with_ -rdynamic all 3 modules now have the same typeinfos. But this testcase even works _without_ the -rdynamic flag. Then lib1 and lib2 share the same typeinfos, but the executable has a different one (and cannot do anything with the boost::any from the libs) HTH, Stephan

Thanks! Sorry I didn't respond sooner - I was out of town. I will attempt to document the items that you mention - and put it into our own test cases. It may also help to overcome one minor performance bottleneck. Jeremy On 7/12/07, Stephan Diederich <stephan.diederich@googlemail.com> wrote:
On 7/11/07, Janek Kozicki <janek_listy@wp.pl> wrote:
Stephan Diederich said: (by the date of Wed, 11 Jul 2007 21:52:32 +0200)
First, the executable needs to be linked with "-rdynamic". With that flag
I confirm that. To write my own plugin loading library on linux I had to use -rdynamic flag almost everywhere (for plugins too!).
Second, the call to dlopen a library should be passed RTLD_GLOBAL. With that
However i don't need to do anything with regard to RTLD_GLOBAL
I gave it a try with another testcase. It is attached to this mail, but is a bit clumsy. There are 3 directories: src (with the main app), lib1 and lib2. (the tests are handmade)
The first test is to create a boost::any in lib1 and try to extract it in main. This works, as long as the executable is linked with -rdynamic. (the lib doesn't need it). dlopen flags don't matter, too.
The second testcase is to create a boost::any in lib1, and try to extract it in lib2. This works, as long as the first lib is opened with RTLD_GLOBAL flag. If the executable is linked _with_ -rdynamic all 3 modules now have the same typeinfos. But this testcase even works _without_ the -rdynamic flag. Then lib1 and lib2 share the same typeinfos, but the executable has a different one (and cannot do anything with the boost::any from the libs)
HTH, Stephan
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman /listinfo.cgi/boost
participants (3)
-
Janek Kozicki
-
Jeremy Pack
-
Stephan Diederich