$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Stephan Diederich (stephan.diederich_at_[hidden])
Date: 2007-07-01 20:56:29
On 6/29/07, Jeremy Pack <rostovpack_at_[hidden]> wrote:
> On 6/29/07, Stephan Diederich <stephan.diederich_at_[hidden]> wrote:
> > On 5/29/07, Jeremy Pack <rostovpack_at_[hidden]> 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
> 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