CIB C omponent I nterface B inder: ABI stable architecture for a C++ SDK Satya Das
About me ● A developer at T omT om ● Previously I was at Adobe 2
ABI Stability Binary compatibility of older client with new library 3
Agenda ● ABI stable architecture for – Simple Class with demo – Class Hierarchy – Interface Class with demo ● About CIB tool ● Case Study ● What all we left 4
Simple class Demo SIMPLE CLASS class Circle { public: Circle(float r); Circle(const Circle&) = default; virtual ~Circle() = default; float Radius() const; void SetRadius(float r); virtual float Area() const; private: float mRadius; }; 5
Hourglass Interface 6
Hourglass Interface ● It was a great talk but few things it did not cover – Virtual function – Interface classes – Inheritance ● As per Stefanus it was more for ABI isolation 7
Export free functions ? float Radius(const Circle* pCircle) { Circle* CreateCircle(float r) { return pCircle->Radius(); return new Circle(r); } } void SetRadius(Circle* pCircle, float r) { Circle* CopyCircle(const Circle* pCircle) { pCircle->SetRadius(r); return new Circle(*pCircle); } } float Area(const Circle* pCircle) { void DeleteCircle(Circle* pCircle) { return pCircle->Area(); delete pCircle; } } Except the constructor all functions have fjrst parameter as pointer of the class. 8
Export MethodTable instead class Circle; extern "C" { using CircleImpl = Circle; MethodTableCircle DLLEXPORT gMethodTableCircle = { extern "C" struct MethodTableCircle { 6, const size_t numMethods; &CreateCircle, &CopyCircle, CircleImpl* (*Create) (float); &DeleteCircle, CircleImpl* (*Copy) (const CircleImpl*); &Radius, void (*Delete) (CircleImpl*); &SetRadius, float (*Radius) (const CircleImpl*); &Area void (*SetRadius) (CircleImpl*, float); }; float (*Area) (const CircleImpl*); } }; 9
Export MethodTable instead class Circle; extern "C" { using CircleImpl = Circle; MethodTableCircle DLLEXPORT gMethodTableCircle = { extern "C" struct MethodTableCircle { 6, const size_t numMethods; &CreateCircle, &CopyCircle, CircleImpl* (*Create) (float); &DeleteCircle, CircleImpl* (*Copy) (const CircleImpl*); &Radius, void (*Delete) (CircleImpl*); &SetRadius, float (*Radius) (const CircleImpl*); &Area void (*SetRadius) (CircleImpl*, float); }; float (*Area) (const CircleImpl*); } }; 10
Export MethodTable instead class Circle; extern "C" { using CircleImpl = Circle; MethodTableCircle DLLEXPORT gMethodTableCircle = { extern "C" struct MethodTableCircle { 6, const size_t numMethods; &CreateCircle, &CopyCircle, CircleImpl* (*Create) (float); &DeleteCircle, CircleImpl* (*Copy) (const CircleImpl*); &Radius, void (*Delete) (CircleImpl*); &SetRadius, float (*Radius) (const CircleImpl*); &Area void (*SetRadius) (CircleImpl*, float); }; float (*Area) (const CircleImpl*); } }; 11
Client Uses Proxy Class Library side class definition Client side class definition class CircleImpl; class Circle { class Circle { public: public: Circle(float r); Circle(float r); Circle(const Circle&) = default; Circle(const Circle&); virtual ~Circle() = default; virtual ~Circle(); float Radius() const; float Radius() const; void SetRadius(float r); void SetRadius(float r); virtual float Area() const; virtual float Area() const; private: private: float mRadius; Circle(CircleImpl* pCircleImpl) }; : pImpl(pCircleImpl) {} CircleImpl* pImpl; }; 12
Client Imports MethodTable class CircleImpl; extern "C" struct MethodTableCircle { const size_t numMethods; CircleImpl* (*Create) (float); CircleImpl* (*Copy) (const CircleImpl*); void (*Delete) (CircleImpl*); float (*Radius) (const CircleImpl*); void (*SetRadius) (CircleImpl*, float); float (*Area) (const CircleImpl*); }; extern "C" MethodTableCircle DLLIMPORT gMethodTableCircle; 13
Proxy Class Implementation Circle::Circle(float r) : pImpl(gMethodTableCircle.Create(r)) {} Circle::Circle(const Circle& a) : pImpl(gMethodTableCircle.Copy(a.pImpl)) {} Circle::~Circle() { gMethodTableCircle.Delete(pImpl); } 14
Proxy Class Implementation float Circle::Radius() const { return gMethodTableCircle.Radius(pImpl); } void Circle::SetRadius(float r) { return gMethodTableCircle.SetRadius(pImpl, r); } float Circle::Area() const { return gMethodTableCircle.Area(pImpl); } 15
Proxy Class Sequence Diagram Circle c(10); c.Area(); 16
Class use sequence diagram Circle c(10); c.Area(); 17
Class use sequence diagram Circle c(10); c.Area(); 18
Class use sequence diagram Circle c(10); c.Area(); 19
Class use sequence diagram Circle c(10); c.Area(); 20
Class use sequence diagram Circle c(10); c.Area(); 21
Ensuring ABI stability class Circle { public: Circle(float r); Circle(const Circle&) = default; virtual ~Circle() = default; float Radius() const; void SetRadius(float r); virtual float Perimeter() const; virtual float Area() const; private: float mOx {0}; float mOy {0}; float mRadius; }; 22
Ensuring ABI stability MethodT able is only appended Method Table: Version 1 Method Table: Version 2 extern "C" { extern "C" { MethodTableCircle DLLEXPORT MethodTableCircle DLLEXPORT gMethodTableCircle = { gMethodTableCircle = { 6, 7, &CreateCircle, &CreateCircle, &CopyCircle, &CopyCircle, &DeleteCircle, &DeleteCircle, &Radius, &Radius, &SetRadius, &SetRadius, &Area &Area, }; &Perimeter } }; } 23
MethodTable Vs Virtual Table ● MethodT able is NOT replacement of virtual table. 24
MethodTable Vs Virtual Table ● MethodT able is NOT replacement of virtual table. ● MethodT able is just a mechanism for cross component function call. 25
MethodTable Vs Virtual Table ● MethodT able is NOT replacement of virtual table. ● MethodT able is just a mechanism for cross component function call. ● MethodT able is shared between components. 26
MethodTable Vs Virtual Table ● MethodT able is NOT replacement of virtual table. ● MethodT able is just a mechanism for cross component function call. ● MethodT able is shared between components. ● Virtual table remains local. 27
MethodTable Vs Virtual Table ● MethodT able is NOT replacement of virtual table. ● MethodT able is just a mechanism for cross component function call. ● MethodT able is shared between components. ● Virtual table remains local. ● MethodT able is handy in supporting backward compatibility. 28
Client’s Backward Compatiblity class CircleImpl; class Circle { public: Circle(float r); Circle(const Circle&); virtual ~Circle(); float Radius() const; void SetRadius(float r); virtual float Perimeter() const; virtual float Area() const; private: Circle(CircleImpl* pImplA); CircleImpl* pImpl; }; 29
Client’s Backward Compatiblity float Circle::Perimeter() const { if (gMethodTableCircle.numMethods < 7) throw std::bad_function_call(); return gMethodTableCircle.Perimeter(pImpl); } 30
Client’s Backward Compatiblity try { Circle c; const auto p = c.Perimeter(); } catch (std::bad_function_call) { std::clog << "New client with old library" << std::endl; } } 31
MethodTable Vs Virtual Table ● MethodT able is NOT replacement of virtual table. ● MethodT able is just a mechanism for cross component function call. ● MethodT able is shared between components. ● Virtual table remains local. ● MethodT able is handy in supporting backward compatibility. ● MethodT able does NOT inherit. 32
EXAMPLE 2 INHERITANCE 33
Inheritance Library Version 1 Library Version 2 struct Base { struct Base { Base() {} Base() {} virtual ~Base() {} virtual ~Base() {} virtual int F() { return 1; } virtual int F() { return 1; } virtual int G() { return 2; } virtual int G() { return 2; } }; virtual int E() { return 9; } }; struct Derived : Base { struct Derived : Base { Derived() {} Derived() {} virtual ~Derived() {}; virtual ~Derived() {}; int G() override { return 3; } int G() override { return 3; } virtual int H() { return 4; } virtual int H() { return 4; } }; }; 34
Inheritance Library Version 1 Library Version 2 struct Base { struct Base { Base() {} Base() {} virtual ~Base() {} virtual ~Base() {} virtual int F() { return 1; } virtual int F() { return 1; } virtual int G() { return 2; } virtual int G() { return 2; } }; virtual int E() { return 9; } }; struct Derived : Base { struct Derived : Base { Derived() {} Derived() {} virtual ~Derived() {}; virtual ~Derived() {}; int G() override { return 3; } int G() override { return 3; } virtual int H() { return 4; } virtual int H() { return 4; } }; }; 35
Recommend
More recommend