SlideShare a Scribd company logo
Interface Versioning in C++
Steve Love
ACCU London October 2010
Interface Versioning in C++
The problem
One library, many clients
Recompilation causes redeployment
Synchronising release cycles is painful
Quarantined release environments are prone to error
Copyright (c) Steve Love 2010 Interface Versioning in C++
The goal
Design a library that:
1 Can grow as requirements arise
2 Doesn’t force redeployment of clients every
upgrade
3 Is easy to use without undue effort
Dependency Management
Requires a loosening of the coupling between the
library and its clients
Copyright (c) Steve Love 2010 Interface Versioning in C++
Cause and effect
Changes to a class cause clients to recompile when:
Public member functions change
Base class list is changed
Data members change
Protected and Private member functions change
#pragma once
namespace inventory
{
class part
{
public:
unsigned id() const;
private:
unsigned num;
};
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Solution
Hide changes behind an interface
#pragma once
namespace inventory
{
class part
{
public:
virtual ~part();
virtual int id() const = 0;
};
part * create_part();
}
#include "part.h"
namespace
{
class realpart : public inventory::part
{
public:
realpart();
int id() const;
private:
int num;
};
}
namespace inventory
{
part * create_part()
{
return new realpart;
}
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Indirectly
Interfaces are only part of the solution...
Public member functions change
Base class list is changed
Data members change
Protected and Private member functions change
Copyright (c) Steve Love 2010 Interface Versioning in C++
A false hope
Necessity and sufficiency
Interfaces are vital
...but not sufficient on their own
Padding interfaces at the bottom
...is common.
But wrong.
Copyright (c) Steve Love 2010 Interface Versioning in C++
Why not?
class properties
{
public:
virtual int integer() const { return 0; }
virtual double floatingpoint() const { return 0; }
};
int main()
{
properties p;
}
cl /EHsc /FAs test.cpp
??_7properties@@6B@ DD FLAT:??_R4properties@@6B@ ; properties::‘vftable’
DD FLAT:?integer@properties@@UBEHXZ
DD FLAT:?floatingpoint@properties@@UAENXZ
; Function compile flags: /Odtp
Copyright (c) Steve Love 2010 Interface Versioning in C++
Changed interface
class properties
{
public:
virtual int integer() const { return 0; }
virtual double floatingpoint() const { return 0; }
virtual void integer( int ) { }
virtual void floatingpoint( double ) { }
};
int main()
{
properties p;
}
cl /EHsc /FAs test.cpp
Copyright (c) Steve Love 2010 Interface Versioning in C++
The result
Old:
??_7properties@@6B@ DD FLAT:??_R4properties@@6B@ ; properties::‘vftable’
DD FLAT:?integer@properties@@UBEHXZ
DD FLAT:?floatingpoint@properties@@UAENXZ
; Function compile flags: /Odtp
New:
??_7properties@@6B@ DD FLAT:??_R4properties@@6B@ ; properties::‘vftable’
DD FLAT:?integer@properties@@UAEXH@Z
DD FLAT:?integer@properties@@UBEHXZ
DD FLAT:?floatingpoint@properties@@UAEXN@Z
DD FLAT:?floatingpoint@properties@@UAENXZ
; Function compile flags: /Odtp
Copyright (c) Steve Love 2010 Interface Versioning in C++
The true path
Old new thing
Adding a method to an interface is bad.
Adding a new interface to a library is benign.
Copyright (c) Steve Love 2010 Interface Versioning in C++
Simple interfaces
#pragma once
#include <string>
namespace inventory
{
class part
{
public:
virtual ~part();
virtual unsigned id() const = 0;
virtual const std::string & name()
const = 0;
};
part * create_part();
}
#include <part.h>
#include <iostream>
#include <memory>
// Also link to inventory.lib
int main()
{
using namespace std;
using namespace inventory;
unique_ptr< part > p( create_part() );
cout << p->id() << endl;
cout << p->name() << endl;
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Extended interface
#pragma once
#include <string>
namespace inventory
{
class part
{
public:
virtual ~part();
virtual unsigned id() const = 0;
virtual const std::string & name()
const = 0;
};
class part_v2 : public part
{
public:
using part::id;
using part::name;
virtual void id( unsigned val ) = 0;
virtual void name( const std::string
& val ) = 0;
};
part * create_part();
}
#include <part.h>
#include <iostream>
#include <memory>
// Also link to inventory.lib
int main()
{
using namespace std;
using namespace inventory;
unique_ptr< part_v2 > p( dynamic_cast<
part_v2* >( create_part() ) );
p->id( 100 );
p->name( "wingnut" );
cout << p->id() << endl;
cout << p->name() << endl;
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Implementing part_v2
#include "part.h"
namespace
{
class realpart : public inventory::part_v2
{
public:
realpart();
unsigned id() const;
const std::string & name() const;
void id( unsigned val );
void name( const std::string & val );
private:
unsigned num;
std::string namestr;
};
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Wrong turn!
#include <string>
class part
{
public:
virtual int id() const = 0;
virtual const std::string & name() const = 0;
};
class part_v2 : public part
{
public:
virtual void id( int val ) = 0;
virtual void name( const std::string & val ) = 0;
};
It’s not the interface that’s the problem...
Copyright (c) Steve Love 2010 Interface Versioning in C++
v1 and v2 side-by-side
...it’s the implementation.
class realpart : public part
{
public:
int id() const { return num; }
const std::string & name() const { return namestr; }
private:
int num;
std::string namestr;
};
class realpart_v2 : public part_v2
{
public:
int id() const { return num; }
const std::string & name() const { return namestr; }
void id( int val ) { }
void name( const std::string & val ) { }
private:
int num;
std::string namestr;
};
Copyright (c) Steve Love 2010 Interface Versioning in C++
More vtables
??_7realpart@@6B@ DD FLAT:??_R4realpart@@6B@ ; realpart::‘vftable’
DD FLAT:?id@realpart@@UBEHXZ
DD FLAT:?
name@realpart@@UBEABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ
; Function compile flags: /Odtp
??_7realpart_v2@@6B@ DD FLAT:??_R4realpart_v2@@6B@ ; realpart_v2::‘vftable’
DD FLAT:?id@realpart_v2@@UBEHXZ
DD FLAT:?
name@realpart_v2@@UBEABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ
DD FLAT:?id@realpart_v2@@UAEXH@Z
DD FLAT:?
name@realpart_v2@@UAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z
; Function compile flags: /Odtp
DANGER!
This will appear to work, but depends on the
implementation
Copyright (c) Steve Love 2010 Interface Versioning in C++
Split the implementations
#include "part.h"
namespace
{
class realpart : public inventory::part
{
public:
realpart();
unsigned id() const;
const std::string & name() const;
protected:
unsigned num;
std::string namestr;
};
class realpart_v2 : public inventory::part_v2, public
realpart
{
public:
using part::id;
using part::name;
void id( unsigned val );
void name( const std::string & val );
};
namespace inventory
{
part::~part()
{
}
part * create_part()
{
return new realpart_v2;
}
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Split the implementations
#include "part.h"
namespace
{
class realpart : public inventory::part
{
public:
realpart();
unsigned id() const;
const std::string & name() const;
protected:
unsigned num;
std::string namestr;
};
class realpart_v2 : public inventory::part_v2, public
realpart
{
public:
using part::id;
using part::name;
void id( unsigned val );
void name( const std::string & val );
};
namespace inventory
{
part::~part()
{
}
part * create_part()
{
return new realpart_v2;
}
}
...except...
it doesn’t compile!
Copyright (c) Steve Love 2010 Interface Versioning in C++
Ambiguity
Copyright (c) Steve Love 2010 Interface Versioning in C++
Resolved
#pragma once
#include <string>
namespace inventory
{
class part
{
};
class part_v2 : public virtual part
{
};
}
#include "part.h"
namespace
{
class realpart : public virtual inventory::part
{
};
class realpart_v2 : public virtual inventory::part_v2, public realpart
{
};
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Finishing polish
Names have power
namespace inventory
{
class part
{
};
class part_v2 : public virtual part
{
};
part * create_part();
}
The factory returns a part.
Changing this breaks the ABI (ODR).
Clients of part_v2 must now find all references to
“part”, and use the factory like this:
unique_ptr< part_v2 > p( dynamic_cast< part_v2* >( create_part() ) );
Copyright (c) Steve Love 2010 Interface Versioning in C++
Called by a common name
Class part becomes class part_v1
typedef part_v1 * part;
Copyright (c) Steve Love 2010 Interface Versioning in C++
Called by a common name
Class part becomes class part_v1
typedef part_v1 * part;
Or, even better....
#include "part_v1.h"
#include <memory>
namespace inventory
{
typedef part_v1 part_current;
typedef std::unique_ptr< part_current > part;
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
No more casts
namespace inventory
{
INVENTORY_LIB part_v1 * create_part_ptr();
template< typename type_version >
part create_part()
{
return part( static_cast< type_version * >( create_part_ptr() ) );
}
}
#include <part_factory.h>
#include <iostream>
int main()
{
using namespace std;
using namespace inventory;
part p = create_part< part_current >();
cout << p->number() << endl;
cout << p->name() << endl;
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Version up!
New interface
namespace inventory
{
class INVENTORY_LIB part_v2 : public virtual part_v1
{
};
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
The real thing
New implementation
#include <part_v2.h>
namespace inventory_impl
{
class realpart_v2 : public virtual inventory::part_v2, public realpart_v1
{
};
}
Update factory
namespace inventory
{
using namespace inventory_impl;
INVENTORY_LIB part_v1 * create_part_ptr()
{
return new realpart_v2;
}
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Status quo
Update the typedef
namespace inventory
{
typedef part_v2 part_current;
typedef std::unique_ptr< part_current > part;
}
The template factory stays the same
#include <part.h>
namespace inventory
{
INVENTORY_LIB part_v1 * create_part_ptr();
template< typename type_version >
part create_part()
{
return part( static_cast< type_version * >( create_part_ptr() ) );
}
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
The client
V1
#include <part_factory.h>
#include <iostream>
int main()
{
using namespace std;
using namespace inventory;
part p = create_part< part_current >();
cout << p->number() << endl;
cout << p->name() << endl;
}
v2
#include <part_factory.h>
#include <iostream>
int main()
{
using namespace std;
using namespace inventory;
part p = create_part< part_current >();
p->number( 100 );
p->name( "steve" );
cout << p->number() << endl;
cout << p->name() << endl;
}
Copyright (c) Steve Love 2010 Interface Versioning in C++
Dependency management
Code that has objects passed to it
(i.e. does not need to create objects)
should have no dependency on the factory.
Paramaterize from Above
and all that
Copyright (c) Steve Love 2010 Interface Versioning in C++
Divide and conquer
IINVENTORY.DLL
Headers for part_v1/v2
Implementation file for
part_v1
The part type and
part_current typedef
INVENTORY_IMPL.DLL
Headers and
implementation of
realpart
The factory
Copyright (c) Steve Love 2010 Interface Versioning in C++
In other words...
Copyright (c) Steve Love 2010 Interface Versioning in C++
Justifying the means
The means
An extensible, safe and conforming way of extending
interfaces without causing clients to redeploy.
The end
Loosening the coupling between the library and its
clients.
Copyright (c) Steve Love 2010 Interface Versioning in C++

More Related Content

PDF
Oop Presentation
PDF
Objective c beginner's guide
PDF
Deep Dive Java 17 Devoxx UK
PDF
The Future of Java: Records, Sealed Classes and Pattern Matching
PDF
С++ without new and delete
PPTX
Summary of C++17 features
PDF
The First C# Project Analyzed
PDF
C++aptitude questions and answers
Oop Presentation
Objective c beginner's guide
Deep Dive Java 17 Devoxx UK
The Future of Java: Records, Sealed Classes and Pattern Matching
С++ without new and delete
Summary of C++17 features
The First C# Project Analyzed
C++aptitude questions and answers

What's hot (20)

PDF
Analyzing FreeCAD's Source Code and Its "Sick" Dependencies
PPTX
NDC 2011, C++ 프로그래머를 위한 C#
PDF
Solid C++ by Example
PDF
Open source report writing tools for IBM i Vienna 2012
PPT
Log4 J
PPTX
Automatically Documenting Program Changes
PDF
It's complicated, but it doesn't have to be: a Dagger journey
PDF
Insecure coding in C (and C++)
PDF
3150 Chapter 2 Part 1
PPT
C# Variables and Operators
PDF
Java Full Throttle
PPTX
Java 8 features
PDF
Java Keeps Throttling Up!
PPTX
introduction of c langauge(I unit)
ODP
OOP in C - Before GObject (Chinese Version)
PDF
Cbse question-paper-computer-science-2009
PDF
OOP in C - Inherit (Chinese Version)
PDF
C++ idioms by example (Nov 2008)
PPTX
P/Invoke - Interoperability of C++ and C#
PDF
Qno1
 
Analyzing FreeCAD's Source Code and Its "Sick" Dependencies
NDC 2011, C++ 프로그래머를 위한 C#
Solid C++ by Example
Open source report writing tools for IBM i Vienna 2012
Log4 J
Automatically Documenting Program Changes
It's complicated, but it doesn't have to be: a Dagger journey
Insecure coding in C (and C++)
3150 Chapter 2 Part 1
C# Variables and Operators
Java Full Throttle
Java 8 features
Java Keeps Throttling Up!
introduction of c langauge(I unit)
OOP in C - Before GObject (Chinese Version)
Cbse question-paper-computer-science-2009
OOP in C - Inherit (Chinese Version)
C++ idioms by example (Nov 2008)
P/Invoke - Interoperability of C++ and C#
Qno1
 
Ad

Viewers also liked (10)

PDF
An Introduction To Palomino
PPTX
Martin Strýček - Ako začať s MongoDB
PDF
C++: Interface as Concept
PDF
Recruiting for diversity in tech
PPT
C++: inheritance, composition, polymorphism
PDF
Intro to C++ - language
PDF
Moved to Speakerdeck
PPT
C++: Constructor, Copy Constructor and Assignment operator
PPT
C++ Inheritance
An Introduction To Palomino
Martin Strýček - Ako začať s MongoDB
C++: Interface as Concept
Recruiting for diversity in tech
C++: inheritance, composition, polymorphism
Intro to C++ - language
Moved to Speakerdeck
C++: Constructor, Copy Constructor and Assignment operator
C++ Inheritance
Ad

Similar to C++ Interface Versioning (20)

PPT
Visual studio 2008
PDF
How to code to code less
PDF
1z0 804 exam-java se 7 programmer ii
PDF
Chapter27 polymorphism-virtual-function-abstract-class
PPT
C# Tutorial MSM_Murach chapter-14-slides
PPTX
CAP444-Unit-3-Polymorphism.pptx
PPT
Virtual Function and Polymorphism.ppt
PPTX
cppt-170218053903.pptxhjkjhjjjjjjjjjjjjjjj
PDF
Modern c++ (C++ 11/14)
PPTX
C++ Overview PPT
PDF
Cleaning your architecture with android architecture components
PPT
Oops lecture 1
PPTX
MS Day EPITA 2010: Visual Studio 2010 et Framework .NET 4.0
PPTX
2008 - TechDays PT: Building Software + Services with Volta
PDF
High performance web programming with C++14
PPT
Refactoring - improving the smell of your code
PPTX
C++ 11 Features
PDF
Kotlin Developer Starter in Android projects
Visual studio 2008
How to code to code less
1z0 804 exam-java se 7 programmer ii
Chapter27 polymorphism-virtual-function-abstract-class
C# Tutorial MSM_Murach chapter-14-slides
CAP444-Unit-3-Polymorphism.pptx
Virtual Function and Polymorphism.ppt
cppt-170218053903.pptxhjkjhjjjjjjjjjjjjjjj
Modern c++ (C++ 11/14)
C++ Overview PPT
Cleaning your architecture with android architecture components
Oops lecture 1
MS Day EPITA 2010: Visual Studio 2010 et Framework .NET 4.0
2008 - TechDays PT: Building Software + Services with Volta
High performance web programming with C++14
Refactoring - improving the smell of your code
C++ 11 Features
Kotlin Developer Starter in Android projects

More from Skills Matter (20)

PDF
5 things cucumber is bad at by Richard Lawrence
ODP
Patterns for slick database applications
PDF
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
ODP
Oscar reiken jr on our success at manheim
ODP
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
PDF
Cukeup nyc ian dees on elixir, erlang, and cucumberl
PDF
Cukeup nyc peter bell on getting started with cucumber.js
PDF
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
ODP
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
ODP
Progressive f# tutorials nyc don syme on keynote f# in the open source world
PDF
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
PPTX
Dmitry mozorov on code quotations code as-data for f#
PDF
A poet's guide_to_acceptance_testing
PDF
Russ miles-cloudfoundry-deep-dive
KEY
Serendipity-neo4j
PDF
Simon Peyton Jones: Managing parallelism
PDF
Plug 20110217
PDF
Lug presentation
PPT
I went to_a_communications_workshop_and_they_t
PDF
Plug saiku
5 things cucumber is bad at by Richard Lawrence
Patterns for slick database applications
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
Oscar reiken jr on our success at manheim
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
Cukeup nyc ian dees on elixir, erlang, and cucumberl
Cukeup nyc peter bell on getting started with cucumber.js
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
Progressive f# tutorials nyc don syme on keynote f# in the open source world
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
Dmitry mozorov on code quotations code as-data for f#
A poet's guide_to_acceptance_testing
Russ miles-cloudfoundry-deep-dive
Serendipity-neo4j
Simon Peyton Jones: Managing parallelism
Plug 20110217
Lug presentation
I went to_a_communications_workshop_and_they_t
Plug saiku

Recently uploaded (20)

PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Empathic Computing: Creating Shared Understanding
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
cuic standard and advanced reporting.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
Machine Learning_overview_presentation.pptx
PPTX
Big Data Technologies - Introduction.pptx
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPTX
Spectroscopy.pptx food analysis technology
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Encapsulation theory and applications.pdf
PPTX
Programs and apps: productivity, graphics, security and other tools
PPTX
Cloud computing and distributed systems.
“AI and Expert System Decision Support & Business Intelligence Systems”
Digital-Transformation-Roadmap-for-Companies.pptx
Diabetes mellitus diagnosis method based random forest with bat algorithm
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Empathic Computing: Creating Shared Understanding
Chapter 3 Spatial Domain Image Processing.pdf
Dropbox Q2 2025 Financial Results & Investor Presentation
cuic standard and advanced reporting.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Review of recent advances in non-invasive hemoglobin estimation
Reach Out and Touch Someone: Haptics and Empathic Computing
Machine Learning_overview_presentation.pptx
Big Data Technologies - Introduction.pptx
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Spectroscopy.pptx food analysis technology
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Encapsulation theory and applications.pdf
Programs and apps: productivity, graphics, security and other tools
Cloud computing and distributed systems.

C++ Interface Versioning

  • 1. Interface Versioning in C++ Steve Love ACCU London October 2010 Interface Versioning in C++
  • 3. One library, many clients Recompilation causes redeployment Synchronising release cycles is painful Quarantined release environments are prone to error Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 4. The goal Design a library that: 1 Can grow as requirements arise 2 Doesn’t force redeployment of clients every upgrade 3 Is easy to use without undue effort Dependency Management Requires a loosening of the coupling between the library and its clients Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 5. Cause and effect Changes to a class cause clients to recompile when: Public member functions change Base class list is changed Data members change Protected and Private member functions change #pragma once namespace inventory { class part { public: unsigned id() const; private: unsigned num; }; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 6. Solution Hide changes behind an interface #pragma once namespace inventory { class part { public: virtual ~part(); virtual int id() const = 0; }; part * create_part(); } #include "part.h" namespace { class realpart : public inventory::part { public: realpart(); int id() const; private: int num; }; } namespace inventory { part * create_part() { return new realpart; } } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 7. Indirectly Interfaces are only part of the solution... Public member functions change Base class list is changed Data members change Protected and Private member functions change Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 9. Necessity and sufficiency Interfaces are vital ...but not sufficient on their own Padding interfaces at the bottom ...is common. But wrong. Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 10. Why not? class properties { public: virtual int integer() const { return 0; } virtual double floatingpoint() const { return 0; } }; int main() { properties p; } cl /EHsc /FAs test.cpp ??_7properties@@6B@ DD FLAT:??_R4properties@@6B@ ; properties::‘vftable’ DD FLAT:?integer@properties@@UBEHXZ DD FLAT:?floatingpoint@properties@@UAENXZ ; Function compile flags: /Odtp Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 11. Changed interface class properties { public: virtual int integer() const { return 0; } virtual double floatingpoint() const { return 0; } virtual void integer( int ) { } virtual void floatingpoint( double ) { } }; int main() { properties p; } cl /EHsc /FAs test.cpp Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 12. The result Old: ??_7properties@@6B@ DD FLAT:??_R4properties@@6B@ ; properties::‘vftable’ DD FLAT:?integer@properties@@UBEHXZ DD FLAT:?floatingpoint@properties@@UAENXZ ; Function compile flags: /Odtp New: ??_7properties@@6B@ DD FLAT:??_R4properties@@6B@ ; properties::‘vftable’ DD FLAT:?integer@properties@@UAEXH@Z DD FLAT:?integer@properties@@UBEHXZ DD FLAT:?floatingpoint@properties@@UAEXN@Z DD FLAT:?floatingpoint@properties@@UAENXZ ; Function compile flags: /Odtp Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 14. Old new thing Adding a method to an interface is bad. Adding a new interface to a library is benign. Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 15. Simple interfaces #pragma once #include <string> namespace inventory { class part { public: virtual ~part(); virtual unsigned id() const = 0; virtual const std::string & name() const = 0; }; part * create_part(); } #include <part.h> #include <iostream> #include <memory> // Also link to inventory.lib int main() { using namespace std; using namespace inventory; unique_ptr< part > p( create_part() ); cout << p->id() << endl; cout << p->name() << endl; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 16. Extended interface #pragma once #include <string> namespace inventory { class part { public: virtual ~part(); virtual unsigned id() const = 0; virtual const std::string & name() const = 0; }; class part_v2 : public part { public: using part::id; using part::name; virtual void id( unsigned val ) = 0; virtual void name( const std::string & val ) = 0; }; part * create_part(); } #include <part.h> #include <iostream> #include <memory> // Also link to inventory.lib int main() { using namespace std; using namespace inventory; unique_ptr< part_v2 > p( dynamic_cast< part_v2* >( create_part() ) ); p->id( 100 ); p->name( "wingnut" ); cout << p->id() << endl; cout << p->name() << endl; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 17. Implementing part_v2 #include "part.h" namespace { class realpart : public inventory::part_v2 { public: realpart(); unsigned id() const; const std::string & name() const; void id( unsigned val ); void name( const std::string & val ); private: unsigned num; std::string namestr; }; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 18. Wrong turn! #include <string> class part { public: virtual int id() const = 0; virtual const std::string & name() const = 0; }; class part_v2 : public part { public: virtual void id( int val ) = 0; virtual void name( const std::string & val ) = 0; }; It’s not the interface that’s the problem... Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 19. v1 and v2 side-by-side ...it’s the implementation. class realpart : public part { public: int id() const { return num; } const std::string & name() const { return namestr; } private: int num; std::string namestr; }; class realpart_v2 : public part_v2 { public: int id() const { return num; } const std::string & name() const { return namestr; } void id( int val ) { } void name( const std::string & val ) { } private: int num; std::string namestr; }; Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 20. More vtables ??_7realpart@@6B@ DD FLAT:??_R4realpart@@6B@ ; realpart::‘vftable’ DD FLAT:?id@realpart@@UBEHXZ DD FLAT:? name@realpart@@UBEABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ ; Function compile flags: /Odtp ??_7realpart_v2@@6B@ DD FLAT:??_R4realpart_v2@@6B@ ; realpart_v2::‘vftable’ DD FLAT:?id@realpart_v2@@UBEHXZ DD FLAT:? name@realpart_v2@@UBEABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ DD FLAT:?id@realpart_v2@@UAEXH@Z DD FLAT:? name@realpart_v2@@UAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ; Function compile flags: /Odtp DANGER! This will appear to work, but depends on the implementation Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 21. Split the implementations #include "part.h" namespace { class realpart : public inventory::part { public: realpart(); unsigned id() const; const std::string & name() const; protected: unsigned num; std::string namestr; }; class realpart_v2 : public inventory::part_v2, public realpart { public: using part::id; using part::name; void id( unsigned val ); void name( const std::string & val ); }; namespace inventory { part::~part() { } part * create_part() { return new realpart_v2; } } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 22. Split the implementations #include "part.h" namespace { class realpart : public inventory::part { public: realpart(); unsigned id() const; const std::string & name() const; protected: unsigned num; std::string namestr; }; class realpart_v2 : public inventory::part_v2, public realpart { public: using part::id; using part::name; void id( unsigned val ); void name( const std::string & val ); }; namespace inventory { part::~part() { } part * create_part() { return new realpart_v2; } } ...except... it doesn’t compile! Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 23. Ambiguity Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 24. Resolved #pragma once #include <string> namespace inventory { class part { }; class part_v2 : public virtual part { }; } #include "part.h" namespace { class realpart : public virtual inventory::part { }; class realpart_v2 : public virtual inventory::part_v2, public realpart { }; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 26. Names have power namespace inventory { class part { }; class part_v2 : public virtual part { }; part * create_part(); } The factory returns a part. Changing this breaks the ABI (ODR). Clients of part_v2 must now find all references to “part”, and use the factory like this: unique_ptr< part_v2 > p( dynamic_cast< part_v2* >( create_part() ) ); Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 27. Called by a common name Class part becomes class part_v1 typedef part_v1 * part; Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 28. Called by a common name Class part becomes class part_v1 typedef part_v1 * part; Or, even better.... #include "part_v1.h" #include <memory> namespace inventory { typedef part_v1 part_current; typedef std::unique_ptr< part_current > part; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 29. No more casts namespace inventory { INVENTORY_LIB part_v1 * create_part_ptr(); template< typename type_version > part create_part() { return part( static_cast< type_version * >( create_part_ptr() ) ); } } #include <part_factory.h> #include <iostream> int main() { using namespace std; using namespace inventory; part p = create_part< part_current >(); cout << p->number() << endl; cout << p->name() << endl; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 30. Version up! New interface namespace inventory { class INVENTORY_LIB part_v2 : public virtual part_v1 { }; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 31. The real thing New implementation #include <part_v2.h> namespace inventory_impl { class realpart_v2 : public virtual inventory::part_v2, public realpart_v1 { }; } Update factory namespace inventory { using namespace inventory_impl; INVENTORY_LIB part_v1 * create_part_ptr() { return new realpart_v2; } } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 32. Status quo Update the typedef namespace inventory { typedef part_v2 part_current; typedef std::unique_ptr< part_current > part; } The template factory stays the same #include <part.h> namespace inventory { INVENTORY_LIB part_v1 * create_part_ptr(); template< typename type_version > part create_part() { return part( static_cast< type_version * >( create_part_ptr() ) ); } } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 33. The client V1 #include <part_factory.h> #include <iostream> int main() { using namespace std; using namespace inventory; part p = create_part< part_current >(); cout << p->number() << endl; cout << p->name() << endl; } v2 #include <part_factory.h> #include <iostream> int main() { using namespace std; using namespace inventory; part p = create_part< part_current >(); p->number( 100 ); p->name( "steve" ); cout << p->number() << endl; cout << p->name() << endl; } Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 34. Dependency management Code that has objects passed to it (i.e. does not need to create objects) should have no dependency on the factory. Paramaterize from Above and all that Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 35. Divide and conquer IINVENTORY.DLL Headers for part_v1/v2 Implementation file for part_v1 The part type and part_current typedef INVENTORY_IMPL.DLL Headers and implementation of realpart The factory Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 36. In other words... Copyright (c) Steve Love 2010 Interface Versioning in C++
  • 37. Justifying the means The means An extensible, safe and conforming way of extending interfaces without causing clients to redeploy. The end Loosening the coupling between the library and its clients. Copyright (c) Steve Love 2010 Interface Versioning in C++