Libzypp/Design/Binary

From openSUSE

Binary Compatibility in Libraries

For stable and released libraries, it is very important to try to preserve binary compatibility across major revisions of the code. Library developers should try not to alienate users by making frequent binary incompatible changes on production libraries.

A library typically exports a number of interfaces. These include routine names, the signatures or prototypes of these routines, global variables, structures, structure fields (both the types and the meaning of these), the meaning of enumeration values, and the semantics of the files that are created by the library. To keep binary compatibility means that these interfaces will not be changed. You can add new interfaces without breaking binary compatibility, but you cannot change the existing ones, since old programs will no longer run correctly.

This section includes a number of hints on how you can make your library code binary compatible with old versions of itself.

Keeping binary compatibility means that programs and object files that were compiled against a previous version of the code will continue to work without recompiling if the library is replaced.

A nice explanation of Binary Compatibility can be found in the KDE project:

http://developer.kde.org/documentation/other/binarycompatibility.html

The main strategy of keeping Binary Compatibility would be to handle data members and virtual functions in an own class which is called <classname>Impl. The access to these data members/virtual functions will be managed via a pointer which is defined in the <classname> class.

An simple example is the Dependencies class to show the machanism:

/*---------------------------------------------------------------------\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\---------------------------------------------------------------------*/
/** \file	zypp/Dependencies.h
*
*/
#ifndef ZYPP_DEPENDENCIES_H
#define ZYPP_DEPENDENCIES_H

#include <iosfwd>

#include "zypp/base/PtrTypes.h"

#include "zypp/CapSetFwd.h"

///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
namespace detail
{ /////////////////////////////////////////////////////////////////
DEFINE_PTR_TYPE(DependenciesImpl)
/////////////////////////////////////////////////////////////////
} // namespace detail
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//
//	CLASS NAME : Dependencies
//
/** */
class Dependencies
{
public:
/** Default ctor */
Dependencies();
/** Factory ctor */
explicit
Dependencies( detail::DependenciesImpl_Ptr impl_r );
/** Dtor */
~Dependencies();

public:
/**  */
const CapSet & provides() const;
/**  */
const CapSet & prerequires() const;
/**  */
const CapSet & requires() const;
/**  */
const CapSet & conflicts() const;
/**  */
const CapSet & obsoletes() const;
/**  */
const CapSet & recommends() const;
/**  */
const CapSet & suggests() const;
/**  */
const CapSet & freshens() const;

/**  */
void setProvides( const CapSet & val_r );
/**  */
void setPrerequires( const CapSet & val_r );
/**  */
void setRequires( const CapSet & val_r );
/**  */
void setConflicts( const CapSet & val_r );
/**  */
void setObsoletes( const CapSet & val_r );
/**  */
void setRecommends( const CapSet & val_r );
/**  */
void setSuggests( const CapSet & val_r );
/**  */
void setFreshens( const CapSet & val_r );

private:
/** Pointer to implementation */
detail::DependenciesImpl_Ptr _pimpl;
public:
/** Avoid a bunch of friend decl. */
detail::DependenciesImpl_constPtr sayFriend() const;
};
///////////////////////////////////////////////////////////////////

/** \relates Dependencies Stream output */
extern std::ostream & operator<<( std::ostream & str, const Dependencies & obj );

/////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////
#endif // ZYPP_DEPENDENCIES_H

We see that the Dependencies class has a pointer to the DependenciesImplclass called _pimpl. With this pointer the Dependencies class access to the member variables of DependenciesImpl. So each upgrading will be Biniary Compatible in the future:

/*---------------------------------------------------------------------\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\---------------------------------------------------------------------*/
/** \file	zypp/Dependencies.cc
*
*/
#include <iostream>

#include "zypp/base/ReferenceCounted.h"
#include "zypp/base/NonCopyable.h"
#include "zypp/Dependencies.h"
#include "zypp/CapSet.h"

using namespace std;

///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
namespace detail
{ /////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//
//	CLASS NAME : DependenciesImpl
//
/** Dependencies implementation */
struct DependenciesImpl : public base::ReferenceCounted, private base::NonCopyable
{
/**  */
CapSet _provides;
/**  */
CapSet _prerequires;
/**  */
CapSet _requires;
/**  */
CapSet _conflicts;
/**  */
CapSet _obsoletes;
/**  */
CapSet _recommends;
/**  */
CapSet _suggests;
/**  */
CapSet _freshens;

static DependenciesImpl_Ptr nodeps()
{
if ( !_nodeps ) { _nodeps = new DependenciesImpl; _nodeps->ref(); }
return _nodeps;
}

private:
static DependenciesImpl_Ptr _nodeps;
};
///////////////////////////////////////////////////////////////////
IMPL_PTR_TYPE(DependenciesImpl)
DependenciesImpl_Ptr DependenciesImpl::_nodeps;
///////////////////////////////////////////////////////////////////

/** \relates DependenciesImpl Stream output */
inline std::ostream & operator<<( std::ostream & str, const DependenciesImpl & obj )
{
str << "PROVIDES:" << endl << obj._provides;
str << "PREREQUIRES:" << endl << obj._prerequires;
str << "REQUIRES:" << endl << obj._requires;
str << "CONFLICTS:" << endl << obj._conflicts;
str << "OBSOLETES:" << endl << obj._obsoletes;
str << "RECOMMENDS:" << endl << obj._recommends;
str << "SUGGESTS:" << endl << obj._suggests;
str << "FRESHENS:" << endl << obj._freshens;
return str;
}

/////////////////////////////////////////////////////////////////
} // namespace detail
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//
//	CLASS NAME : Dependencies
//
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
//
//	METHOD NAME : Dependencies::Dependencies
//	METHOD TYPE : Ctor
//
Dependencies::Dependencies()
: _pimpl( detail::DependenciesImpl::nodeps() )
{}

///////////////////////////////////////////////////////////////////
//
//	METHOD NAME : Dependencies::Dependencies
//	METHOD TYPE : Ctor
//
Dependencies::Dependencies( detail::DependenciesImpl_Ptr impl_r )
: _pimpl( impl_r ? impl_r : detail::DependenciesImpl::nodeps() )
{}

///////////////////////////////////////////////////////////////////
//
//	METHOD NAME : Dependencies::~Dependencies
//	METHOD TYPE : Dtor
//
Dependencies::~Dependencies()
{}

///////////////////////////////////////////////////////////////////
//
//	METHOD NAME : Dependencies::sayFriend
//	METHOD TYPE : detail::DependenciesImplconstPtr
//
detail::DependenciesImpl_constPtr Dependencies::sayFriend() const
{ return _pimpl; }

const CapSet & Dependencies::provides() const
{ return _pimpl->_provides; }

const CapSet & Dependencies::prerequires() const
{ return _pimpl->_prerequires; }

const CapSet & Dependencies::requires() const
{ return _pimpl->_requires; }

const CapSet & Dependencies::conflicts() const
{ return _pimpl->_conflicts; }

const CapSet & Dependencies::obsoletes() const
{ return _pimpl->_obsoletes; }

const CapSet & Dependencies::recommends() const
{ return _pimpl->_recommends; }

const CapSet & Dependencies::suggests() const
{ return _pimpl->_suggests; }

const CapSet & Dependencies::freshens() const
{ return _pimpl->_freshens; }

void Dependencies::setProvides( const CapSet & val_r )
{ _pimpl->_provides = val_r; }

void Dependencies::setPrerequires( const CapSet & val_r )
{ _pimpl->_prerequires = val_r; }

void Dependencies::setRequires( const CapSet & val_r )
{ _pimpl->_requires = val_r; }

void Dependencies::setConflicts( const CapSet & val_r )
{ _pimpl->_conflicts = val_r; }

void Dependencies::setObsoletes( const CapSet & val_r )
{ _pimpl->_obsoletes = val_r; }

void Dependencies::setRecommends( const CapSet & val_r )
{ _pimpl->_recommends = val_r; }

void Dependencies::setSuggests( const CapSet & val_r )
{ _pimpl->_suggests = val_r; }

void Dependencies::setFreshens( const CapSet & val_r )
{ _pimpl->_freshens = val_r; }

/******************************************************************
**
**	FUNCTION NAME : operator<<
**	FUNCTION TYPE : std::ostream &
*/
std::ostream & operator<<( std::ostream & str, const Dependencies & obj )
{
return str << *obj.sayFriend();
}

/////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////

This strategy must be used for virtual functions too.

A little bit more complex is the definition of the class Resolvable and all other classes which are derived from it. But the concept is the same. The complete description can be found here:Binary Compatibility

The main difference of the Resolvable class and "normal" classes would be the creation of an instant. This must be mananaged by an own mechanism which is described in this chapter:FAQ


Last edit in Trac '12/06/05 12:09:08' by 'schubi'


Last edit in Trac '12/06/05 12:09:08' by 'schubi'


Last edit in Trac '12/06/05 12:09:08' by 'schubi'


Last edit in Trac '12/06/05 12:09:08' by 'schubi'


Last edit in Trac '12/06/05 12:09:08' by 'schubi'