SlideShare a Scribd company logo
Building reusable libraries
Without shooting yourself in the foot too badly
Felix Morgner
September 28, 2016
Institute for Software
University of Applied Sciences Rapperswil
Agenda
What will we be talking about
◦ Hiding your secrets
1
What will we be talking about
◦ Hiding your secrets
◦ Instant Replay
1
What will we be talking about
◦ Hiding your secrets
◦ Instant Replay
◦ Crossing boundaries
1
What will we be talking about
◦ Hiding your secrets
◦ Instant Replay
◦ Crossing boundaries
◦ Summary
1
Hiding your secrets
Of unicorns - unicorn.hpp
struct unicorn {
unicorn(std::string name, std::string color);
void glitter(std::ostream & out = std::cout) const;
void fly(std::ostream & out = std::cout) const;
private:
std::size_t calculate_altitude() const;
std::string const m_name{};
std::string const m_color{};
};
2
Of unicorns - unicorn.cpp
unicorn::unicorn(std::string name, std::string color) :
m_name{name}, m_color{color} {}
void unicorn::glitter(std::ostream & out) const {
out << m_name << " glitters beautifullyn";
}
void unicorn::fly(std::ostream & out) const {
out << m_name << " flies at " << calculate_altitude() << "mn";
}
std::size_t unicorn::calculate_altitude() const {
return 8 * m_color.size();
}
3
Of unicorns - freddy.cpp
#include "unicorn.hpp"
int main() {
auto freddy = unicorn{"freddy", "red"};
freddy.glitter();
freddy.fly();
}
4
Magical dependencies
5
Magical dependencies
5
Why is that?
6
Why is that?
◦ #include == Copy & Paste
6
Why is that?
◦ #include == Copy & Paste
◦ All members are part of the interface
6
Why is that?
◦ #include == Copy & Paste
◦ All members are part of the interface
◦ Private members take part in overload resolution
6
Why is that?
◦ #include == Copy & Paste
◦ All members are part of the interface
◦ Private members take part in overload resolution
◦ Modules might fix that to some degree
6
PIMPL to the rescue!
7
PIMPL to the rescue!
◦ Pointer to IMPLementation
7
PIMPL to the rescue!
◦ Pointer to IMPLementation
◦ Hides the innards of our class
7
PIMPL to the rescue!
◦ Pointer to IMPLementation
◦ Hides the innards of our class
◦ Also known as ”Compilation Firewall”
7
How does it work?
8
How does it work?
◦ Remove private declarations
8
How does it work?
◦ Remove private declarations
◦ Add opaque Pointer to IMPLementation
8
How does it work?
◦ Remove private declarations
◦ Add opaque Pointer to IMPLementation
◦ Define opaque type in unicorn.cpp
8
How does it work?
8
Show the code! - unicorn.hpp
struct unicorn {
unicorn(std::string name, std::string color);
// MUST declare dtor!
~unicorn();
void glitter(std::ostream & out = std::cout) const;
void fly(std::ostream & out = std::cout) const;
private:
std::unique_ptr<struct unicorn_impl> m_impl;
};
9
Show the code! - unicorn.cpp the private part
struct unicorn_impl {
unicorn_impl(std::string name, std::string color) :
m_name{name}, m_color{color} {}
void glitter(std::ostream & out) const {
out << m_name << " glitters beautifullyn";
}
void fly(std::ostream & out) const {
out << m_name << " flies at " << altitude() << "mn";
}
private:
std::size_t altitude() const { return 14 * m_color.size(); }
std::string const m_name;
std::string const m_color;
};
10
Show the code! - unicorn.cpp the rest
// MUST define dtor after unicorn_impl is known
unicorn::~unicorn() = default;
unicorn::unicorn(std::string name, std::string color) :
m_impl{std::make_unique<unicorn_impl>(name, color)} { }
void unicorn::glitter(std::ostream & out) const {
m_impl->glitter(out);
}
void unicorn::fly(std::ostream & out) const {
m_impl->fly(out);
}
11
What does that buy us?
12
What does that buy us?
◦ Clients only see what they should
12
What does that buy us?
◦ Clients only see what they should
◦ Private changes are isolated
12
What does that buy us?
◦ Clients only see what they should
◦ Private changes are isolated
◦ No ”compilation cascade”
12
Is it for free?
13
Is it for free?
◦ Another layer of indirection
13
Is it for free?
◦ Another layer of indirection
◦ What pointer-style should we use?
13
Is it for free?
◦ Another layer of indirection
◦ What pointer-style should we use?
▷ Think about copying
13
Is it for free?
◦ Another layer of indirection
◦ What pointer-style should we use?
▷ Think about copying
▷ Should nullptr be allowed?
13
Is it for free?
◦ Another layer of indirection
◦ What pointer-style should we use?
▷ Think about copying
▷ Should nullptr be allowed?
▷ How to handle constnes?
13
Is it for free?
◦ Another layer of indirection
◦ What pointer-style should we use?
▷ Think about copying
▷ Should nullptr be allowed?
▷ How to handle constnes?
◦ What about inheritance?
13
Instant Replay
What have we seen so far?
14
What have we seen so far?
◦ Inclusion incurs ”strong coupling”
14
What have we seen so far?
◦ Inclusion incurs ”strong coupling”
◦ PIMPL can give us some ABI stability
14
What have we seen so far?
◦ Inclusion incurs ”strong coupling”
◦ PIMPL can give us some ABI stability
◦ We are still in the C++ domain
14
What have we seen so far?
◦ Inclusion incurs ”strong coupling”
◦ PIMPL can give us some ABI stability
◦ We are still in the C++ domain
◦ PIMPL has some costs attached
14
No unicorns were harmed!
Freddy still flies!
15
Crossing boundaries
Of standards…
http://xkcd.com/927/
16
…and ABIs
17
…and ABIs
◦ Think API on Binary level
17
…and ABIs
◦ Think API on Binary level
◦ Covers
17
…and ABIs
◦ Think API on Binary level
◦ Covers
▷ Instruction set
17
…and ABIs
◦ Think API on Binary level
◦ Covers
▷ Instruction set
▷ Calling conventions
17
…and ABIs
◦ Think API on Binary level
◦ Covers
▷ Instruction set
▷ Calling conventions
▷ Names of things
17
…and ABIs
◦ Think API on Binary level
◦ Covers
▷ Instruction set
▷ Calling conventions
▷ Names of things
◦ C++ does NOT specify a standard ABI
17
An introductory example - overview
18
An introductory example - fancy.cpp
#include "fancy.hpp"
namespace cppug {
void be_fancy(std::string const & ent, std::ostream & out) {
out << "I am a fancy " << ent << 'n';
}
}
19
An introductory example - fancy_doge.cpp
#include "fancy.hpp"
int main() {
cppug::be_fancy("doge");
}
20
An introductory example - fancy_sloth.cpp
#include "fancy.hpp"
int main() {
cppug::be_fancy("sloth");
}
21
Looks good to me!
What could possibly go wrong?
22
This error is so fancy, I need a second monocle!
/tmp/fancy_sloth-2edd1b.o: In function `main':
fancy_sloth.cpp:(.text+0x6c): undefined reference to
`cppug::be_fancy(std::__1::basic_string<char,
std::__1::char_traits<char>, std::__1::allocator<char> >
const&, std::__1::basic_ostream<char,
std::__1::char_traits<char> >&)'
→
→
→
→
clang-3.8: error: linker command failed with exit code 1
(use -v to see invocation)→
23
This error is so fancy, I need a second monocle!
23
There is no single STL
24
It is not just libraries!
25
It is not just libraries!
◦ GCC changed its C++ ABI from 2.95 to 3.0
25
It is not just libraries!
◦ GCC changed its C++ ABI from 2.95 to 3.0
◦ …and again from 3.0 to 3.1
25
It is not just libraries!
◦ GCC changed its C++ ABI from 2.95 to 3.0
◦ …and again from 3.0 to 3.1
◦ …and yet again from 3.1 to 3.2
25
It is not just libraries!
◦ GCC changed its C++ ABI from 2.95 to 3.0
◦ …and again from 3.0 to 3.1
◦ …and yet again from 3.1 to 3.2
◦ …and again with version 3.4
25
It is not just libraries!
◦ GCC changed its C++ ABI from 2.95 to 3.0
◦ …and again from 3.0 to 3.1
◦ …and yet again from 3.1 to 3.2
◦ …and again with version 3.4
◦ …and pretty much again with version 5.1
25
But… what can we do?
26
But… what can we do?
◦ Use C
26
But… what can we do?
◦ Use C
◦ seriously!
26
But… what can we do?
◦ Use C
◦ seriously!
◦ C has extremely stable ABIs
26
But… what can we do?
◦ Use C
◦ seriously!
◦ C has extremely stable ABIs
◦ Use C as the ”frontend”
26
But… what can we do?
◦ Use C
◦ seriously!
◦ C has extremely stable ABIs
◦ Use C as the ”frontend”
◦ C++ under the hood
26
Introducing the Hourglass Interface
27
What is that?
28
What is that?
◦ Full C++ on the bottom-end
28
What is that?
◦ Full C++ on the bottom-end
◦ LanguageXYZ on the upper-end
28
What is that?
◦ Full C++ on the bottom-end
◦ LanguageXYZ on the upper-end
◦ Stable C interface in between
28
What is that?
◦ Full C++ on the bottom-end
◦ LanguageXYZ on the upper-end
◦ Stable C interface in between
◦ Excellent talk by Stefanus DuToit @ CppCon 2014
▷ https://www.youtube.com/watch?v=PVYdHDm0q6Y
28
Back to our example
29
Its not that simple anymore
30
Lets look at some code - fancy.h
#ifdef __cplusplus
extern "C"
{
#endif
__attribute__((visibility("default")))
void cppug_be_fancy_on_stdout(char const * const entity);
#ifdef __cplusplus
}
#endif
31
Lets look at some code - fancy.cpp
extern "C" {
void cppug_be_fancy_on_stdout(char const * const entity) {
cppug::be_fancy(entity);
}
}
32
Lets look at some code - fancy_lib.hpp
#include <string>
#include <iostream>
namespace cppug {
void be_fancy(std::string const &, std::ostream & = std::cout);
}
33
Lets look at some code - fancy_lib.cpp
namespace cppug {
void be_fancy(std::string const & ent, std::ostream & out) {
out << "I am a fancy " << ent << 'n';
}
}
34
What is important?
35
What is important?
◦ No function overloading in C
35
What is important?
◦ No function overloading in C
◦ No namespaces in C
35
What is important?
◦ No function overloading in C
◦ No namespaces in C
◦ No exceptions in C
35
What is important?
◦ No function overloading in C
◦ No namespaces in C
◦ No exceptions in C
◦ Use extern "C" to prevent name-mangling
35
What is important?
◦ No function overloading in C
◦ No namespaces in C
◦ No exceptions in C
◦ Use extern "C" to prevent name-mangling
◦ (Optionally) Use visibility specifiers
35
Speaking foreign languages - fancy_gopher.go
/*
#cgo LDFLAGS: -lfancy -Llib
#include "fancy.h"
*/
import "C"
func be_fancy(ent string) {
cstr := C.CString(ent)
C.cppug_be_fancy_on_stdout(cstr)
}
36
Speaking foreign languages - fancy_python.py
_fancy = ctypes.CDLL('lib/libfancy.so')
_fancy.cppug_be_fancy_on_stdout.argtypes = [ctypes.c_char_p]
def be_fancy(ent):
if not type(ent) is str:
raise TypeError("Argument must be a string")
_fancy.cppug_be_fancy_on_stdout(ent.encode('utf-8'))
37
But I want classes!
38
But I want classes!
◦ structs cannot have member functions in C
38
But I want classes!
◦ structs cannot have member functions in C
◦ But we have opaque pointers
38
But I want classes!
◦ structs cannot have member functions in C
◦ But we have opaque pointers
◦ Sounds familiar?
38
A box of ints
39
A box of ints
40
A box of ints
◦ Create a box of a fixed size
40
A box of ints
◦ Create a box of a fixed size
◦ Push values inside
40
A box of ints
◦ Create a box of a fixed size
◦ Push values inside
◦ Pop them back out
40
A box of ints
◦ Create a box of a fixed size
◦ Push values inside
◦ Pop them back out
◦ Important: Destroy the box!
40
C with classes - box.h
typedef struct box * box_t;
41
C with classes - box.h
typedef struct box * box_t;
EXPORTED
box_t box_create(size_t size, int * err);
EXPORTED
void box_destroy(box_t ins);
EXPORTED
int box_pop(box_t ins, int * err);
EXPORTED
void box_push(box_t ins, int elem, int * err);
41
C with classes - box.cpp
struct box {
box(std::size_t size) : real{size} {}
impl::box real;
};
42
C with classes - box_impl.hpp
struct box {
box(std::size_t const size);
void push(int const element);
int pop();
private:
std::unique_ptr<int[]> m_data{};
std::size_t const m_capacity{};
std::size_t m_size{};
};
43
C with classes - cbox.c
box_t box = box_create(3, NULL);
if(!box) {
printf("Failed to create boxn");
box_destroy(box);
return EXIT_FAILURE;
}
box_push(box, 42, NULL);
int err = 0;
int val = box_pop(box, &err);
44
C with classes - cppbox.cpp
int main() {
cppug::box box{3};
box.push(42);
std::cout << box.pop() << 'n';
}
45
Going the extra mile - gobox.bo
/*
#cgo LDFLAGS: -lbox -Llib
#include "box.h"
*/
import "C"
import "runtime"
import "fmt"
type Box struct {
c_box C.box_t
}
46
Going the extra mile - gobox.bo
func NewBox(size int) *Box {
box := new(Box)
runtime.SetFinalizer(box, func(ins *Box) {
C.box_destroy(ins.c_box)
})
box.c_box = C.box_create(C.size_t(size), nil)
if box.c_box == nil {
panic("Failed to initializ object")
}
return box
}
46
Going the extra mile - gobox.bo
func (obj *Box) Push(val int) {
err := C.int(0)
C.box_push(obj.c_box, C.int(val), &err)
if err != 0 {
panic("Failed to push value")
}
}
46
Going the extra mile - gobox.bo
func (obj *Box) Pop() int {
err := C.int(0)
val := C.box_pop(obj.c_box, &err)
if err != 0 {
panic("Failed to pop value")
}
return int(val)
}
46
Going the extra mile - gobox.bo
func main() {
box := NewBox(3)
box.Push(42)
val := box.Pop()
fmt.Println("Popped ", val)
}
46
What is worth noting
47
What is worth noting
◦ It is basically PIMPL for C
47
What is worth noting
◦ It is basically PIMPL for C
◦ Exceptions do not propagate well
47
What is worth noting
◦ It is basically PIMPL for C
◦ Exceptions do not propagate well
◦ You need to manage the memory
47
What is worth noting
◦ It is basically PIMPL for C
◦ Exceptions do not propagate well
◦ You need to manage the memory
◦ You need to define ”contracts”
47
Summary
Summing up
48
Summing up
◦ The C++ ABI is not stable
48
Summing up
◦ The C++ ABI is not stable
◦ Neither is it ”compatible”
48
Summing up
◦ The C++ ABI is not stable
◦ Neither is it ”compatible”
◦ C tends to be very stable
48
Summing up
◦ The C++ ABI is not stable
◦ Neither is it ”compatible”
◦ C tends to be very stable
◦ But hard to get right
48
Summing up
◦ The C++ ABI is not stable
◦ Neither is it ”compatible”
◦ C tends to be very stable
◦ But hard to get right
◦ Other languages can use C libraries
48
What I did not tell you
49
What I did not tell you
◦ Tools can do the heavy lifting
49
What I did not tell you
◦ Tools can do the heavy lifting
◦ Some languages provide their own tools
49
What I did not tell you
◦ Tools can do the heavy lifting
◦ Some languages provide their own tools
◦ SWIG supports a lot of languages
49
What I did not tell you
◦ Tools can do the heavy lifting
◦ Some languages provide their own tools
◦ SWIG supports a lot of languages
◦ You do not need C++ on the bottom-end
49
What I did not tell you
◦ Tools can do the heavy lifting
◦ Some languages provide their own tools
◦ SWIG supports a lot of languages
◦ You do not need C++ on the bottom-end
▷ https://github.com/dns2utf8/hour_glass
▷ Uses Rust on the bottom-end
49
std::terminate()
50
std::terminate()
Thank You!
50
References
Where to read more
◦ http://en.cppreference.com/w/cpp/language/pimpl
◦ https://en.wikipedia.org/wiki/Application_binary_interface
◦ https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
◦ https://gcc.gnu.org/wiki/Visibility
51

More Related Content

PDF
Tiramisu をちょっと、味見してみました。
PDF
Про асинхронность / Максим Щепелин / Web Developer Wargaming
PPTX
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
PDF
RAII and ScopeGuard
PDF
Антон Бикинеев, Reflection in C++Next
PDF
C++ idioms by example (Nov 2008)
PDF
Fuzzing: The New Unit Testing
PDF
Tensor comprehensions
Tiramisu をちょっと、味見してみました。
Про асинхронность / Максим Щепелин / Web Developer Wargaming
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
RAII and ScopeGuard
Антон Бикинеев, Reflection in C++Next
C++ idioms by example (Nov 2008)
Fuzzing: The New Unit Testing
Tensor comprehensions

What's hot (20)

PDF
Tiramisu概要
PDF
Insecure coding in C (and C++)
PDF
Антон Бикинеев, Writing good std::future&lt; C++ >
PDF
Python idiomatico
PPTX
C++17 std::filesystem - Overview
PDF
COG Back to the Future, Part II
PPT
Евгений Крутько, Многопоточные вычисления, современный подход.
PDF
Boost.Python - domesticating the snake
PDF
Os Goodger
PDF
Introduce to Rust-A Powerful System Language
PDF
The Rust Programming Language: an Overview
ODP
OpenGurukul : Language : PHP
PDF
Php radomize
PDF
Checking the Open-Source Multi Theft Auto Game
PDF
An introduction to Rust: the modern programming language to develop safe and ...
PDF
Why rust?
PPTX
Introduction to Rust language programming
PPTX
Pro typescript.ch03.Object Orientation in TypeScript
PDF
(5) cpp abstractions essential_operators
PPTX
Php Extensions for Dummies
Tiramisu概要
Insecure coding in C (and C++)
Антон Бикинеев, Writing good std::future&lt; C++ >
Python idiomatico
C++17 std::filesystem - Overview
COG Back to the Future, Part II
Евгений Крутько, Многопоточные вычисления, современный подход.
Boost.Python - domesticating the snake
Os Goodger
Introduce to Rust-A Powerful System Language
The Rust Programming Language: an Overview
OpenGurukul : Language : PHP
Php radomize
Checking the Open-Source Multi Theft Auto Game
An introduction to Rust: the modern programming language to develop safe and ...
Why rust?
Introduction to Rust language programming
Pro typescript.ch03.Object Orientation in TypeScript
(5) cpp abstractions essential_operators
Php Extensions for Dummies
Ad

Similar to Building reusable libraries (20)

PDF
C++primer
PPTX
Return of c++
PPTX
Lab 1.pptx
PPTX
C++ Introduction brown bag
PDF
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
PDF
data structure book in c++ and c in easy wording
PDF
Secure Programming Practices in C++ (NDC Security 2018)
PDF
c-for-c-programmers.pdf
PDF
Introduction-to-C-Part-1.pdf
PPTX
C++Basics2022.pptx
PPTX
Introduction-to-C-Part-1 JSAHSHAHSJAHSJAHSJHASJ
PPTX
Introduction-to-C-Part-1.pptx
PPTX
C++ helps you to format the I/O operations like determining the number of dig...
DOC
Introduction-to-C-Part-1 (1).doc
PPTX
C++ process new
PPTX
cbybalaguruswami-e-180803051831.pptx
PPTX
cbybalaguruswami-e-180803051831.pptx
PPTX
How to Adopt Modern C++17 into Your C++ Code
PPTX
How to Adopt Modern C++17 into Your C++ Code
PPTX
C++ language
C++primer
Return of c++
Lab 1.pptx
C++ Introduction brown bag
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
data structure book in c++ and c in easy wording
Secure Programming Practices in C++ (NDC Security 2018)
c-for-c-programmers.pdf
Introduction-to-C-Part-1.pdf
C++Basics2022.pptx
Introduction-to-C-Part-1 JSAHSHAHSJAHSJAHSJHASJ
Introduction-to-C-Part-1.pptx
C++ helps you to format the I/O operations like determining the number of dig...
Introduction-to-C-Part-1 (1).doc
C++ process new
cbybalaguruswami-e-180803051831.pptx
cbybalaguruswami-e-180803051831.pptx
How to Adopt Modern C++17 into Your C++ Code
How to Adopt Modern C++17 into Your C++ Code
C++ language
Ad

Recently uploaded (20)

PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
top salesforce developer skills in 2025.pdf
PPTX
ai tools demonstartion for schools and inter college
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PPTX
history of c programming in notes for students .pptx
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PPTX
Introduction to Artificial Intelligence
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Understanding Forklifts - TECH EHS Solution
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
System and Network Administration Chapter 2
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PPTX
Odoo POS Development Services by CandidRoot Solutions
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
wealthsignaloriginal-com-DS-text-... (1).pdf
top salesforce developer skills in 2025.pdf
ai tools demonstartion for schools and inter college
VVF-Customer-Presentation2025-Ver1.9.pptx
history of c programming in notes for students .pptx
Design an Analysis of Algorithms I-SECS-1021-03
How Creative Agencies Leverage Project Management Software.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Introduction to Artificial Intelligence
How to Choose the Right IT Partner for Your Business in Malaysia
Understanding Forklifts - TECH EHS Solution
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
System and Network Administration Chapter 2
2025 Textile ERP Trends: SAP, Odoo & Oracle
Odoo POS Development Services by CandidRoot Solutions
Reimagine Home Health with the Power of Agentic AI​
Odoo Companies in India – Driving Business Transformation.pdf
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...

Building reusable libraries

  • 1. Building reusable libraries Without shooting yourself in the foot too badly Felix Morgner September 28, 2016 Institute for Software University of Applied Sciences Rapperswil
  • 3. What will we be talking about ◦ Hiding your secrets 1
  • 4. What will we be talking about ◦ Hiding your secrets ◦ Instant Replay 1
  • 5. What will we be talking about ◦ Hiding your secrets ◦ Instant Replay ◦ Crossing boundaries 1
  • 6. What will we be talking about ◦ Hiding your secrets ◦ Instant Replay ◦ Crossing boundaries ◦ Summary 1
  • 8. Of unicorns - unicorn.hpp struct unicorn { unicorn(std::string name, std::string color); void glitter(std::ostream & out = std::cout) const; void fly(std::ostream & out = std::cout) const; private: std::size_t calculate_altitude() const; std::string const m_name{}; std::string const m_color{}; }; 2
  • 9. Of unicorns - unicorn.cpp unicorn::unicorn(std::string name, std::string color) : m_name{name}, m_color{color} {} void unicorn::glitter(std::ostream & out) const { out << m_name << " glitters beautifullyn"; } void unicorn::fly(std::ostream & out) const { out << m_name << " flies at " << calculate_altitude() << "mn"; } std::size_t unicorn::calculate_altitude() const { return 8 * m_color.size(); } 3
  • 10. Of unicorns - freddy.cpp #include "unicorn.hpp" int main() { auto freddy = unicorn{"freddy", "red"}; freddy.glitter(); freddy.fly(); } 4
  • 14. Why is that? ◦ #include == Copy & Paste 6
  • 15. Why is that? ◦ #include == Copy & Paste ◦ All members are part of the interface 6
  • 16. Why is that? ◦ #include == Copy & Paste ◦ All members are part of the interface ◦ Private members take part in overload resolution 6
  • 17. Why is that? ◦ #include == Copy & Paste ◦ All members are part of the interface ◦ Private members take part in overload resolution ◦ Modules might fix that to some degree 6
  • 18. PIMPL to the rescue! 7
  • 19. PIMPL to the rescue! ◦ Pointer to IMPLementation 7
  • 20. PIMPL to the rescue! ◦ Pointer to IMPLementation ◦ Hides the innards of our class 7
  • 21. PIMPL to the rescue! ◦ Pointer to IMPLementation ◦ Hides the innards of our class ◦ Also known as ”Compilation Firewall” 7
  • 22. How does it work? 8
  • 23. How does it work? ◦ Remove private declarations 8
  • 24. How does it work? ◦ Remove private declarations ◦ Add opaque Pointer to IMPLementation 8
  • 25. How does it work? ◦ Remove private declarations ◦ Add opaque Pointer to IMPLementation ◦ Define opaque type in unicorn.cpp 8
  • 26. How does it work? 8
  • 27. Show the code! - unicorn.hpp struct unicorn { unicorn(std::string name, std::string color); // MUST declare dtor! ~unicorn(); void glitter(std::ostream & out = std::cout) const; void fly(std::ostream & out = std::cout) const; private: std::unique_ptr<struct unicorn_impl> m_impl; }; 9
  • 28. Show the code! - unicorn.cpp the private part struct unicorn_impl { unicorn_impl(std::string name, std::string color) : m_name{name}, m_color{color} {} void glitter(std::ostream & out) const { out << m_name << " glitters beautifullyn"; } void fly(std::ostream & out) const { out << m_name << " flies at " << altitude() << "mn"; } private: std::size_t altitude() const { return 14 * m_color.size(); } std::string const m_name; std::string const m_color; }; 10
  • 29. Show the code! - unicorn.cpp the rest // MUST define dtor after unicorn_impl is known unicorn::~unicorn() = default; unicorn::unicorn(std::string name, std::string color) : m_impl{std::make_unique<unicorn_impl>(name, color)} { } void unicorn::glitter(std::ostream & out) const { m_impl->glitter(out); } void unicorn::fly(std::ostream & out) const { m_impl->fly(out); } 11
  • 30. What does that buy us? 12
  • 31. What does that buy us? ◦ Clients only see what they should 12
  • 32. What does that buy us? ◦ Clients only see what they should ◦ Private changes are isolated 12
  • 33. What does that buy us? ◦ Clients only see what they should ◦ Private changes are isolated ◦ No ”compilation cascade” 12
  • 34. Is it for free? 13
  • 35. Is it for free? ◦ Another layer of indirection 13
  • 36. Is it for free? ◦ Another layer of indirection ◦ What pointer-style should we use? 13
  • 37. Is it for free? ◦ Another layer of indirection ◦ What pointer-style should we use? ▷ Think about copying 13
  • 38. Is it for free? ◦ Another layer of indirection ◦ What pointer-style should we use? ▷ Think about copying ▷ Should nullptr be allowed? 13
  • 39. Is it for free? ◦ Another layer of indirection ◦ What pointer-style should we use? ▷ Think about copying ▷ Should nullptr be allowed? ▷ How to handle constnes? 13
  • 40. Is it for free? ◦ Another layer of indirection ◦ What pointer-style should we use? ▷ Think about copying ▷ Should nullptr be allowed? ▷ How to handle constnes? ◦ What about inheritance? 13
  • 42. What have we seen so far? 14
  • 43. What have we seen so far? ◦ Inclusion incurs ”strong coupling” 14
  • 44. What have we seen so far? ◦ Inclusion incurs ”strong coupling” ◦ PIMPL can give us some ABI stability 14
  • 45. What have we seen so far? ◦ Inclusion incurs ”strong coupling” ◦ PIMPL can give us some ABI stability ◦ We are still in the C++ domain 14
  • 46. What have we seen so far? ◦ Inclusion incurs ”strong coupling” ◦ PIMPL can give us some ABI stability ◦ We are still in the C++ domain ◦ PIMPL has some costs attached 14
  • 47. No unicorns were harmed! Freddy still flies! 15
  • 51. …and ABIs ◦ Think API on Binary level 17
  • 52. …and ABIs ◦ Think API on Binary level ◦ Covers 17
  • 53. …and ABIs ◦ Think API on Binary level ◦ Covers ▷ Instruction set 17
  • 54. …and ABIs ◦ Think API on Binary level ◦ Covers ▷ Instruction set ▷ Calling conventions 17
  • 55. …and ABIs ◦ Think API on Binary level ◦ Covers ▷ Instruction set ▷ Calling conventions ▷ Names of things 17
  • 56. …and ABIs ◦ Think API on Binary level ◦ Covers ▷ Instruction set ▷ Calling conventions ▷ Names of things ◦ C++ does NOT specify a standard ABI 17
  • 57. An introductory example - overview 18
  • 58. An introductory example - fancy.cpp #include "fancy.hpp" namespace cppug { void be_fancy(std::string const & ent, std::ostream & out) { out << "I am a fancy " << ent << 'n'; } } 19
  • 59. An introductory example - fancy_doge.cpp #include "fancy.hpp" int main() { cppug::be_fancy("doge"); } 20
  • 60. An introductory example - fancy_sloth.cpp #include "fancy.hpp" int main() { cppug::be_fancy("sloth"); } 21
  • 61. Looks good to me! What could possibly go wrong? 22
  • 62. This error is so fancy, I need a second monocle! /tmp/fancy_sloth-2edd1b.o: In function `main': fancy_sloth.cpp:(.text+0x6c): undefined reference to `cppug::be_fancy(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_ostream<char, std::__1::char_traits<char> >&)' → → → → clang-3.8: error: linker command failed with exit code 1 (use -v to see invocation)→ 23
  • 63. This error is so fancy, I need a second monocle! 23
  • 64. There is no single STL 24
  • 65. It is not just libraries! 25
  • 66. It is not just libraries! ◦ GCC changed its C++ ABI from 2.95 to 3.0 25
  • 67. It is not just libraries! ◦ GCC changed its C++ ABI from 2.95 to 3.0 ◦ …and again from 3.0 to 3.1 25
  • 68. It is not just libraries! ◦ GCC changed its C++ ABI from 2.95 to 3.0 ◦ …and again from 3.0 to 3.1 ◦ …and yet again from 3.1 to 3.2 25
  • 69. It is not just libraries! ◦ GCC changed its C++ ABI from 2.95 to 3.0 ◦ …and again from 3.0 to 3.1 ◦ …and yet again from 3.1 to 3.2 ◦ …and again with version 3.4 25
  • 70. It is not just libraries! ◦ GCC changed its C++ ABI from 2.95 to 3.0 ◦ …and again from 3.0 to 3.1 ◦ …and yet again from 3.1 to 3.2 ◦ …and again with version 3.4 ◦ …and pretty much again with version 5.1 25
  • 71. But… what can we do? 26
  • 72. But… what can we do? ◦ Use C 26
  • 73. But… what can we do? ◦ Use C ◦ seriously! 26
  • 74. But… what can we do? ◦ Use C ◦ seriously! ◦ C has extremely stable ABIs 26
  • 75. But… what can we do? ◦ Use C ◦ seriously! ◦ C has extremely stable ABIs ◦ Use C as the ”frontend” 26
  • 76. But… what can we do? ◦ Use C ◦ seriously! ◦ C has extremely stable ABIs ◦ Use C as the ”frontend” ◦ C++ under the hood 26
  • 79. What is that? ◦ Full C++ on the bottom-end 28
  • 80. What is that? ◦ Full C++ on the bottom-end ◦ LanguageXYZ on the upper-end 28
  • 81. What is that? ◦ Full C++ on the bottom-end ◦ LanguageXYZ on the upper-end ◦ Stable C interface in between 28
  • 82. What is that? ◦ Full C++ on the bottom-end ◦ LanguageXYZ on the upper-end ◦ Stable C interface in between ◦ Excellent talk by Stefanus DuToit @ CppCon 2014 ▷ https://www.youtube.com/watch?v=PVYdHDm0q6Y 28
  • 83. Back to our example 29
  • 84. Its not that simple anymore 30
  • 85. Lets look at some code - fancy.h #ifdef __cplusplus extern "C" { #endif __attribute__((visibility("default"))) void cppug_be_fancy_on_stdout(char const * const entity); #ifdef __cplusplus } #endif 31
  • 86. Lets look at some code - fancy.cpp extern "C" { void cppug_be_fancy_on_stdout(char const * const entity) { cppug::be_fancy(entity); } } 32
  • 87. Lets look at some code - fancy_lib.hpp #include <string> #include <iostream> namespace cppug { void be_fancy(std::string const &, std::ostream & = std::cout); } 33
  • 88. Lets look at some code - fancy_lib.cpp namespace cppug { void be_fancy(std::string const & ent, std::ostream & out) { out << "I am a fancy " << ent << 'n'; } } 34
  • 90. What is important? ◦ No function overloading in C 35
  • 91. What is important? ◦ No function overloading in C ◦ No namespaces in C 35
  • 92. What is important? ◦ No function overloading in C ◦ No namespaces in C ◦ No exceptions in C 35
  • 93. What is important? ◦ No function overloading in C ◦ No namespaces in C ◦ No exceptions in C ◦ Use extern "C" to prevent name-mangling 35
  • 94. What is important? ◦ No function overloading in C ◦ No namespaces in C ◦ No exceptions in C ◦ Use extern "C" to prevent name-mangling ◦ (Optionally) Use visibility specifiers 35
  • 95. Speaking foreign languages - fancy_gopher.go /* #cgo LDFLAGS: -lfancy -Llib #include "fancy.h" */ import "C" func be_fancy(ent string) { cstr := C.CString(ent) C.cppug_be_fancy_on_stdout(cstr) } 36
  • 96. Speaking foreign languages - fancy_python.py _fancy = ctypes.CDLL('lib/libfancy.so') _fancy.cppug_be_fancy_on_stdout.argtypes = [ctypes.c_char_p] def be_fancy(ent): if not type(ent) is str: raise TypeError("Argument must be a string") _fancy.cppug_be_fancy_on_stdout(ent.encode('utf-8')) 37
  • 97. But I want classes! 38
  • 98. But I want classes! ◦ structs cannot have member functions in C 38
  • 99. But I want classes! ◦ structs cannot have member functions in C ◦ But we have opaque pointers 38
  • 100. But I want classes! ◦ structs cannot have member functions in C ◦ But we have opaque pointers ◦ Sounds familiar? 38
  • 101. A box of ints 39
  • 102. A box of ints 40
  • 103. A box of ints ◦ Create a box of a fixed size 40
  • 104. A box of ints ◦ Create a box of a fixed size ◦ Push values inside 40
  • 105. A box of ints ◦ Create a box of a fixed size ◦ Push values inside ◦ Pop them back out 40
  • 106. A box of ints ◦ Create a box of a fixed size ◦ Push values inside ◦ Pop them back out ◦ Important: Destroy the box! 40
  • 107. C with classes - box.h typedef struct box * box_t; 41
  • 108. C with classes - box.h typedef struct box * box_t; EXPORTED box_t box_create(size_t size, int * err); EXPORTED void box_destroy(box_t ins); EXPORTED int box_pop(box_t ins, int * err); EXPORTED void box_push(box_t ins, int elem, int * err); 41
  • 109. C with classes - box.cpp struct box { box(std::size_t size) : real{size} {} impl::box real; }; 42
  • 110. C with classes - box_impl.hpp struct box { box(std::size_t const size); void push(int const element); int pop(); private: std::unique_ptr<int[]> m_data{}; std::size_t const m_capacity{}; std::size_t m_size{}; }; 43
  • 111. C with classes - cbox.c box_t box = box_create(3, NULL); if(!box) { printf("Failed to create boxn"); box_destroy(box); return EXIT_FAILURE; } box_push(box, 42, NULL); int err = 0; int val = box_pop(box, &err); 44
  • 112. C with classes - cppbox.cpp int main() { cppug::box box{3}; box.push(42); std::cout << box.pop() << 'n'; } 45
  • 113. Going the extra mile - gobox.bo /* #cgo LDFLAGS: -lbox -Llib #include "box.h" */ import "C" import "runtime" import "fmt" type Box struct { c_box C.box_t } 46
  • 114. Going the extra mile - gobox.bo func NewBox(size int) *Box { box := new(Box) runtime.SetFinalizer(box, func(ins *Box) { C.box_destroy(ins.c_box) }) box.c_box = C.box_create(C.size_t(size), nil) if box.c_box == nil { panic("Failed to initializ object") } return box } 46
  • 115. Going the extra mile - gobox.bo func (obj *Box) Push(val int) { err := C.int(0) C.box_push(obj.c_box, C.int(val), &err) if err != 0 { panic("Failed to push value") } } 46
  • 116. Going the extra mile - gobox.bo func (obj *Box) Pop() int { err := C.int(0) val := C.box_pop(obj.c_box, &err) if err != 0 { panic("Failed to pop value") } return int(val) } 46
  • 117. Going the extra mile - gobox.bo func main() { box := NewBox(3) box.Push(42) val := box.Pop() fmt.Println("Popped ", val) } 46
  • 118. What is worth noting 47
  • 119. What is worth noting ◦ It is basically PIMPL for C 47
  • 120. What is worth noting ◦ It is basically PIMPL for C ◦ Exceptions do not propagate well 47
  • 121. What is worth noting ◦ It is basically PIMPL for C ◦ Exceptions do not propagate well ◦ You need to manage the memory 47
  • 122. What is worth noting ◦ It is basically PIMPL for C ◦ Exceptions do not propagate well ◦ You need to manage the memory ◦ You need to define ”contracts” 47
  • 125. Summing up ◦ The C++ ABI is not stable 48
  • 126. Summing up ◦ The C++ ABI is not stable ◦ Neither is it ”compatible” 48
  • 127. Summing up ◦ The C++ ABI is not stable ◦ Neither is it ”compatible” ◦ C tends to be very stable 48
  • 128. Summing up ◦ The C++ ABI is not stable ◦ Neither is it ”compatible” ◦ C tends to be very stable ◦ But hard to get right 48
  • 129. Summing up ◦ The C++ ABI is not stable ◦ Neither is it ”compatible” ◦ C tends to be very stable ◦ But hard to get right ◦ Other languages can use C libraries 48
  • 130. What I did not tell you 49
  • 131. What I did not tell you ◦ Tools can do the heavy lifting 49
  • 132. What I did not tell you ◦ Tools can do the heavy lifting ◦ Some languages provide their own tools 49
  • 133. What I did not tell you ◦ Tools can do the heavy lifting ◦ Some languages provide their own tools ◦ SWIG supports a lot of languages 49
  • 134. What I did not tell you ◦ Tools can do the heavy lifting ◦ Some languages provide their own tools ◦ SWIG supports a lot of languages ◦ You do not need C++ on the bottom-end 49
  • 135. What I did not tell you ◦ Tools can do the heavy lifting ◦ Some languages provide their own tools ◦ SWIG supports a lot of languages ◦ You do not need C++ on the bottom-end ▷ https://github.com/dns2utf8/hour_glass ▷ Uses Rust on the bottom-end 49
  • 139. Where to read more ◦ http://en.cppreference.com/w/cpp/language/pimpl ◦ https://en.wikipedia.org/wiki/Application_binary_interface ◦ https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html ◦ https://gcc.gnu.org/wiki/Visibility 51