SlideShare una empresa de Scribd logo
1 de 46
Descargar para leer sin conexión
Hourglass Interfaces 
for C++ APIs 
Stefanus Du Toit 
Thalmic Labs 
@stefanusdutoit 
Image © Erik Fitzpatrick, CC BY 2.0
Motivation and 
Introduction
Shared Libraries 
Library Code Data
Shared Libraries 
Interface 
Code Data 
Library
Shared Libraries 
Interface 
Code Data 
Client! 
Program 
Library 
Code Data
Shared Libraries 
Interface 
Code Data 
Client! 
Program 
Library 
Code Data 
ABI! 
! 
Common libraries
Different ABIs? Subtle problems… 
Class A 
Class B
Different ABIs? Subtle problems… 
Class A 
Class B 
Debug Build 
Release Build
My Goal for Libraries 
Get out of the client’s way.
My Goal for Libraries 
Don’t make my clients: 
• build my code if they don’t want to 
• change how they build their code 
• rebuild their code because I shipped a fix in mine 
• learn a new language if they don’t want to
Why does this help? 
• Avoids ABI-related binary compatibility issues 
• Enforces no use of non-trivial C++ types in the 
binary interface (e.g. std::string) 
• Keeps internal layout of data types a secret 
• Makes binding to other languages much easier 
! 
• Still get all the convenience of C++ at both ends
Example: libhairpoll 
• A library for creating hair-related polls, taking 
votes, and tallying the results
hairpoll.h 
typedef struct hairpoll* hairpoll_t; 
! 
hairpoll_t hairpoll_construct(const char* person); 
void hairpoll_destruct(hairpoll_t poll); 
! 
int32_t hairpoll_add_option(hairpoll_t hairpoll, 
const char* name, const char* image_url); 
! 
void hairpoll_vote(hairpoll_t hairpoll, int32_t option); 
! 
typedef void (*hairpoll_result_handler_t)(void* client_data, 
const char* name, int32_t votes, const char* html); 
! 
void hairpoll_tally(const hairpoll_t hairpoll, 
hairpoll_result_handler_t handler, void* client_data);
HairPoll class - client-facing 
class HairPoll { 
public: 
HairPoll(std::string person); 
~HairPoll(); 
! 
int addOption(std::string name, std::string imageUrl); 
void vote(int option); 
! 
struct Result { 
std::string name; 
int votes; 
std::string html; 
}; 
std::vector<Result> results() const; 
! 
private: 
hairpoll_t _opaque; 
};
Best Practices
Parts of C to use in the thin API 
• C89 + const and // comments 
• Functions, but no variadic functions 
• C primitive types (char, void, etc.) 
• Pointers 
• forward-declared structs 
• enumerations 
• Function pointers are OK, too
Opaque Types 
typedef struct hairpoll* hairpoll_t; 
! 
void hairpoll_vote( 
hairpoll_t hairpoll, 
int32_t option 
);
Opaque Types 
extern "C" 
struct hairpoll { 
Poll actual; 
};
Opaque Types 
extern "C" 
struct hairpoll { 
template<typename... Args> 
hairpoll(Args&&... args) 
: actual(std::forward<Args>(args)...) 
{ 
} 
! 
Poll actual; 
};
Opaque Types 
class Poll { 
public: 
Poll(std::string person); 
! 
struct option { 
option(std::string name, std::string url); 
! 
std::string name; 
std::string url; 
int votes; 
}; 
! 
std::string person; 
std::vector<option> options; 
};
Opaque Types 
hairpoll_t 
hairpoll_construct(const char* person) 
{ 
return new hairpoll(person); 
} 
! 
void hairpoll_destruct(hairpoll_t poll) 
{ 
delete poll; 
}
Integral types 
Prefer stdint.h types (int32_t, int64_t, etc.) over plain 
C integer types (short, int, long): 
! 
void hairpoll_vote( 
hairpoll_t hairpoll, 
int32_t option 
);
Integral types 
Using char is OK, though: 
! 
int32_t 
hairpoll_add_option( 
hairpoll_t hairpoll, 
const char* name, 
const char* image_url 
);
Enumerations 
typedef enum motu_alignment { 
heroic_warrior = 0, 
evil_warrior = 1, 
}; 
! 
int32_t motu_count( 
int32_t alignment 
);
Error Handling 
void hairpoll_vote( 
hairpoll_t hairpoll, 
int32_t option, 
error_t* out_error 
);
Error Handling 
typedef struct error* error_t; 
! 
const char* error_message( 
error_t error 
); 
! 
void error_destruct(error_t error); 
! 
! 
void hairpoll_vote(hairpoll_t hairpoll, int32_t option, error_t* out_error);
Errors → Exceptions 
struct Error { 
Error() : opaque(nullptr) {} 
~Error() { if (opaque) { error_destruct(opaque); } } 
error_t opaque; 
}; 
! 
class ThrowOnError { 
public: 
~ThrowOnError() noexcept(false) { 
if (_error.opaque) { 
throw std::runtime_error(error_message(_error.opaque)); 
} 
} 
! 
operator error_t*() { return &_error.opaque; } 
! 
private: 
Error _error; 
};
Errors → Exceptions 
void hairpoll_vote(hairpoll_t hairpoll, int32_t option, error_t* out_error); 
! 
void vote(int option) { 
return hairpoll_vote( 
_opaque, 
option, 
ThrowOnError{} 
); 
}
Exceptions → Errors 
template<typename Fn> 
bool translateExceptions(error_t* out_error, Fn&& fn) 
{ 
try { 
fn(); 
} catch (const std::exception& e) { 
*out_error = new error{e.what()}; 
return false; 
} catch (...) { 
*out_error = new error{"Unknown internal error"}; 
return false; 
} 
return true; 
}
Exceptions → Errors 
void hairpoll_vote(const hairpoll_t poll, 
int32_t option, error_t* out_error) 
{ 
translateExceptions(out_error, [&]{ 
if (option < 0 || 
option >= poll->actual.options.size()) { 
throw std::runtime_error("Bad optn index"); 
} 
! 
poll->actual.options[option].votes++; 
}); 
}
Callbacks 
typedef void (*hairpoll_result_handler_t)( 
void* client_data, 
const char* name, 
int32_t votes, 
const char* html 
); 
! 
void hairpoll_tally(const hairpoll_t hairpoll, 
hairpoll_result_handler_t handler, 
void* client_data, error_t* out_error);
Using lambdas as callbacks 
std::vector<Result> results() const { 
std::vector<Result> ret; 
! 
auto addResult = [&ret](const char* name, int32_t votes, 
const char* html){ 
ret.push_back(Result{name, votes, html}); 
}; 
! 
auto callback = [](void* client_data, const char* name, int32_t votes, 
const char* html){ 
auto fn = static_cast<decltype(&addResult)>(client_data); 
(*fn)(name, votes, html); 
}; 
! 
hairpoll_tally(_opaque, callback, &addResult, ThrowOnError{}); 
! 
return ret; 
}
Symbol visibility 
#if defined(_WIN32) || defined(__CYGWIN__) 
#ifdef hairpoll_EXPORTS 
#ifdef __GNUC__ 
#define HAIRPOLL_EXPORT __attribute__ ((dllexport)) 
#else 
#define HAIRPOLL_EXPORT __declspec(dllexport) 
#endif 
#else 
#ifdef __GNUC__ 
#define HAIRPOLL_EXPORT __attribute__ ((dllimport)) 
#else 
#define HAIRPOLL_EXPORT __declspec(dllimport) 
#endif 
#endif 
#else 
#if __GNUC__ >= 4 
#define HAIRPOLL_EXPORT __attribute__ ((visibility ("default"))) 
#else 
#define HAIRPOLL_EXPORT 
#endif 
#endif
Symbol visibility 
#include "visibility.h" 
! 
… 
! 
HAIRPOLL_EXPORT 
hairpoll_t hairpoll_construct(const char* person); 
! 
HAIRPOLL_EXPORT 
void hairpoll_destruct(hairpoll_t poll); 
! 
…
Symbol visibility 
On Clang and GCC, add to your compile: 
! 
-fvisibility=hidden 
! 
(applies to library only)
Remember to extern “C”! 
#ifdef __cplusplus 
extern "C" { 
#endif 
! 
// C API goes here 
! 
#ifdef __cplusplus 
} // extern "C" 
#endif
Lifetime and Ownership 
• Keep it simple: construct (if needed) and destruct 
• Always use RAII on the client side! 
• Can use refcounting (e.g. shared_ptr) both 
internally to the library and in the client 
• For callbacks, you can use the stack
Interfacing with those 
“other” languages
Foreign Function Interfaces 
• “something that allows calling code written in one 
language from another language” 
• C = de-facto standard 
• Languages with support for calling C functions in 
shared libraries include: 
• Common Lisp, Java, JavaScript, Matlab, .NET 
(C#, F#, etc.), Python, Ruby, OCaml, basically everything…
Hair Polls in Python 
hairpoll = lib.hairpoll_construct("Stefanus Du Toit", None) 
! 
skeletor = lib.hairpoll_add_option(hairpoll, "Skeletor", 
"<long URL omitted>", None) 
beast = lib.hairpoll_add_option(hairpoll, "Beast Man", 
"<long URL omitted>", None) 
! 
lib.hairpoll_vote(hairpoll, skeletor, None) 
lib.hairpoll_vote(hairpoll, beast, None) 
lib.hairpoll_vote(hairpoll, beast, None) 
! 
def print_result(client_data, name, votes, html): 
print name, votes 
! 
lib.hairpoll_tally(hairpoll, hairpoll_result_handler(print_result), 
None, None) 
! 
lib.hairpoll_destruct(hairpoll)
Hair Polls In Python 
from ctypes import * 
! 
lib = CDLL("libhairpoll.dylib") 
lib.hairpoll_construct.restype = c_void_p 
lib.hairpoll_construct.argtypes = [c_char_p, c_void_p] 
! 
lib.hairpoll_destruct.restype = None 
lib.hairpoll_destruct.argtypes = [c_void_p] 
! 
lib.hairpoll_add_option.restype = c_int 
lib.hairpoll_add_option.argtypes = [c_void_p, c_char_p, c_char_p, c_void_p] 
! 
lib.hairpoll_vote.restype = None 
lib.hairpoll_vote.argtypes = [c_void_p, c_int, c_void_p] 
! 
hairpoll_result_handler = CFUNCTYPE(None, c_void_p, c_char_p, c_int, c_char_p) 
lib.hairpoll_tally.restype = None 
lib.hairpoll_tally.argtypes = [c_void_p, hairpoll_result_handler, c_void_p, 
c_void_p]
Summary 
• Hourglass = C++ on top of C89 on top of C++ 
• Avoids ABI issues, sneaky dependencies, etc. 
• Hides object layout and other implementation 
details 
• Makes FFI bindings easy
Q&A 
http://blog.sduto.it/ @stefanusdutoit

Más contenido relacionado

La actualidad más candente

SIGGRAPH Asia 2008 Modern OpenGL
SIGGRAPH Asia 2008 Modern OpenGLSIGGRAPH Asia 2008 Modern OpenGL
SIGGRAPH Asia 2008 Modern OpenGL
Mark Kilgard
 
Rust-lang
Rust-langRust-lang

La actualidad más candente (20)

The Internals of "Hello World" Program
The Internals of "Hello World" ProgramThe Internals of "Hello World" Program
The Internals of "Hello World" Program
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in Rust
 
Low-level Shader Optimization for Next-Gen and DX11 by Emil Persson
Low-level Shader Optimization for Next-Gen and DX11 by Emil PerssonLow-level Shader Optimization for Next-Gen and DX11 by Emil Persson
Low-level Shader Optimization for Next-Gen and DX11 by Emil Persson
 
Intrinsic Methods in HotSpot VM
Intrinsic Methods in HotSpot VMIntrinsic Methods in HotSpot VM
Intrinsic Methods in HotSpot VM
 
Nginx Internals
Nginx InternalsNginx Internals
Nginx Internals
 
Introduction to Rust language programming
Introduction to Rust language programmingIntroduction to Rust language programming
Introduction to Rust language programming
 
OpenGL 4.6 Reference Guide
OpenGL 4.6 Reference GuideOpenGL 4.6 Reference Guide
OpenGL 4.6 Reference Guide
 
Linux Internals - Part I
Linux Internals - Part ILinux Internals - Part I
Linux Internals - Part I
 
Introduction to Skia by Ryan Chou @20141008
Introduction to Skia by Ryan Chou @20141008Introduction to Skia by Ryan Chou @20141008
Introduction to Skia by Ryan Chou @20141008
 
Analyze Virtual Machine Overhead Compared to Bare Metal with Tracing
Analyze Virtual Machine Overhead Compared to Bare Metal with TracingAnalyze Virtual Machine Overhead Compared to Bare Metal with Tracing
Analyze Virtual Machine Overhead Compared to Bare Metal with Tracing
 
Embedded Android : System Development - Part I
Embedded Android : System Development - Part IEmbedded Android : System Development - Part I
Embedded Android : System Development - Part I
 
Impacts of Sharding, Partitioning, Encoding, and Sorting on Distributed Query...
Impacts of Sharding, Partitioning, Encoding, and Sorting on Distributed Query...Impacts of Sharding, Partitioning, Encoding, and Sorting on Distributed Query...
Impacts of Sharding, Partitioning, Encoding, and Sorting on Distributed Query...
 
An introduction to Rust: the modern programming language to develop safe and ...
An introduction to Rust: the modern programming language to develop safe and ...An introduction to Rust: the modern programming language to develop safe and ...
An introduction to Rust: the modern programming language to develop safe and ...
 
Understand more about C
Understand more about CUnderstand more about C
Understand more about C
 
SIGGRAPH Asia 2008 Modern OpenGL
SIGGRAPH Asia 2008 Modern OpenGLSIGGRAPH Asia 2008 Modern OpenGL
SIGGRAPH Asia 2008 Modern OpenGL
 
Linux Initialization Process (1)
Linux Initialization Process (1)Linux Initialization Process (1)
Linux Initialization Process (1)
 
Rust-lang
Rust-langRust-lang
Rust-lang
 
A hands-on introduction to the ELF Object file format
A hands-on introduction to the ELF Object file formatA hands-on introduction to the ELF Object file format
A hands-on introduction to the ELF Object file format
 
Embedded Android : System Development - Part III
Embedded Android : System Development - Part IIIEmbedded Android : System Development - Part III
Embedded Android : System Development - Part III
 
Anatomy of the loadable kernel module (lkm)
Anatomy of the loadable kernel module (lkm)Anatomy of the loadable kernel module (lkm)
Anatomy of the loadable kernel module (lkm)
 

Destacado

Connecting C++ and JavaScript on the Web with Embind
Connecting C++ and JavaScript on the Web with EmbindConnecting C++ and JavaScript on the Web with Embind
Connecting C++ and JavaScript on the Web with Embind
Chad Austin
 
Programação simultânea em pares
Programação simultânea em paresProgramação simultânea em pares
Programação simultânea em pares
Herez Moise Kattan
 
Cleaning and shaping the root canal system
Cleaning and shaping the root canal systemCleaning and shaping the root canal system
Cleaning and shaping the root canal system
Parth Thakkar
 
How To Design A Good A P I And Why It Matters G O O G L E
How To Design A Good  A P I And Why It Matters    G O O G L EHow To Design A Good  A P I And Why It Matters    G O O G L E
How To Design A Good A P I And Why It Matters G O O G L E
guestbe92f4
 

Destacado (9)

Connecting C++ and JavaScript on the Web with Embind
Connecting C++ and JavaScript on the Web with EmbindConnecting C++ and JavaScript on the Web with Embind
Connecting C++ and JavaScript on the Web with Embind
 
Refactoring
RefactoringRefactoring
Refactoring
 
Software Development Practices Patterns
Software Development Practices PatternsSoftware Development Practices Patterns
Software Development Practices Patterns
 
Introduction to Mob Programming
Introduction to Mob ProgrammingIntroduction to Mob Programming
Introduction to Mob Programming
 
Programação simultânea em pares
Programação simultânea em paresProgramação simultânea em pares
Programação simultânea em pares
 
Nonvital pulp therapy in pediatric dentistry
Nonvital pulp therapy in pediatric dentistryNonvital pulp therapy in pediatric dentistry
Nonvital pulp therapy in pediatric dentistry
 
Cleaning and shaping the root canal system
Cleaning and shaping the root canal systemCleaning and shaping the root canal system
Cleaning and shaping the root canal system
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
How To Design A Good A P I And Why It Matters G O O G L E
How To Design A Good  A P I And Why It Matters    G O O G L EHow To Design A Good  A P I And Why It Matters    G O O G L E
How To Design A Good A P I And Why It Matters G O O G L E
 

Similar a Hourglass Interfaces for C++ APIs - CppCon 2014

Session 2 - Objective-C basics
Session 2 - Objective-C basicsSession 2 - Objective-C basics
Session 2 - Objective-C basics
Vu Tran Lam
 
Things to Remember When Developing 64-bit Software
Things to Remember When Developing 64-bit SoftwareThings to Remember When Developing 64-bit Software
Things to Remember When Developing 64-bit Software
Andrey Karpov
 
Presentation 2nd
Presentation 2ndPresentation 2nd
Presentation 2nd
Connex
 

Similar a Hourglass Interfaces for C++ APIs - CppCon 2014 (20)

Modern C++
Modern C++Modern C++
Modern C++
 
Gcrc talk
Gcrc talkGcrc talk
Gcrc talk
 
Bringing nullability into existing code - dammit is not the answer.pptx
Bringing nullability into existing code - dammit is not the answer.pptxBringing nullability into existing code - dammit is not the answer.pptx
Bringing nullability into existing code - dammit is not the answer.pptx
 
Glimpses of C++0x
Glimpses of C++0xGlimpses of C++0x
Glimpses of C++0x
 
devLink - What's New in C# 4?
devLink - What's New in C# 4?devLink - What's New in C# 4?
devLink - What's New in C# 4?
 
Solid C++ by Example
Solid C++ by ExampleSolid C++ by Example
Solid C++ by Example
 
Session 2 - Objective-C basics
Session 2 - Objective-C basicsSession 2 - Objective-C basics
Session 2 - Objective-C basics
 
Things to Remember When Developing 64-bit Software
Things to Remember When Developing 64-bit SoftwareThings to Remember When Developing 64-bit Software
Things to Remember When Developing 64-bit Software
 
What has to be paid attention when reviewing code of the library you develop
What has to be paid attention when reviewing code of the library you developWhat has to be paid attention when reviewing code of the library you develop
What has to be paid attention when reviewing code of the library you develop
 
Fundamentals of Programming Constructs.pptx
Fundamentals of  Programming Constructs.pptxFundamentals of  Programming Constructs.pptx
Fundamentals of Programming Constructs.pptx
 
Design Patterns in Modern C++
Design Patterns in Modern C++Design Patterns in Modern C++
Design Patterns in Modern C++
 
Vocabulary Types in C++17
Vocabulary Types in C++17Vocabulary Types in C++17
Vocabulary Types in C++17
 
Aspdot
AspdotAspdot
Aspdot
 
Programming using c++ tool
Programming using c++ toolProgramming using c++ tool
Programming using c++ tool
 
Lecture03
Lecture03Lecture03
Lecture03
 
Hooking signals and dumping the callstack
Hooking signals and dumping the callstackHooking signals and dumping the callstack
Hooking signals and dumping the callstack
 
C
CC
C
 
Presentation 2nd
Presentation 2ndPresentation 2nd
Presentation 2nd
 
Data types and Operators
Data types and OperatorsData types and Operators
Data types and Operators
 
Introduction Of C++
Introduction Of C++Introduction Of C++
Introduction Of C++
 

Último

Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Último (20)

Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 

Hourglass Interfaces for C++ APIs - CppCon 2014

  • 1. Hourglass Interfaces for C++ APIs Stefanus Du Toit Thalmic Labs @stefanusdutoit Image © Erik Fitzpatrick, CC BY 2.0
  • 4. Shared Libraries Interface Code Data Library
  • 5. Shared Libraries Interface Code Data Client! Program Library Code Data
  • 6. Shared Libraries Interface Code Data Client! Program Library Code Data ABI! ! Common libraries
  • 7. Different ABIs? Subtle problems… Class A Class B
  • 8. Different ABIs? Subtle problems… Class A Class B Debug Build Release Build
  • 9. My Goal for Libraries Get out of the client’s way.
  • 10. My Goal for Libraries Don’t make my clients: • build my code if they don’t want to • change how they build their code • rebuild their code because I shipped a fix in mine • learn a new language if they don’t want to
  • 11.
  • 12.
  • 13. Why does this help? • Avoids ABI-related binary compatibility issues • Enforces no use of non-trivial C++ types in the binary interface (e.g. std::string) • Keeps internal layout of data types a secret • Makes binding to other languages much easier ! • Still get all the convenience of C++ at both ends
  • 14. Example: libhairpoll • A library for creating hair-related polls, taking votes, and tallying the results
  • 15. hairpoll.h typedef struct hairpoll* hairpoll_t; ! hairpoll_t hairpoll_construct(const char* person); void hairpoll_destruct(hairpoll_t poll); ! int32_t hairpoll_add_option(hairpoll_t hairpoll, const char* name, const char* image_url); ! void hairpoll_vote(hairpoll_t hairpoll, int32_t option); ! typedef void (*hairpoll_result_handler_t)(void* client_data, const char* name, int32_t votes, const char* html); ! void hairpoll_tally(const hairpoll_t hairpoll, hairpoll_result_handler_t handler, void* client_data);
  • 16. HairPoll class - client-facing class HairPoll { public: HairPoll(std::string person); ~HairPoll(); ! int addOption(std::string name, std::string imageUrl); void vote(int option); ! struct Result { std::string name; int votes; std::string html; }; std::vector<Result> results() const; ! private: hairpoll_t _opaque; };
  • 18. Parts of C to use in the thin API • C89 + const and // comments • Functions, but no variadic functions • C primitive types (char, void, etc.) • Pointers • forward-declared structs • enumerations • Function pointers are OK, too
  • 19. Opaque Types typedef struct hairpoll* hairpoll_t; ! void hairpoll_vote( hairpoll_t hairpoll, int32_t option );
  • 20. Opaque Types extern "C" struct hairpoll { Poll actual; };
  • 21. Opaque Types extern "C" struct hairpoll { template<typename... Args> hairpoll(Args&&... args) : actual(std::forward<Args>(args)...) { } ! Poll actual; };
  • 22. Opaque Types class Poll { public: Poll(std::string person); ! struct option { option(std::string name, std::string url); ! std::string name; std::string url; int votes; }; ! std::string person; std::vector<option> options; };
  • 23. Opaque Types hairpoll_t hairpoll_construct(const char* person) { return new hairpoll(person); } ! void hairpoll_destruct(hairpoll_t poll) { delete poll; }
  • 24. Integral types Prefer stdint.h types (int32_t, int64_t, etc.) over plain C integer types (short, int, long): ! void hairpoll_vote( hairpoll_t hairpoll, int32_t option );
  • 25. Integral types Using char is OK, though: ! int32_t hairpoll_add_option( hairpoll_t hairpoll, const char* name, const char* image_url );
  • 26. Enumerations typedef enum motu_alignment { heroic_warrior = 0, evil_warrior = 1, }; ! int32_t motu_count( int32_t alignment );
  • 27. Error Handling void hairpoll_vote( hairpoll_t hairpoll, int32_t option, error_t* out_error );
  • 28. Error Handling typedef struct error* error_t; ! const char* error_message( error_t error ); ! void error_destruct(error_t error); ! ! void hairpoll_vote(hairpoll_t hairpoll, int32_t option, error_t* out_error);
  • 29. Errors → Exceptions struct Error { Error() : opaque(nullptr) {} ~Error() { if (opaque) { error_destruct(opaque); } } error_t opaque; }; ! class ThrowOnError { public: ~ThrowOnError() noexcept(false) { if (_error.opaque) { throw std::runtime_error(error_message(_error.opaque)); } } ! operator error_t*() { return &_error.opaque; } ! private: Error _error; };
  • 30. Errors → Exceptions void hairpoll_vote(hairpoll_t hairpoll, int32_t option, error_t* out_error); ! void vote(int option) { return hairpoll_vote( _opaque, option, ThrowOnError{} ); }
  • 31. Exceptions → Errors template<typename Fn> bool translateExceptions(error_t* out_error, Fn&& fn) { try { fn(); } catch (const std::exception& e) { *out_error = new error{e.what()}; return false; } catch (...) { *out_error = new error{"Unknown internal error"}; return false; } return true; }
  • 32. Exceptions → Errors void hairpoll_vote(const hairpoll_t poll, int32_t option, error_t* out_error) { translateExceptions(out_error, [&]{ if (option < 0 || option >= poll->actual.options.size()) { throw std::runtime_error("Bad optn index"); } ! poll->actual.options[option].votes++; }); }
  • 33. Callbacks typedef void (*hairpoll_result_handler_t)( void* client_data, const char* name, int32_t votes, const char* html ); ! void hairpoll_tally(const hairpoll_t hairpoll, hairpoll_result_handler_t handler, void* client_data, error_t* out_error);
  • 34. Using lambdas as callbacks std::vector<Result> results() const { std::vector<Result> ret; ! auto addResult = [&ret](const char* name, int32_t votes, const char* html){ ret.push_back(Result{name, votes, html}); }; ! auto callback = [](void* client_data, const char* name, int32_t votes, const char* html){ auto fn = static_cast<decltype(&addResult)>(client_data); (*fn)(name, votes, html); }; ! hairpoll_tally(_opaque, callback, &addResult, ThrowOnError{}); ! return ret; }
  • 35. Symbol visibility #if defined(_WIN32) || defined(__CYGWIN__) #ifdef hairpoll_EXPORTS #ifdef __GNUC__ #define HAIRPOLL_EXPORT __attribute__ ((dllexport)) #else #define HAIRPOLL_EXPORT __declspec(dllexport) #endif #else #ifdef __GNUC__ #define HAIRPOLL_EXPORT __attribute__ ((dllimport)) #else #define HAIRPOLL_EXPORT __declspec(dllimport) #endif #endif #else #if __GNUC__ >= 4 #define HAIRPOLL_EXPORT __attribute__ ((visibility ("default"))) #else #define HAIRPOLL_EXPORT #endif #endif
  • 36. Symbol visibility #include "visibility.h" ! … ! HAIRPOLL_EXPORT hairpoll_t hairpoll_construct(const char* person); ! HAIRPOLL_EXPORT void hairpoll_destruct(hairpoll_t poll); ! …
  • 37. Symbol visibility On Clang and GCC, add to your compile: ! -fvisibility=hidden ! (applies to library only)
  • 38. Remember to extern “C”! #ifdef __cplusplus extern "C" { #endif ! // C API goes here ! #ifdef __cplusplus } // extern "C" #endif
  • 39. Lifetime and Ownership • Keep it simple: construct (if needed) and destruct • Always use RAII on the client side! • Can use refcounting (e.g. shared_ptr) both internally to the library and in the client • For callbacks, you can use the stack
  • 40. Interfacing with those “other” languages
  • 41. Foreign Function Interfaces • “something that allows calling code written in one language from another language” • C = de-facto standard • Languages with support for calling C functions in shared libraries include: • Common Lisp, Java, JavaScript, Matlab, .NET (C#, F#, etc.), Python, Ruby, OCaml, basically everything…
  • 42. Hair Polls in Python hairpoll = lib.hairpoll_construct("Stefanus Du Toit", None) ! skeletor = lib.hairpoll_add_option(hairpoll, "Skeletor", "<long URL omitted>", None) beast = lib.hairpoll_add_option(hairpoll, "Beast Man", "<long URL omitted>", None) ! lib.hairpoll_vote(hairpoll, skeletor, None) lib.hairpoll_vote(hairpoll, beast, None) lib.hairpoll_vote(hairpoll, beast, None) ! def print_result(client_data, name, votes, html): print name, votes ! lib.hairpoll_tally(hairpoll, hairpoll_result_handler(print_result), None, None) ! lib.hairpoll_destruct(hairpoll)
  • 43. Hair Polls In Python from ctypes import * ! lib = CDLL("libhairpoll.dylib") lib.hairpoll_construct.restype = c_void_p lib.hairpoll_construct.argtypes = [c_char_p, c_void_p] ! lib.hairpoll_destruct.restype = None lib.hairpoll_destruct.argtypes = [c_void_p] ! lib.hairpoll_add_option.restype = c_int lib.hairpoll_add_option.argtypes = [c_void_p, c_char_p, c_char_p, c_void_p] ! lib.hairpoll_vote.restype = None lib.hairpoll_vote.argtypes = [c_void_p, c_int, c_void_p] ! hairpoll_result_handler = CFUNCTYPE(None, c_void_p, c_char_p, c_int, c_char_p) lib.hairpoll_tally.restype = None lib.hairpoll_tally.argtypes = [c_void_p, hairpoll_result_handler, c_void_p, c_void_p]
  • 44. Summary • Hourglass = C++ on top of C89 on top of C++ • Avoids ABI issues, sneaky dependencies, etc. • Hides object layout and other implementation details • Makes FFI bindings easy
  • 45.