Boost logo

Boost :

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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk