C++ for numerical computing - part 2

Similar documents
Distributed and Parallel Systems Group University of Innsbruck. Simone Pellegrini, Radu Prodan and Thomas Fahringer

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

Part X. Advanced C ++

Auto - a necessary evil?

Type Inference auto for Note: Note:

COMP6771 Advanced C++ Programming

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

Object-Oriented Programming for Scientific Computing

I m sure you have been annoyed at least once by having to type out types like this:

CSCI-1200 Data Structures Fall 2018 Lecture 22 Hash Tables, part 2 & Priority Queues, part 1

Part XI. Algorithms and Templates. Philip Blakely (LSC) C++ Introduction 314 / 370

QUIZ. 1. Explain the meaning of the angle brackets in the declaration of v below:

CSCI-1200 Data Structures Spring 2016 Lecture 22 Priority Queues, part II (& Functors)

6.096 Introduction to C++

Outline. 1 About the course

C++ Basics. Data Processing Course, I. Hrivnacova, IPN Orsay

CS 247: Software Engineering Principles. C++ Templates. Reading: Eckel, Vol. 2 Ch. 5 Templates in Depth. U Waterloo CS247 (Spring 2017) p.

Functors. Cristian Cibils

printf( Please enter another number: ); scanf( %d, &num2);

Basics of Java: Expressions & Statements. Nathaniel Osgood CMPT 858 February 15, 2011

A brief introduction to C++

C++ for numerical computing

CS201 Some Important Definitions

PROGRAMMING FUNDAMENTALS

What will happen if we try to compile, link and run this program? Do you have any comments to the code?

Fast Introduction to Object Oriented Programming and C++

C++ Programming Lecture 6 Software Engineering Group

Functions, Pointers, and the Basics of C++ Classes

Templates Templates are functions or classes that are parameterized. We have already seen a few in STL:

QUIZ. What are 3 differences between C and C++ const variables?

Suppose that you want to use two libraries with a bunch of useful classes and functions, but some names collide:

CE221 Programming in C++ Part 1 Introduction

use static size for this buffer

These are notes for the third lecture; if statements and loops.

CSCI-1200 Data Structures Spring 2018 Lecture 14 Associative Containers (Maps), Part 1 (and Problem Solving Too)

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

Data Abstraction. An Abstraction for Inductive Data Types. Philip W. L. Fong.

Lambdas in unevaluated contexts

Object-oriented programming. and data-structures CS/ENGRD 2110 SUMMER 2018

Overload Resolution. Ansel Sermersheim & Barbara Geller ACCU / C++ June 2018

Botet C++ generic overload functions P0051

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

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

C++ (Non for C Programmer) (BT307) 40 Hours

M.EC201 Programming language

6.001 Notes: Section 6.1

CPSC 427a: Object-Oriented Programming

Chapter 1 Getting Started

QUIZ. Source:

Dr Richard Greenaway

PIC 10A Objects/Classes

MARKING KEY The University of British Columbia MARKING KEY Computer Science 260 Midterm #2 Examination 12:30 noon, Thursday, March 15, 2012

CS93SI Handout 04 Spring 2006 Apr Review Answers

Computer Science II Lecture 1 Introduction and Background

10. Functions (Part 2)

TDDD38 - Advanced programming in C++

Overload Resolution. Ansel Sermersheim & Barbara Geller Amsterdam C++ Group March 2019

C++ Data Types. 1 Simple C++ Data Types 2. 3 Numeric Types Integers (whole numbers) Decimal Numbers... 5

P1267R0: Custom Constraint Diagnostics

Outline. Function calls and results Returning objects by value. return value optimization (RVO) Call by reference or by value?

CCReflect has a few interesting features that are quite desirable for DigiPen game projects:

Programming Languages and Compilers (CS 421)

Ch. 12: Operator Overloading

CSci 4223 Lecture 12 March 4, 2013 Topics: Lexical scope, dynamic scope, closures

C The new standard

Object-Oriented Principles and Practice / C++

COMP6771 Advanced C++ Programming

Intro to Algorithms. Professor Kevin Gold

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

Biostatistics 615/815 - Lecture 2 Introduction to C++ Programming

Tutorial 7. Y. Bernat. Object Oriented Programming 2, Spring Y. Bernat Tutorial 7

Instantiation of Template class

Expansion statements. Version history. Introduction. Basic usage

Lambdas in unevaluated contexts

Absolute C++ Walter Savitch

7.1 Optional Parameters

CSE 374 Programming Concepts & Tools. Hal Perkins Spring 2010

Short Notes of CS201

calling a function - function-name(argument list); y = square ( z ); include parentheses even if parameter list is empty!

Weiss Chapter 1 terminology (parenthesized numbers are page numbers)

Operator overloading

Distributed Real- Time Control Systems 13/14

CS107 Handout 08 Spring 2007 April 9, 2007 The Ins and Outs of C Arrays

Recap: Can Specialize STL Algorithms. Recap: Can Specialize STL Algorithms. C++11 Facilities to Simplify Supporting Code

ADTs in C++ In C++, member functions can be defined as part of a struct

Type Conversion. and. Statements

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Functions. Using Bloodshed Dev-C++ Heejin Park. Hanyang University

CS201 - Introduction to Programming Glossary By

Lesson 13 - Vectors Dynamic Data Storage

CS 247: Software Engineering Principles. C++ lambdas, bind, mem_fn. U Waterloo CS247 (Spring 2017)

Exercise 1.1 Hello world

Lecture Transcript While and Do While Statements in C++

QUIZ How do we implement run-time constants and. compile-time constants inside classes?

C++ for Python Programmers

CSCI-1200 Data Structures Fall 2018 Lecture 3 Classes I

Function Templates. Consider the following function:

EL2310 Scientific Programming

Computer Programming. Basic Control Flow - Loops. Adapted from C++ for Everyone and Big C++ by Cay Horstmann, John Wiley & Sons

Let return Be Direct and explicit

Transcription:

C++ for numerical computing - part 2 Rupert Nash r.nash@epcc.ed.ac.uk 1 / 36

Recap 2 / 36

Iterators std::vector<double> data = GetData(n); // C style iteration - fully explicit for (auto i=0; i!= n; ++i) { data[i] *= 2; // Old C++ style - hides some details for (auto iter = data.begin(); iter!= data.end(); ++iter) { *iter *= 2; // New range-based for for (auto& item : data) { item *= 2; 3 / 36

Templates in a bit more detail Recall from the first C++ lecture template<class T> T sum(t a, T b) { return a+b; We then used this without specifying, explicitly, what type T was - e.g.: int x = 1, y = 2; auto z = sum(x, y); The compiler is doing template argument deduction. This means it examines the types of the expressions given as arguments to the function and then tries to choose a T such that the type of the argument and the expected type match. 4 / 36

Templates in a bit more detail Important to note that the template parameter T and the type of function arguments might be different (but related) template<class T> void f(t x); template<class T> void g(t& x); template<class T> void h(const T& x); 5 / 36

Templates in a bit more detail Full rules are quite complex See Meyer's Effective Modern C++ chapter 1 - free online https://www.safaribooksonline.com/library/view/effective-modernc/9781491908419/ch01.html But usually you can ignore these and just think about: 1. Whether you want to copy the argument or not - if you don't want a copy add a reference & 2. Whether you can handle a const argument - if so add a const qualifier 6 / 36

Auto The auto keyword follows very nearly the same rules as template argument deduction but can trip you up more easily. :( Even more important to express your intent: Use auto x when you want to copy Use auto &x when you want a reference to original item and may modify it Use auto const &x when you want a reference to original item and will not modify it Use the last whenever possible 7 / 36

Standard algorithms library 8 / 36

Standard algorithms library The library includes many (around 100) function templates that implement algorithms, like "count the elements that match a criteria", or "divide the elements based on a condition". These all use iterators to specify the data they will work on, e.g., count might use a vector's begin() and end() iterators. #include <algorithm> std::vector<int> data = Read(); int nzeros = std::count(data.begin(), data.end(), 0); bool is_prime(int x); int nprimes = std::count_if(data.begin(), data.end(), is_prime); 9 / 36

Standard algorithms library Possible implementation: template<class InputIt, class UnaryPredicate> intptr_t count_if(inputit first, InputIt last, UnaryPredicate p) { intptr_t ans = 0; for (; first!= last; ++first) { if (p(*first)) { ans++; return ans; (Unary one argument, a predicate is a Boolean-valued function.) 10 / 36

Key algorithms Algorithm Description for_each Apply function to each element in the range. count/count_if Return number of elements matching. find/find_if Return iterator to first element matching or end if no match. copy/copy_if Copy input range (if predicate true) to destination transform Apply the function to input elements storing in destination (has overload work on two inputs at once) swap Swap two values - used widely! You may wish to provide a way to make your types swappable (see cpprefence.com) sort Sort elements in ascending order using either operator< or the binary predicate provided lower_bound/ upper_bound Given a sorted range, do a binary search for value. 11 / 36

for_each One of the simplest algorithms: apply a function to every element in a range. template< class InputIt, class UnaryFunction > UnaryFunction for_each(inputit first, InputIt last, UnaryFunction f); Why bother? Clearly states your intent Cannot get an off-by-one errors / skip elements Works well with other range-based algorithms Concise if your operation is already in a function However often a range-based for loop is better! 12 / 36

Many ways to iterate // C style iteration - fully explicit for (auto i=0; i!= n; ++i) { data[i] *= 2; // Old C++ style - hides some details for (auto iter = data.begin(); iter!= data.end(); ++iter) { *iter *= 2; // New range-based for for (auto& item : data) { item *= 2; // Algorithms library std::for_each(data.begin(), data.end(), double_in_place); 13 / 36

transform A very powerful function with two variants: one takes a single range, applies a function to each element, and stores the result in an output iterator. template<class InputIt, class OutputIt, class UnaryOperation > OutputIt transform(inputit first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op ); This is basically the equivalent of map from functional programming. std::vector<float> data = GetData(); std::transform(data.begin(), data.end(), data.begin(), double_in_place); You can use your input as the output. 14 / 36

Motivation Implementations have been written and tested by your compiler authors. The library may be able to do platform-specific optimizations that you probably don't want to maintain. They form a language to communicate with other programmers what your code is really doing. for (auto it = images.begin(); it!= images.end(); ++it) { if (ContainsCat(*it)) { catpics.push_back(*it); vs std::copy_if(images.begin(), images.end(), ContainsCat, std::back_inserter(catpics)); 15 / 36

Lambda functions 16 / 36

Algorithms need functions Very many of the algorithms just discussed need you to provide a function-like object as an argument for them to use. If you have to declare a new function for a one-off use in an algorithm call that is inconvenient and moves the code away from its use site. Worse would be to have to create a custom functor each time. 17 / 36

A verbose example struct SquareAndAddConstF { const float c; SquareAndAddConstF(float c_) : c(c_) { float operator()(float x) { return x*x + c; ; std::vector<float> SquareAndAddConst(const std::vector<float>& x, float c) { std::vector<float> ans; ans.resize(x.size()); std::transform(x.begin(), x.end(), ans.begin(), SquareAndAddConst(c)); return ans; 18 / 36

Lambdas to the rescue A lambda function a.k.a. a closure is a function object that does not have a name like the functions you have seen so far. You can define one inside a function body You can bind them to a variable, call them and pass them to other functions. They can capture local variables (either by value or reference). They have a unique, unknown type so you may have to use auto or pass them straight to a template argument. 19 / 36

A less verbose example std::vector<float> SquareAndAddConst(const std::vector<float>& x, float c) { std::vector<float> ans; ans.resize(x.size()); auto func = [c](double z) { return z*z + c; ; std::transform(x.begin(), x.end(), ans.begin(), func); return ans; 20 / 36

A less less verbose example std::vector<float> SquareAndAddConst(const std::vector<float>& x, float c) { std::vector<float> ans; ans.resize(x.size()); std::transform(x.begin(), x.end(), ans.begin(), [c](double z) { return z*z + c; ); return ans; 21 / 36

Anatomy of a lambda [captures](arg-list) -> ret-type {function-body [... ] new syntax that indicates this is a lambda expression arg-list exactly as normal functionbody zero or more statements as normal -> rettype new C++11 syntax to specify the return type of a function - can be skipped if return type is void or function body is only a single return statement. captures zero or more comma separated captures You can capture a value by copy (just put its name: local) or by reference (put an ampersand before its name: &local). 22 / 36

Anatomy of a lambda [captures](arg-list) -> ret-type {function-body This creates a function object of a unique unnamed type (hence you must use auto to store it in a local variable. You can call this like any object that overloads operator(): std::cout << "3^2 +c = " << func(3) << std::endl; Note that because it does not have a name, it cannot take part in overload resolution. 23 / 36

Quick Quiz What does the following do? [](){(); 24 / 36

Quick Quiz What does the following do? Nothing [](){(); 25 / 36

Uses STL algorithms library (or similar) - pass small one-off pieces of code in freely std::sort(molecules.begin(), molecules.end(), [](const Mol& a, const Mol& b) { return a.charge < b.charge; ); To do complex initialisation on something, especially if it should be const. const auto rands =[&size] -> std::vector<float> { std::vector<float> ans(size); for (auto& el: ans) { el = GetRandomNumber(); return ans; (); // Note parens to call! 26 / 36

Rules of thumb Be careful with what you capture! If your lambda is used locally - including passed to algorithms - capture by reference. This ensures there are no copies and ensures any changes made propagate. If you use the lambda elsewhere, especially if you return it, capture by value. Recall references to locals are invalid after the function returns! References to other objects may still be valid but hard to keep track of. Keep lambdas short - more than 10 lines you should think about moving it to a function/functor instead. 27 / 36

Traits 28 / 36

Type traits Important C++ generic programming technique, used across the standard library The "if-then-else" of types Provide a template class that has typedefs/member functions that specify the configurable parts Your generic algorithm then uses this class, specialised on the type it is working on, to select behaviour You do not have to change the source of either the algorithm or the working type 29 / 36

STL traits Several headers contain these: The header <type_traits> has lots of information for handling types. E.g. std::is_pointer<int>::value would be false. std::numeric_limits<t> gives lots of parameters for the built in number types, such as largest and smallest values, whether they are integer or floating types, etc. Other traits are used everywhere behind the scenes for efficiency. 30 / 36

Real example Suppose you are writing a wrapper around MPI to allow use without having to always say MPI_FLOAT - since the compiler knows the types already! class Comm { public: template <class T> void send(const std::vector<t>& data, int dest, int tag); ; auto& world = Mpi::CommWorld(); std::vector<int> data = {2, 4, 6, 8; world.send(data, other_rank, tag); 31 / 36

Real example Simplified implementation: template <class T> void Comm::send(const std::vector<t>& data, int dest, int tag) { MPI_Send(reinterpret_cast<void*>(data.data()), data.size(), DATATYPE, dest, tag, m_comm); 32 / 36

Real example Simplified implementation: template <class T> void Comm::send(const std::vector<t>& data, int dest, int tag) { MPI_Send(reinterpret_cast<void*>(data.data()), data.size(), DATATYPE, dest, tag, m_comm); How to fill in the right type? For the types in the standard, we could provide a specialisation for each one: template <> void Comm::send<int>(const std::vector<int>& data, int dest, int tag) { MPI_Send(reinterpret_cast<void*>(data.data()), data.size(), MPI_INT, dest, tag, m_comm); 33 / 36

Real example But there are lots of types (say M) and lots of MPI calls (say N) to be wrapped, so we have to write M * N specialisations... Also how to add any custom types? User would have to mess around with our library. 34 / 36

Real example Create a traits class that tells our functions how to handle different types: template <class T> struct DataTypeTraits { // Don't provide a default definition! static MPI_Datatype Get(); ; template <class T> void Comm::send(const std::vector<t>& data, int dest, int tag) { MPI_Send(reinterpret_cast<void*>(data.data()), data.size(), DataTypeTraits<T>::Get(), dest, tag, m_comm); 35 / 36

Real example Then we can then provide a specialised defintion for all the types we can handle: template<> MPI_Datatype DataTypeTraits<int>::Get() { return MPI_INT; template<> MPI_Datatype DataTypeTraits<float>::Get() { return MPI_FLOAT; // etc If we try to communicate a data type we haven't specialised for, we will get a compile time error! 36 / 36