MODERN AND LUCID C++ ADVANCED

Similar documents
Modern and Lucid C++ Advanced for Professional Programmers. Part 12 Advanced Library Design. Department I - C Plus Plus Advanced

6 Architecture of C++ programs

05-01 Discussion Notes

C++ Modern and Lucid C++ for Professional Programmers

Modern and Lucid C++ Advanced for Professional Programmers. Part 3 Move Semantics. Department I - C Plus Plus Advanced

Fast Introduction to Object Oriented Programming and C++

Exception Namespaces C Interoperability Templates. More C++ David Chisnall. March 17, 2011

Appendix. Grammar. A.1 Introduction. A.2 Keywords. There is no worse danger for a teacher than to teach words instead of things.

Midterm Review. PIC 10B Spring 2018

PIC 10A Objects/Classes

A brief introduction to C++

C++ Modern and Lucid C++ for Professional Programmers

A Crash Course in (Some of) Modern C++

Arizona s First University. More ways to show off--controlling your Creation: IP and OO ECE 373

Outline. 1 Function calls and parameter passing. 2 Pointers, arrays, and references. 5 Declarations, scope, and lifetimes 6 I/O

Tokens, Expressions and Control Structures

AIMS Embedded Systems Programming MT 2017

Short Notes of CS201

A Generic Non-intrusive Smart Pointer Implementation

Kurt Schmidt. October 30, 2018

COMP6771 Advanced C++ Programming

CS201 - Introduction to Programming Glossary By

G52CPP C++ Programming Lecture 20

Modern and Lucid C++ Advanced for Professional Programmers. Part 1 C Plus Plus Recap. Department I - C Plus Plus

2 ADT Programming User-defined abstract data types

Assertions and Exceptions

CS3157: Advanced Programming. Outline

the gamedesigninitiative at cornell university Lecture 7 C++ Overview

C++\CLI. Jim Fawcett CSE687-OnLine Object Oriented Design Summer 2017

Programming in C and C++

Introduction to C++ Professor Hugh C. Lauer CS-2303, System Programming Concepts

CSE 333 Midterm Exam July 24, Name UW ID#

Exception Safe Coding

Chapter 1 Getting Started

CSE 333. Lecture 11 - constructor insanity. Hal Perkins Paul G. Allen School of Computer Science & Engineering University of Washington

Interview Questions of C++

MARKING KEY The University of British Columbia MARKING KEY Computer Science 260 Midterm #1 Examination 12:30 noon, Tuesday, February 14, 2012

Remedial C Now that we can actually use it Pete Williamson

Elevate your Code to Modern C++ with Automated Tooling. Peter Sommerlad

C++ Primer for CS175

Part X. Advanced C ++

The issues. Programming in C++ Common storage modes. Static storage in C++ Session 8 Memory Management

Basic Types, Variables, Literals, Constants

Friendship in Service of Testing

COMP6771 Advanced C++ Programming

Pointers, Dynamic Data, and Reference Types

boost::enable_if Deep Down Boostimagical Fun Christian Bay and Kasper A Andersen Department of Computer Science University of Copenhagen

Absolute C++ Walter Savitch

G52CPP C++ Programming Lecture 9

A brief introduction to C programming for Java programmers

QUIZ Friends class Y;

Auxiliary class interfaces

C++ Mini-Course. Part 1: Mechanics Part 2: Basics Part 3: References Part 4: Const Part 5: Inheritance Part 6: Libraries Part 7: Conclusion. C Rulez!

04-24/26 Discussion Notes

C++ Mini-Course. Part 1: Mechanics Part 2: Basics Part 3: References Part 4: Const Part 5: Inheritance Part 6: Libraries Part 7: Conclusion. C Rulez!

Object-Oriented Principles and Practice / C++

Objects Managing a Resource

Mastering Data Abstraction or Get nit-picky on design advantages

Introduction to C++ Part II. Søren Debois. Department of Theoretical Computer Science IT University of Copenhagen. September 12th, 2005

GEA 2017, Week 4. February 21, 2017

G52CPP C++ Programming Lecture 16

04-19 Discussion Notes

Ch. 12: Operator Overloading

SERIOUS ABOUT SOFTWARE. Qt Core features. Timo Strömmer, May 26,

Introduction to C++ Systems Programming

COMP 2355 Introduction to Systems Programming

Chapter 15 - C++ As A "Better C"

RAII and Smart Pointers. Ali Malik

C++ PROGRAMMING LANGUAGE: DYNAMIC MEMORY ALLOCATION AND EXCEPTION IN C++. CAAM 519, CHAPTER 15

Part III. Advanced C ++

Modern and Lucid C++ Advanced for Professional Programmers. Part 7 Compile-Time Computation. Department I - C Plus Plus

CS

And Even More and More C++ Fundamentals of Computer Science

C++ Coding Standards. 101 Rules, Guidelines, and Best Practices. Herb Sutter Andrei Alexandrescu. Boston. 'Y.'YAddison-Wesley

Variables. Data Types.

Value Semantics and Concept Based Polymorphism Sean Parent Principal Scientist Adobe Systems Incorporated. All Rights Reserved.

Object-Oriented Programming for Scientific Computing


CSE 374 Programming Concepts & Tools. Hal Perkins Fall 2015 Lecture 19 Introduction to C++

C++ Modern and Lucid C++ for Professional Programmers

ECE 449 OOP and Computer Simulation Lecture 12 Resource Management II

COMP322 - Introduction to C++ Lecture 02 - Basics of C++

CSC1322 Object-Oriented Programming Concepts

Problem Solving with C++

CE221 Programming in C++ Part 1 Introduction

Function Overloading

CSci 4061 Introduction to Operating Systems. Programs in C/Unix

G Programming Languages - Fall 2012

This examination has 11 pages. Check that you have a complete paper.

Programmazione. Prof. Marco Bertini

Written by John Bell for CS 342, Spring 2018

C++ Programming Lecture 7 Software Engineering Group

a data type is Types

Advanced C++ Programming Workshop (With C++11, C++14, C++17) & Design Patterns

CS93SI Handout 04 Spring 2006 Apr Review Answers

Classes and Objects. Class scope: - private members are only accessible by the class methods.

Introducing C++ to Java Programmers

An Introduction to C++

Object-Oriented Programming

QUIZ. What is wrong with this code that uses default arguments?

Transcription:

Informatik MODERN AND LUCID C++ ADVANCED for Professional Programmers Prof. Peter Sommerlad Thomas Corbat Director of IFS Research Assistant Rapperswil, FS 2016

LIBRARY API/ABI DESIGN PIMPL IDIOM HOURGLASS INTERFACE

Opaque Types In C++ often also incomplete type name known, but not content by forward declaration can use pointers and references in declarations class WizardImpl; // forward declaration class Wizard { std::shared_ptr<class WizardImpl> pimpl; // ; but not dereference values without definition C only uses pointers universally opaque pointer in C: void * can be cast to any other pointer type validity and avoidance of undefined behavior is left to the programmer sometimes char* is used for memory of a given size (see BoundedBuffer) template <typename T> void *makeopaque(t *ptr){ return ptr; template <typename T> T *ptrcast(void *p){ return static_cast<t*>(p); int main() { int i{42; void * const pi{makeopaque(&i); cout << *ptrcast<int>(pi)<< endl; 3

The PIMPL Idiom - Pointer to IMPLementation Problem: even minor changes in a class definition requires clients to re-compile e.g. changing a type of a private member variable Compilation Firewall allow changes to implementation without the need to re-compile users read self-study material! it can be used to shield client code from implementation changes, e.g., when you want to provide a binary library as a DLL/shared library for clients and want to be able to update the library without having the client code to be re-compiled -> you must not change header files your client relies upon Put in the "exported" header file a class consisting of a "Pointer to IMPLementation" plus all public member functions to be used 4

A "Wizard" class All internals and details are exposed to those interacting with class Wizard makes changes hard and will require recompile #include "WizardIngredients.h" #include <string> #include <vector> shouldn't be shown to "muggles" class Wizard { // all magic details visible MagicWand wand; std::vector<spellbook> books; std::vector<potion> potions; std::string name; std::string searchforspell(std::string wish); Potion mixpotion(std::string recipe); void castspell(std::string spell); void applypotion(potion phial); public: Wizard(std::string name="rincewind"):name{name, wand{{ std::string domagic(std::string wish); ; 5

PIMPL with shared_ptr<class Impl> Minimal header all details hidden in implementation delegation to Impl #include <memory> #include <string> class Wizard { std::shared_ptr<class WizardImpl> pimpl; public: Wizard(std::string name); std::string domagic(std::string wish); ; #include "Wizard.h" #include "WizardIngredients.h" #include <vector> class WizardImpl { // all magic details hidden MagicWand wand; std::vector<spellbook> books; std::vector<potion> potions; std::string name; std::string searchforspell(std::string wish){ return "abracadabra"; Potion mixpotion(std::string recipe) { return Potion{; void castspell(std::string spell){ void applypotion(potion phial){ public: WizardImpl(std::string name):name{name, wand{{ std::string domagic(std::string wish){ ; auto spell=searchforspell(wish); if (! spell.empty()){ castspell(spell); return "wootsh"; auto potion=mixpotion(wish); applypotion(potion); return "zapp"; Wizard::Wizard(std::string name) :pimpl{std::make_shared<wizardimpl>(name) { std::string Pimpl::Wizard::doMagic(std::string wish) { return pimpl->domagic(wish); 6

PIMPL with unique_ptr<class Impl> must declare & define destructor default destructor can not delete forward declared class for unique_ptr member variable #include <memory> #include <string> class Wizard { std::unique_ptr<class WizardImpl> pimpl; public: Wizard(std::string name); ~Wizard(); // must declare dtor std::string domagic(std::string wish); ; #include "WizardIngredients.h" #include <vector> class WizardImpl { // all magic details public std::string name; MagicWand wand; std::vector<spellbook> books; std::vector<potion> potions; std::string searchforspell(std::string wish){ return "abracadabra"; Potion mixpotion(std::string recipe) { return Potion{; void castspell(std::string spell){ void applypotion(potion phial){ public: WizardImpl(std::string name):name{name, wand{{ std::string domagic(std::string wish){ auto spell=searchforspell(wish); if (! spell.empty()){ ; castspell(spell); return "wootsh"; auto potion=mixpotion(wish); applypotion(potion); return "zapp"; Wizard::Wizard(std::string name) :pimpl{std::make_unique<wizardimpl>(name) { Wizard::~Wizard() = default; // must define it //after WizardImpl is known std::string Wizard::doMagic(std::string wish) { return pimpl->domagic(wish); 7

Design Decisions with PIMPL Idiom How should objects be copied? no-copying - but moving unique_ptr<class Impl> pimpl; declare dtor, move-ops as =default shallow-copying - sharing implementation shared_ptr<class Impl> pimpl; deep-copying - C++ default unique_ptr<class Impl> pimpl; with DIY copy-ctor employing Impl s copy-ctor future standard might bring clone_ptr<> Can pimpl == nullptr? IMHO: never! Can one inherit from PIMPL class? better not 8

Hourglass Interfaces DLL APIs work best (and cross platform compatible) with C only We ignore the Windows burden of providing DLL-export and DLL-import syntax (see self-study) C++ can provide C-compatible function interfaces using extern C in front of a declaration C-APIs are error-prone and can be tedious to use C++ exceptions do not pass nicely across a C-API Foreign language bindings (e.g. for Python etc) often expect C-APIs API - application programming interface if stable, you do not need to change your code, if something changes ABI - application binary interface if stable, you can use and share DLLs/shared libraries without recompilation C++ Client C++ Client API Header-only C Header C ABI DLL Not universally applicable, but very common C++ Library 9

Starting Point C++ Wizard Class Wizards can learn spells and how to mix potions Trying to do unknown magic results in an exception potions make zapp spells make wootsh How can we transport that to a C API? void cancreatedefaultwizard() { const Wizard magician { ; ASSERT_EQUAL("Rincewind",magician.getName()); void cancreatewizardwithname(){ const Wizard magician { "Petrosilius Zwackelmann" ; ASSERT_EQUAL("Petrosilius Zwackelmann",magician.getName()); void wizardlearnsspellandcanrecall(){ Wizard magician{; magician.learnspell("expelliarmus"); ASSERT_EQUAL("wootsh",magician.doMagic("Expelliarmus")); void wizardmixespotionandcanapply(){ Wizard magician{; magician.mixandstorepotion("polyjuice Potion"); ASSERT_EQUAL("zapp",magician.doMagic("Polyjuice Potion")); void uknownmagicfails(){ Wizard magician{; ASSERT_THROWS(magician.doMagic( Expecto Patronum! ),std::logic_error); 10

Background C API design Abstract data types can be represented by pointers ultimate abstract pointer is void * Member functions map to functions taking the abstract data type pointer as first argument Requires Factory and Disposal functions to manage object lifetime typedef struct Wizard *wizard; typedef struct Wizard const *cwizard; char const *domagic(wizard w, char const *wish, error_t *out_error); void learnspell(wizard w, char const *spell); void mixandstorepotion(wizard w, char const *potion); char const *wizardname(cwizard w); wizard createwizard(char const *name, error_t *out_error); void disposewizard(wizard todispose); Strings can only be represented by char * need to know who will be responsible for memory make sure not to return pointers to temporary objects! Exceptions do not work across a C API typedef struct Error *error_t; char const *error_message(error_t error); void error_dispose(error_t error); 11

What parts of C++ can be used in an extern C interface // comments functions, but not templates or variadic no overloading in C! C primitive types (char, int, double, void) Pointers, including function pointers forward-declared structs pointers to those are opaque types! are used for abstract data types enums (without class or base type!) if using from C must embrace it with extern C when compiling it with C++ otherwise names do not match, because of mangling #ifndef WIZARD_H_ #define WIZARD_H_ // redefine the following for windows' compilers #define WIZARD_EXPORT_DLL #ifdef cplusplus extern "C" { #endif typedef struct Wizard *wizard; typedef struct Wizard const *cwizard; typedef struct Error *error_t; char const *error_message(error_t error); void error_dispose(error_t error); wizard createwizard(char const *name, error_t *out_error); void disposewizard(wizard todispose); char const *domagic(wizard w, char const *wish, error_t *out_error); void learnspell(wizard w, char const *spell); void mixandstorepotion(wizard w, char const *potion); char const *wizardname(cwizard w); #ifdef cplusplus #endif #endif /* WIZARD_H_ */ mark functions with C linkage no overloads! 12

Implementing Opaque Types - use a C-linkage trampolin class Wizard class must be implemented To allow full C++ including templates, we use a trampolin class NB: Wizard vs. unseen::wizard_ Stefanus hairpoll example has non-compilable/ non-standard code in the trampolin extern "C" { struct Wizard { // C linkage trampolin Wizard(char const *name) :wiz{name{ unseen::wizard wiz; ; #ifndef WIZARDHIDDEN_H_ #define WIZARDHIDDEN_H_ #include <string> #include <vector> #include "WizardIngredients.h" namespace unseen{ struct Wizard { private: std::string const name; MagicWand const wand; std::vector<spell> books; std::vector<potion> potions; std::string searchforspell(std::string const &wish); Potion mixpotion(std::string const &recipe); void castspell(spell spell); void applypotion(potion phial); public: Wizard(std::string name="rincewind"):name{name, wand{{ char const * domagic(std::string const &wish); void learnspell(std::string const &newspell); void mixandstorepotion(std::string const &potion); char const * getname() const { return name.c_str() ; ; #endif /* WIZARDHIDDEN_H_ */ 13

Dealing with Exceptions -> map to error code reference parameter Remember the 5 ways to deal with errors! can t use references in C API, must use pointers to pointers In case of an error, allocate error value on the heap must provide a disposal function to clean up Can use C++ types internally (std::string) it is safe to return the char const * because caller owns the object providing the memory typedef struct Error *error_t; wizard createwizard(char const *name, error_t *out_error); char const *error_message(error_t error); void error_dispose(error_t error); // in C++ implementation: extern "C"{ struct Error { std::string message; ; WIZARD_EXPORT_DLL char const* error_message(error_t error) { return error->message.c_str(); WIZARD_EXPORT_DLL void error_dispose(error_t error) { delete error; 14

Creating error messages from Exceptions call the function body and catch exceptions map them to an Error object set the pointer pointed to by out_error use pointer to pointer as reference to pointer passed out_error must not be nullptr! 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; // usage: wizard createwizard(const char* name, error_t *out_error) { wizard result=nullptr; translateexceptions(out_error,[&]{ result = new Wizard{name; // new or ctor might throw ); return result; 15

Error Handling Client side Client-side C++ usage requires mapping error codes back to exceptions unfortunately exception type doesn t map through but can use a generic standard exception runtime_error and keep the message dedicated RAII class for disposal temporary object with throwing destructor strange but possible automatic type conversion passes the address of its guts (opaque) tricky, take care you don t leak when creating the object struct ErrorRAII{ ErrorRAII(error_t error):opaque{error{ ~ErrorRAII(){ if (opaque) error_dispose(opaque); error_t opaque; ; struct ThrowOnError{ ThrowOnError()=default; ~ThrowOnError() noexcept(false){ if(error.opaque){ throw std::runtime_error{error_message(error.opaque); operator error_t*(){return &error.opaque; private: ErrorRAII error{nullptr; ; // usage: (in separate namespace to avoid linkage problems) struct Wizard{ Wizard(std::string const &who="rincewind") :wiz{createwizard(who.c_str(),throwonerror{){ std::string domagic(std::string const &wish){ return ::domagic(wiz,wish.c_str(),throwonerror{); 16

Really hiding DLL content With the Gnu compiler (and clang I presume) -fvisibility=hidden can be added to suppress exporting symbols must mark exported ABI functions with default visibility visibility refers to dynamic library/object file export of symbols Windows: declspec(dllexport) see also hairpoll demo project #define WIZARD_EXPORT_DLL attribute ((visibility ("default"))) WIZARD_EXPORT_DLL char const *error_message(error_t error); WIZARD_EXPORT_DLL void error_dispose(error_t error); WIZARD_EXPORT_DLL wizard createwizard(char const *name, error_t *out_error); WIZARD_EXPORT_DLL void disposewizard(wizard todispose); WIZARD_EXPORT_DLL char const *domagic(wizard w, char const *wish, error_t *out_error); WIZARD_EXPORT_DLL void learnspell(wizard w, char const *spell); WIZARD_EXPORT_DLL void mixandstorepotion(wizard w, char const *potion); WIZARD_EXPORT_DLL char const *wizardname(cwizard w); for more on gcc visibility (expert-level knowledge) see https://gcc.gnu.org/wiki/visibility 17

Completing the Client side Here the complete view of the client side Wizard class Calls C functions from global namespace namespace prefix needed for synonyms to member functions Header-only inline functions delegating Need to take care of passed and returned Pointers, esp. char * do not pass/return dangling pointers struct Wizard{ Wizard(std::string const &who="rincewind") :wiz{createwizard(who.c_str(),throwonerror{){ ~Wizard(){ disposewizard(wiz); std::string domagic(std::string const &wish){ return ::domagic(wiz,wish.c_str(),throwonerror{); void learnspell(std::string const &spell){ ::learnspell(wiz,spell.c_str()); void mixandstorepotion(std::string const &potion){ ::mixandstorepotion(wiz,potion.c_str()); char const * getname() const { return wizardname(wiz) ; private: Wizard(Wizard const &)=delete; Wizard& operator=(wizard const &)=delete; ; wizard wiz; 18

Test cases still run just a little namespace trickery to avoid name conflicts, because of using Wizard in two different namespaces namespace { using wizard_client::wizard; void cancreatedefaultwizard() { const Wizard magician { ; ASSERT_EQUAL("Rincewind",magician.getName()); void cancreatewizardwithname(){ const Wizard magician { "Petrosilius Zwackelmann" ; ASSERT_EQUAL("Petrosilius Zwackelmann",magician.getName()); void wizardlearnsspellandcanrecall(){ Wizard magician{; magician.learnspell("expelliarmus"); ASSERT_EQUAL("wootsh",magician.doMagic("Expelliarmus")); void wizardmixespotionandcanapply(){ Wizard magician{; magician.mixandstorepotion("polyjuice Potion"); ASSERT_EQUAL("zapp",magician.doMagic("Polyjuice Potion")); void uknownmagicfails(){ Wizard magician{; ASSERT_THROWS(magician.doMagic("Expecto Patronum!"),std::runtime_error); 19

Wrap up Library API and ABI design can be tricky for third party users only really a problem if not in-house or all open source even with open source libraries, re-compiles can be a burden there are just too many compiler options plus DLL versioning API stability can be important PIMPL idiom helps with avoiding client re-compiles not easily applicable with heavily templated code -> that often is header-only ABI stability is even more important when delivering DLLs/shared libraries only relevant when not header only C linkage safe, but crippling - Hourglass-Interfaces allow shielding C++ clients from the crippled ABI still easy to make mistakes 20