ECE326 PROGRAMMING LANGUAGES

Lecture 1 : Course Introduction

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Course Instructor

 Kuei (Jack) Sun  Contact Information  Use Piazza  Send me (Kuei Sun) a private post  Not “Instructors”, otherwise the TAs will also see it  http://piazza.com/utoronto.ca/fall2019/ece326  Sign up to the course to get access  Office: PRA371 (office hours upon request only)  Research Interests  Systems programming, software optimization, etc.

2 Course Information

 Course Website  http://fs.csl.toronto.edu/~sunk/ece326.html  Lecture notes, tutorial slides, assignment handouts  Quercus  Grade posting, course evaluation, lab group sign-up!  Piazza  Course announcement, course discussion  Assignment discussion  Lab TAs will read and answer relevant posts periodically

3 Course Information

 No required textbook  Exam questions will come from lectures, tutorials, and assignments  See course website for suggested textbooks  Lab sessions  Get face-to-face help from a TA with your assignments  Tutorials  Cover supplementary materials not in lectures  Go through sample problems that may appear on exams

4 Volunteer Notetakers

 Help your peers  Improve your own notetaking skills  Receive Certificate of Recognition  Information:  https://www.studentlife.utoronto.ca/as/note-taking  Registration:  https://clockwork.studentlife.utoronto.ca/custom/misc/home.aspx  Notetakers needed for lectures and tutorials 🙏🙏

5 Background

 Programming Languages  A formal language consisting of a instructions to implement algorithms and perform tasks on computers  Thousands exist, more being made  Programming Paradigms  A way to categorize programming languages by features  Execution model  Code organization  Style and syntax  …etc

6 Course Outline

 Imperative Programming  Writing instructions and commands (APS105)  Object-Oriented Programming  Objects interacting with one another (ECE244)  Metaprogramming  Writing code that generates more code (CSC324)  Concurrent Programming  Multiple tasks overlapping in their executions (ECE344, CSC367)  Functional Programming  Programming in the style of evaluating mathematical functions (CSC324)

7 Course Objective

 Learn different ways of writing computer programs  Law of the instrument  “To the man who only has a hammer, everything he encounters begins to look like a nail.” – Abraham Maslow  One style may work better than others for a given problem  E.g. recursion vs. iteration  Analyze programming languages and their features  Semantic: more expressive power  Syntactic: easier to read/write  Optimization: improves performance and efficiency

8 Syntactic Sugar

 Example: Java  For Loop String[] fruits = { “Apple”, “Banana”, “Strawberry” }; for (int i = 0; i < fruits.length; i++) { system.out.println(fruits[i]); }  For-Each Loop

for (String f : fruits) { system.out.println(f); }  Same bytecode generated, but easier to read

9 Compiler Optimization

 Example:  restrict keyword  Informs compiler that a pointer has no alias

void add2(int * a, int * b, int * restrict c) { *a += *c; // normally, *c must be reloaded because a may // point to same address as c, which means that // *c could change after “*a += *c” is executed *b += *c; }  Compiler can thus be more aggressive in optimizing this function

10 Course Objective

 Learn implementations of language features  Cost-benefit analysis  E.g. automatic memory management  Avoids bugs associated with manual memory management  Requires garbage collection  Learn programming languages that matters  Popularity – large community of active developers  Easy to find help  Easy to find libraries or packages  Easy to find jobs

11 Popularity

 By job posting  Java: 65,986 jobs  Mainly used by web application developers  Python: 61,818 jobs  Known for high productivity  JavaScript: 38,018 jobs  The de facto language for modern browsers  C++: 36,798 jobs  Favored by game developers, large application software, …etc.  C#, PHP, Perl…

Source: https://www.codingdojo.com/blog/the-7-most-in-demand-programming-languages-of-2019 12 Popularity

 By contribution on GitHub  JavaScript  Java  Python  PHP  C++  C#, TypeScript, Shell, C, Ruby, …etc  Correlates well with popularity by job postings

Source: https://github.blog/2018-11-15-state-of-the-octoverse-top-programming-languages/

13 Popularity

 By Google search trends  JavaScript  Python  Java  Go  Google’s  Aims to simplify programming in multicore, networking environment  Elixir  Ruby, Kotlin, TypeScript, Scala, Clojure, ..etc.

Source: https://codeburst.io/10-top-programming-languages-in-2019-for-developers-a2921798d652

14 Popularity

 By growth in community  Kotlin  HCL  TypeScript  PowerShell  Rust  CMake  Go  Python, Groovy, SQLPL

Source: https://github.blog/2018-11-15-state-of-the-octoverse-top-programming-languages/

15 Lab Assignments

 4 Assignments  3 Programming languages  C++11  A newer version of C++ compared to what’s learned in ECE244  Python 3  Extremely popular high-level programming language  Rust  Fairly new systems programming language  Focuses on performance and safety  Requires you to really understand your own code

16 Lab Assignments

1. Easy Blackjack  Play the game automatically 2. Optimal strategy calculator  Play Blackjack optimally 3. Object-relational mapping  Seamless integration of objects with a relational database 4. Concurrent Database  Implement a fast toy database

17 Lab Assignments

 Expect significant efforts involved  You will need to spend time outside of lab hours  Learn new programming languages  Apply new programming techniques  Require time to design and implement your program  Require lots of time for debugging and performance improvement  Groups of 2  Sign up for a group on Quercus  Required for you to gain access to Git repository  Allows you to work on a common code base  Also used to submit your assignment

18 Lab Information  You may go to any of the lab sessions for TA help  Lab times and location posted on course website  After hour support (on Piazza)  Your peers!  Teaching Assistants  Assignment 1: Xue (Chloe) Geng  Assignment 2: Wenjun (Wendy) Qiu  Assignment 3: Szu-Chieh (Jeffrey) Fang  Assignment 4: Jemin Andrew Choi  Me  I won’t answer unless the problem is with the assignment

19 Assignment Grading

 Automated Tester  Runs a set of tests  Correctness  Performance  Gives you a mark on your solution  WYSIWYG  Mark you get from tester is what you expect for the assignment  Assignment Submission  Follow lab instructions  You may submit multiple times – do not submit last minute!

20 Assignment Policy

 No deadline extensions  Failure to submit on time will result in zero marks  There’s two of you per group  You are given 3 to 5 weeks per assignment  start early, don’t procrastinate!  Make sure you submit something for partial marks  Plagiarism and Cheating  Severe penalty  Grade reduction to suspension from University  Talk to me if you feel overwhelmed

21 Grading Scheme

 Midterm: 25%  October 29th, 4pm to 5:30pm, EXE320  Final: 45%  Assignments: 26%  5%, 6%, 7%, 8%, respectively  Tutorial Quizzes: 4%  12 tutorials, 12 quizzes  Only need to complete 10/12 quizzes for full marks

22 Next Lecture

 “Classifications of Programming Languages”

 Instructor: Kuei (Jack) Sun

 Course Website: http://fs.csl.toronto.edu/~sunk/ece326.html  Piazza Discussion: http://piazza.com/utoronto.ca/fall2019/ece326

23 ECE326 PROGRAMMING LANGUAGES

Lecture 2 : Comparison of Programming Languages

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Programming Languages

 Thousands of them  Goal  How to describe a programming language in one line?  Buzzwords!  Intended audience (i.e. programmers)  Performance  Expressiveness  Dominant style, syntax, or feature  etc…

2 By Intended Users

 General-purpose language  Used by various application domains  E.g. C++, Python, Java, …etc  Domain-specific language (DSL)  Specialized use  E.g. Galaxy  Designed for StarCraft 2’s map editor by Blizzard Entertainment  No clear boundary  Perl and SQL used to be considered DSL

3 By Support

 Standard Library  Provides frequently used functionality  E.g. libstdc++  Build tool  Automates creation of executables from source code  E.g. GNU make, Apache Ant  Compiler must support separate compilation  Package Manager  E.g. Python pip  Tool that automates installing, upgrading, and removing software and/or data in a consistent manner

4 By Implementation

 Compiled  Source code compiled to machine code (executable)  Runs faster/more efficient  Requires recompilation for any source code change  Can impact productivity, especially for large software  Portability concerns  Compiler must support all target architectures  Bugs may be introduced when porting to another architecture  E.g. long in C is architecture dependent – can break developer assumption  long long – at least 64 bits  In couple of years: long long long for 128-bits?

5 By Implementation

 Interpreted  Usually associated with high-level languages  Interpreter directly executes instructions of a program  Usually done in one of the following way: 1. Parse as you go  Syntax errors not caught until line is (about to be) executed  E.g. Bash script 2. Translate to some intermediate representation first  E.g. Python  Limited form of optimization possible

6 By Implementation

 Mixture of both  Virtual machine  Source code compiled to bytecode  Run bytecode on virtual machine  Virtual machine can run on any hardware platform (portability)  E.g. Java Virtual Machine (JVM)  Just -in-time compilation  Compile while program is running!  Compile when “needed”  E.g. if compilation can result in speed up over running on the interpreter

7 Programming Idioms

 A language-specific convention of accomplishing a task  E.g. the “Pythonic” way  Create a string of numbers delimited by white space  nums is an array of numbers

def slow(nums): def fast(nums): text = str(nums[0]) return “ ".join( for n in nums[1:]: map(str, nums)) text += " %d"%n return text

 fast is about 50% faster than slow (on my laptop) Performance can still be good if you know the way

∴ 8 By Level of Abstraction

- more abstraction - easier to write High-Level Python, Ruby Languages Java, Kotlin, Scala, Clojure Haskell, Racket Visual Basic, C# Systems C/C++ Languages Ada, D, Rust Swift (by Apple, for iOS apps) - more direct Low-Level Assembly Languages hardware access Languages Machine Languages - better performance

9 By Level of Abstraction

 Systems programming languages  Designed for performance  Allows some level of hardware awareness  Optimization hints (e.g. restrict, volatile, …etc)  Inline assembly  Still provides some high-level concepts  High-level programming languages  Designed for convenience  Designed for expressiveness  Functional programming languages, E.g. Haskell

10 By Programming Style

 Imperative Programming  Writing commands and statements, changing program state  Concerns with how a program operates  E.g. procedural programming, object-oriented programming  Declarative Programming  Writing expressions and desired result  Concerns with what a program should achieve  E.g. SQL queries, functional programming

SELECT firstName, LastName FROM Customers WHERE city=“Toronto”;

11 Turing Complete

 A programming language that can solve any computation problems (theoretically)  Requirements 1. Supports conditional branching  Allows for conditional (e.g. if else) and loops 2. Can work with unlimited amount of memory  Some languages are not Turing complete  E.g. regular languages, vanilla SQL, Datalog

12 By

 Type system  the rules governing the use of types in a program, and how types affect the program semantics

unsigned sum_of_squares(unsigned a, unsigned b); // which one of these is an error in C++11? char res = sum_of_squares(3.3, -2);  Statically Typed  Types of variables checked before runtime  Dynamically Typed  Types are checked at runtime, on the fly

13 Implicit Type Conversion

#include #include unsigned sum_of_squares(unsigned a, unsigned b) { return a*a + b*b; } int main(int argc, const char * argv[]) { int neg = atoi(argv[argc-1]); char res = sum_of_squares(3.3, neg); printf("sos = %d\n", res); return 0; } > ./err -2 sos = 13 > 😑😑 14 By Memory Safety  Protection from invalid memory access  Example  Buffer overflow  Use after free (dangling pointers)  Double free  Memory unsafe languages  Languages that allows arbitrary pointer arithmetic (C/C++)  Solution  runtime error detection (e.g. Java)  Static program analysis (e.g. Rust)

15 By

 Protection from incorrect use of a value

 Example union Foo {  untagged union (C/C++) int i; float f;  Union };  All member variables share the same memory location // in main()  Can lead to type-unsafe Foo u; u.i = atoi(argv[argc-1]); usage! printf("%f\n", u.f);  Solution: tagged union > ./union 1237864534  Adds a tag field to indicate 1640970.750000 which member is in use

16 By Features

 Generic Programming  Functions and classes defined in terms of parameterized types  Parameterized types  Instantiation of a generic type with actual type arguments  E.g. Java

public class Box { private T content; public void set(T repl) { this.content = repl; } public T get() { return content; } } … Box fruit_box = new Box();

17 By Features

 Reflective Programming  Introspection  Program has knowledge of itself at runtime  C++ Runtime Type Information (RTTI)  Enables correct functioning of dynamic_cast  Dynamic cast  Attempt to cast a base class to a derived class Dog * dog = dynamic_cast(animal); if (dog != nullptr) { dog->howl();  Reflection  Program can modify itself at runtime

18 By Simplicity

 E.g. Java vs. C++  E.g. Visual Basic  Simple is good  “Focus on debugging your application rather than debugging your programming language knowledge” – Zig developers  Design language for average programmers, not pros  Reduces chance of allowing for mistakes  Cheaper to hire 😒😒

19 By Syntax

 E.g. Off-side rule  Blocks in the language are expressed by indentation  Python: def sum(n): if n == 0: return 0 return 2*n + sum(n-1)  Free-form languages  Whitespace characters serve only as delimiters  Scheme: (define (sum n) (if (= n 0) 0 (+ (* n 2) (sum (- n 1))) ))

20 By Seriousness

 Esoteric programming languages  Programming as art, or a joke  Sometimes a subset of another language  (sanitized) E.g. JSF👀👀k  A subset of JavaScript  Uses only 6 characters: [ ] ( ) ! +  Prints “Hello world” in 26,924 characters [][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[] +!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[ +!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![] +[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+( !![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[] ]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]... 21 Next Week

 “Imperative Programming”  Reminder  Please sign up on Quercus for a group if you have not done so  First lab (for PRA0101 and PRA0103) starts next week  Course Website: http://fs.csl.toronto.edu/~sunk/ece326.html  Piazza Discussion: http://piazza.com/utoronto.ca/fall2019/ece326

22 ECE326 PROGRAMMING LANGUAGES

Lecture 3 : Anatomy of a Programming Language

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Administrative Matter

 Assignment 1 released  http://fs.csl.toronto.edu/~sunk/asst1.html  Due Date: Sunday September 29th, 11:59pm  Group Sign-Up  Deadline is Thursday, September 12th, 11:59pm  If you do not sign up before the deadline, you will be assigned a random group  Working Alone  Private message me first, otherwise you will be assigned a random partner 2 Python

 General purpose programming language  Interpreted  High-level of abstraction (from hardware)  Multi-paradigm  Dynamically typed  Comprehensive standard library  Easy to learn, hard to master

3 Interactive Mode

 Benefit of interpreted language  Can execute code as you type $ python3 Python 3.6.9 (default, Jul 21 2019, 14:33:59) [GCC 7.4.0] on cygwin Type "help", "copyright", "credits" or "license" for more information. >> x = 2 >> x + 3 5 >> exit()

$ …back in command prompt…

4 Source Code

 hello.py

#!/usr/bin/python3 Tells terminal which interpreter to run # this is a comment (like // in C++) (we will be using # main is not required Python3 for this course) print(“hello world!”)

> python3 hello.py Explicitly run with hello world! Python3 > chmod +x hello.py > ls –l hello.py Make the script -rwxr-xr-x 1 jack jack 41 Sep 10 12:10 executable hello.py > ./hello.py Run script as executable hello world!

5 Value

 A unit of representation or data  Program can use or manipulate a value  Associated with a type  E.g. 5 is a value of type integer (integer value)

6 Type

 A set of values, and (implicitly) a set of behaviours on those values  A way to express constraints  E.g. you should only use integer instructions on integer types  Conveys intended use  Conveys semantics (meaning)  Defines how values of that type is stored  E.g. Two’s complement for signed integers

7 Basic Types

 Usually correlates with machine data types  Integers  E.g. short, int, long in C++  In Python >> x = 5 >> type(x)  Floating-point numbers >> type(3.14)  The term “primitive” types usually means basic types 8 Built-in Types

 Types natively supported by the programming language  String >> type(‘a’) There is no “char” type in Python, a character is just a string of length 1  List You will learn about list, dictionary, >> type([]) and tuple in future lectures  Dictionary  Tuple >> type({}) >> type(())

9 Variable

 A name (i.e. identifier) associated with a memory location that contains a value  In Python, a variable is created through initialization >> foo = 6  In contrast, C++ requires variable declaration (e.g. int a;)  A variable has a type >> type(foo)  Name is used to refer to the stored value >> print(foo) 6

10 Keyword

 A word reserved by the programming language  Has special meaning  Cannot be used as an identifier  Common (between Python and C++)  if, else, for, while, continue, break, return, etc.  Python only  in, and, or, def, with, pass, yield, etc.

11 Literal and Constant

 Literal: represents a fixed value in source code  E.g. 5, “hello”, 3.3  Constant is same as variable, except its value cannot be changed once initialized  Python does not enforce constants  Programmer enforced via naming convention  Use all uppercase letters, words separated by underscore  MAX_LENGTH = 128  C++ const keyword: const int max_length = 128; max_length = 64; // error: assignment of read-only variable

12 Expression

 A combination of one or more values, operators, and functions that produces another value  This process is known as evaluation >> 3 + 5 8 >> -foo * math.sqrt(3) -10.392304845413264  An expression can have subexpressions  Innermost subexpression is evaluated first

13 Operators

 Symbols or keywords that behave like functions  However, they differ syntactically and/or semantically  E.g. add(1, 2) vs. 1 + 2  Follows operator precedence  Python uses same rules as mathematics (i.e. BEDMAS)

 Arithmetic >> 3**2 # 3 to the power of 2  Same as C++, 9 >> 3 + 6/2 # true divsion plus more… 6.0 >> 6//2 # floor division 3

14 Operators

 Relational  Performs comparison, returns true or false  Logical

C++ && || ! Python and or not

>> x = 5 >> y = 4 >> y + 1 == x and y < 5 or not print(“hello”) True  Remember short-circuit evaluation?

15 Statement

 A syntactic unit of imperative programming  Performs “some” action(s)  May contain expressions  Doest no evaluate to a value  If a statement is just an expression, the value is discarded/unused. // In C++, this is valid, but // doesn’t do anything (useful) x + 5;  Example  Loop control (i.e. continue and break)  Function return

16 Statement

 In C++  Terminates with a semicolon ; int a = 1 + 2 + 3 + 4 + 5 + 6 + 7 +  In Python 8 + 9 + 10;  Terminates at end of line  Multi-line statement requires line continuation  Use forward slash \ to continue statement to next line >> a = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + \ .. 9 + 10 55 >> 17 Compound Statement

 Contains one or more statements  Control Flow  Execution results in choice made on which path to take  Example  If statement  switch statement  while loop  for loop  Etc

18 Block

 Consists of one or more statements  Usually part of control flow  E.g. Conditional in Python if a == 3: Same as an empty body in C++ Reminder: pass Required for syntactic reason Do NOT mix else: tabs and b = 5 // in C++ spaces c = a + 6 if (a == 3) {} d = a - 3 else { … }  Off-side rule d = a – 3;  Blocks are expressed by their indentation

19 Conditional

 Python Syntax

if boolean-expression: block elif boolean-expression: block Recall in C++: boolean-expression elif : if (bool-expr) { block … … } else: block  No parentheses required for the conditions  Python does not have a switch statement

20 Assignment

 Assignment is an expression in C++  Evaluates to the assigned value int a, b; a = b = 5; // same as a = (b = 5); if ((b = foo(a))) // this is allowed assert(b != 0); // always true  Assignment is a statement in Python!

a = b = 5 # syntax error, if statement # same as below # expects an expression temp = 5 if a = 5: a = temp b = 3 b = temp

21 Function

 A reusable sequence of program instructions  Usually has an associated name  Also known as subroutines  In Python def foo(parameters…): block  Function can take zero or more parameters  Return type does not need to be specified  Can return different types  returns None if function did not end with return

22 Scope

 Name binding  Association of name to a variable, constant, or function  Region of code where binding is valid  Block Scope  Name valid within the block its declared in  Example: C++ if (a == 3) { int b = foo(); // b is valid inside this … // block only } std::cout << b; // error: b not in scope

23 Function Scope

 Python is different from C++  Local variable valid until end of function

def is_big(i): if i > 10: big = True # boolean true in Python x = foo(i) else: big = False # boolean false print(big) # this is valid, big in scope print(x) # this is invalid if i <= 10 return big # function returns a boolean

24 ECE326 PROGRAMMING LANGUAGES

Lecture 4 : Sequence Types

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Administrative Matter

 Group Sign-Up  Deadline is Today, September 12th, 11:59pm  If you do not sign up before the deadline, you will be assigned a random group  Working Alone  Private message me first, otherwise you will be assigned a random partner  Tutorial Improvement  N ew TA dedicated to tutorials only  Will do exercise(s) based on previous week’s lecture 2 Function Scope

 Python global variables are read-only inside functions CONST = 5 def foo(a): print(CONST+a) foo(3) # prints 8  Declaring variable of same name shadows the global CONST = 5 def foo(a): CONST = 6 # new local variable print(CONST+a) foo(3) # prints 9 print(CONST) # prints 5

3 Function Scope

 UnboundLocalError  Read global followed by write of same name CONST = 5 def foo(a): print(CONST+a) CONST = 6 # error: trying to write global  Solution(?): global keyword def foo(a): global CONST print(CONST+a) CONST = 6 foo(3) # prints 8 print(CONST) # prints 6 4 First Class Citizen  Can do everything that other entities can  Example  Can be assigned to a variable  Can be passed to or return from a function  Can be modified  E.g. type is first class in Python (Not in C++) >> a = int >> a() 0 >> type(int)  Everything in Python is an object, first class!

5 ∴ Sequence

 An ordered collection of values  Python list, string, tuple, range, …etc  Repetition of elements is allowed  E.g., in a string, letter a can appear more than once  Provides mapping from index to value  Like in C, uses zero-based index  Python provides many built-in sequence types  Makes programming easier and you more productive  Sequences are also objects  have methods

6 Python String

 Similar to C++ std::string  Can be declared with single or double quote >> a = ‘hello “world”’ # no need to escape >> print(a) hello “world” >> print(“good \”bye\””) # need to escape good “bye”  Strings are immutable  Cannot be changed once assigned  Copy is made for every operation

7 String Method

 Remove whitespace from both sides >> “ hello ”.strip() ‘hello’  Checks if string ends with substring >> “hello world”.endswith(“world”) True  C style format string >> “hello %s #%d”%(“world”, 42) ‘hello world #42’  Many more (look them up)  E.g. lower, format, isspace, replace, …

8 Python List

 Similar to C++ std::vector – more powerful  Can place objects of different types within >> a = [ 1, 2.5, “hello” ] # common initialization >> list() # another way (empty) []  Lists are mutable, they can be updated >> a.pop() # remove last element and return it “hello” >> a [1, 2.5] >> a.append(3) # add element to end of list >> a [1, 2.5, 3] 9 Alias

 Different names referring to same memory location  Problem: update one implicitly changes the other  Sometimes unintentional (frequent source of bugs) >> a = b = [] >> b = a # shallow copy of a >> a >> b[1] = 4 # update element [] >> a >> b [1, 4, 3] [] >> import copy >> a.append(5) >> d = copy.deepcopy(a) >> a >> d[0] = 5 [5] >> a Solution: make a >> b # why? [1, 4, 3] deep copy (instead [5] >> d of shallow copy) >> a = [1, 2, 3] [5, 4, 3] 10 List Methods

 Insertion >> a = [9, 2, 3, 4, 3] >> a.insert(0, 6) # insert 6 to index 0 >> a [6, 9, 2, 3, 4, 3]  Remove by index >> del a[1] # del expr is a statement >> a # a.pop(1) is an expression [6, 2, 3, 4, 3]  Remove by value >> a.remove(3) # removes first occurrence of 3 >> a [6, 2, 4, 3]

11 String and List Methods

 Tokenize >> “hello big world”.split(‘ ’) [‘hello’, ‘big’, ‘world’]  Join a list of string using a delimiter

>> ‘-’.join([‘hello’, ‘big’, ‘world’]) ‘hello-big-world’  Merge with another list  Sort list >> a = [5, 9] >> a = [5, 9, 1, 2] >> a.extend([1, 2]) >> a.sort() >> a >> a [5, 9, 1, 2] [1, 2, 5, 9]

12 Tuple

 Same as list, except immutable (not exactly, more on this later) >> a = 1, 2, “hello”, 4 >> a (1, 2, “hello”, 4) >> a[1] = 7 TypeError: 'tuple' object does not support item assignment  Can do neat tricks  Swap  Packing/Unpacking >> a = 3 >> foo() def foo(): >> b = 6 (5, 7) return 5, 7 >> a, b = b, a >> x, y = foo() >> a, b >> x (6, 3) 5

13 Common Operations

On Sequence Types

14 Index Operator

 Returns nth element of the sequence  syntax: sequence[n] >> b = [2, 3, 5, 7, 11, 13, 17] >> b[2] 5 >> b[7] IndexError: list index out of range >> b[-1] # returns last element 17 >> b[-8] IndexError: list index out of range  For List (mutable), can update element >> b[-1] += 6

15 Slicing

 Extracts subset of elements from sequence  sequence[i:j:k], i: start, j: end k: step  j th element is excluded from the slice >> b = [2, 3, 5, 7, 11, 13, 17] >> b[:2] # get 0th and 1st [2, 3] >> b[4:-1] # last element excluded [11, 13] >> b[4:] # last element included [11, 13, 17] >> b[::2] # skip every second element [2, 5, 11, 17] >> b[3::-1] # reverse list, from 4th element backwards [7, 5, 3, 2] 16 Relational Operator

 Sequence types are compared by value >> b = “hello” >> b[:5] == “hell” True >> a = [1, 2, 3] >> a > [8, -9] # lexicographical order False  Check for alias (compare by reference)  is operator >> a = b = [1, 2, 3] >> a is c >> a is b False True >> a == c >> c = [1, 2, 3] True 17 Built-in Functions

 Many operate on iterables  Iterable  An object that contains elements you can iterate through  Go through each element one after another  All sequence types are iterable!  E.g. sorted – returns a list of sorted elements >> b = [5, 9, 1, 2] >> sorted(“bad”) >> sorted(b) # returns a copy [‘a’, ‘b’, ‘d’] [1, 2, 5, 9] >> sorted((3, 2, 1)) >> b [1, 2, 3] [5, 9, 1, 2]

18 Foreach loop

>> for n in [2, 3, 5]: # enumerate is a built-in .. print(n+2) # function; returns a tuple 4 >> s = “world” 5 >> for i, c in enumerate(s): 7 .. print(“%d: %s”%(i, c)) >> for c in “hello”: 0: w .. print(c.upper()) 1: o H 2: r E 3: l L 4: d L O

19 Membership Operator

 Checks for existence of element >> 5 in [3, 6, “5”] False >> 5 in [5, “hello”, 3] True  Check for absence of element >> ‘a’ not in “banana” False >> ‘seed’ not in “banana” True

20 Length Function

>> len([1, 2, 3, 4]) 4 >> len(“hello”) 5 >> len([]) 0

# in Python import sys # arguments to program stored here argc = len(sys.argv) # argc (C++) is length of sys.argv

// in C++ int main(int argc, const char * argv[]) { … 21 Repetition and Concatenation

>> “hello ” * 3 ‘hello hello hello ’

>> [0] * 4 # common used to initialize list [0, 0, 0, 0]

>> a = “hello” >> b = “world” >> a + “ ” + b # concatenate three strings ‘hello world’

>> [1, 2] + [3, 4] # concatenate two lists [1, 2, 3, 4]

22 List Comprehension

 Creates sequence from an iterable  In set-builder notation  P(x) for x in iterable  P(x) for x in iterable if F(x)  P(x) if F(x) else Q(x) for x in iterable  Where P, F, Q are expressions

>> [ str(i) for i in range(5) ] [‘0’, ‘1’, ‘2’, ‘3’, ‘4’]

>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >> [ r[2] for r in matrix ] # 3rd element of sublists [3, 6, 9]

23 List Comprehension

 Can loop through multiple iterables! # sieve of eratosthenes >> composite = [ j for i in (2, 3, 5, 7) \ .. for j in range(i*2, 50, i) ] >> tuple( x for x in range(2, 50) if x not in composite ) (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47) >> sorted(set(range(2, 50)) – set(composite)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

>> data = [ 1, -5, 3, 7, -7, -6, -4, 0, 9, -2 ] >> [ x if x >= 0 else –x for x in data ] [ 1, 5, 3, 7, 7, 6, 4, 0, 9, 2 ]

>> [ w for w in “lorem ipsum dolor sit”.split() if ‘i’ in w ] [‘ipsum’, ‘sit’] 24 ECE326 PROGRAMMING LANGUAGES

Lecture 5 : Dictionary and File

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Dictionary

 As known as associative array  std::unordered_map in C++  Collection of key value pairs  All keys must be unique  Unordered  You cannot sort a dictionary  Implementation  Hash table  Search tree (e.g. red-black tree)

2 Dictionary

 { key1:value1, key2:value2, … } >> eng2sp = {‘one’: ‘uno’, ‘two’: ‘dos’, ‘three’: ‘tres’} >> eng2sp['one'] 'uno' >> eng2sp[4] # key not in dictionary KeyError: 4

>> eng2sp['four'] = 'cuatro’ # add key-value pair >> eng2sp {'one': 'uno', 'two': 'dos', 'three': 'tres', 'four': 'cuatro'}

>> 'two' in eng2sp # membership test on key True >> ‘dos’ in eng2sp # cannot use to check if value exists False 3 Key Requirement

 Dictionary key must be hashable  Related to immutable  Tuple is hashable if it has no reference to mutable objects

>> d = dict() # creates an empty dictionary >> d[1,2] = “hi” # OK >> a = [1, 2] >> d[a] = “bye” # not OK, list is mutable TypeError: unhashable type: 'list‘ >> t = (1, a) >> t (1, [1, 2]) >> d[t] = “bye” # not OK, tuple contains a mutable object TypeError: unhashable type: 'list‘

4 Building Dictionary

 zip built-in function  Creates n m-tuples from m sequences of length n  E ach element in tuple taken from same position of each sequence  Lazy iterable: computes as you loop through >> a = zip(“abcd”, range(4), [1.5, 2.5, 3.5, 4.5]) >> a # zip is ‘lazy’ (so is range) >> list(a) # consume the iterable [('a', 0, 1.5), ('b', 1, 2.5), ('c', 2, 3.5), ('d', 3, 4.5)]  Build dictionary with list of 2-tuples (key-value pairs) >> dict(zip(‘abcde’), range(5))) {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4} 5 Dictionary Methods

>> d = dict(zip(‘abcde’), range(5))) >> d.get(‘f’, -1) # avoids KeyError by providing default value -1

>> d.keys() dict_keys(['a', 'b', 'c', 'd', 'e']) >> d.values() dict_values([0, 1, 2, 3, 4])

>> ‘’.join(c for c in d) # loops through keys only ‘abcde’

>> for k, v in d.items(): # loops through (key, value) .. print(k+str(v), end=' ') a0 b1 c2 d3 e4

6 Dictionary Methods

>> d = dict(zip(‘abcdef’), range(6)))

>> del d[‘a’] # remove key ‘a’ and its value >> d {'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 6}

>> d.pop(‘c’) # same as above and return its value 2 >> d {'b': 1, 'd': 3, 'e': 4, 'f': 6}

>> d.update(dict(zip(‘abc’, range(6,9)))) # ‘b’ gets new value >> d {'b': 7, 'd': 3, 'e': 4, 'f': 6, 'a': 6, 'c': 8}

7 Dictionary Comprehension

 { K(x):V(x) for x in iterable }  And the other two forms Loop through unique s # make histogram of letters in string characters only >> s = ‘mississauga’ >> { c:s.count(c) for c in set(s) } {'u': 1, 'g': 1, 'a': 2, 's': 4, 'i': 2, 'm': 1} Convention for # keep only pairs where value > 3 “I don’t care” >> import random >> r = [ random.randint(0, 9) for __ in range(4) ] >> d = dict(zip(“abcd”, r)) >> { k:v for k,v in d.items() if v > 3 } {'b': 8, 'd': 5}

8 Function Arguments

 Keyword arguments  Specify an argument using parameter name  Useful for skipping over default arguments def pizza(size=14, dough=“regular”, sauce=“tomato”, \ cheese=“mozzarella”, toppings=[]): …

# dough is regular mine = pizza(16, sauce=“alfredo”, cheese=“cheddar”)

# all arguments before toppings use default values yours = pizza(toppings=[“bacon”, “pepperoni”, “salami”])

9 Variadic Function

 Allows you to take variable number of arguments  Both positional and/or keyword arguments def foo(*args, **kwargs): print(args, kwargs)

>> foo(1, 2, bar=3, baz=4) ((1, 2), {'baz': 4, 'bar': 3})

# only accepts keyword arguments def foo(**kwargs): print(kwargs)

>> foo(1, 2) TypeError: foo() takes exactly 0 arguments (2 given) 10 Dynamic Programming

 Divide and conquer  Breaks down problem into sub-problems  Solve sub-problems and combine results to form solution  Caches (saves) result of function for future reuse  Requires overlapping sub-problems  Example: Fibonacci series  F(n) = F(n-1) + F(n-2) = F(n-2) + F(n-3) + F(n-3) + F(n-4) = …  Lots of overlapping sub-problems! 11 Bottom-Up Approach

 Start from bottom-most unsolved sub-problem  Solve its way up to the final solution max() returns max # bottom-up Fibonacci value in the iterable def fibonacci(n): if n not in fibonacci.table: # start from smallest unsolved sub-problem mx = max(fibonacci.table) + 1 for i in range(mx, n+1): # ends at i == n fibonacci.table[i] = fibonacci.table[i-1] + \ fibonacci.table[i-2] return fibonacci.table[n]

# static function variable! fibonacci.table = { 0 : 1, 1 : 1 } # f(0) = f(1) = 1 12 Memoization

 Same as dynamic programming, except top-down  Advantage (over dynamic programming)  less computation if not all sub-problem needs to be solved  Disadvantage  Recursion requires more memory than tabulation  Example: Prolog (logic programming language) ?- fibonacci(100, F). ERROR: Out of local stack :- table fibonacci/2 ?- fibonacci(100, F). F = 573147844013817084101. 13 Top-Down Approach

 If sub-problem already solved, use result directly  Else solve the sub-problem and add solution to table

# top-down Fibonacci def fibonacci(n): # assume n >= 0 if n in fibonacci.table: return fibonacci.table[n] else: v = fibonacci(n-1) + fibonacci(n-2) fibonacci.table[n] = v return v fibonacci.table = { 0 : 1, 1 : 1 }

14 Pure Function

 Output solely determined by input to function  Also cannot have side effects  i.e. changing states outside of local environment  e .g. modifying non-local variables, perform I/O, etc.  Important in functional programming  Referential transparency  Replacing expression by its corresponding value does not change program behaviour  Guaranteed from a pure function  Requirement for memoisation/dynamic programming 15 Files

 Use open built-in function >> f = open(“hello.txt”) # read-only mode, file must exist >> h = open(“io.h”, “w”) # write-only mode, file will be wiped  Reading text files

>> for line in f: # file objects are iterable .. print(line)  Writing text files

>> h.write(“hello world\n”) # add a new line to sentence  Close file (after finished) >> f.close()

16 Error Handling

 Deals with runtime error without crashing  Need to disrupt int i; Dir * d = malloc(sizeof(Dir)*NUM_DIRS); normal execution if (d == NULL) goto fail; flow for (i = 0; i < NUM_DIRS; i++) { if (!(d[i] = alloc_dir()))  Example: goto fail_d; goto /* do stuff with d[i] */ statement } in C return 0; fail_d: for (i-- ; i >= 0; i--) free_dir(d[i]); free(d); fail: return –ENOMEM; 17 Error Handling

 C++/Python: try statement  Jumps to exception handler on error  May need to unwind stack frames (function calls)  Can be expensive (C++ compile option --fno-exceptions) try: f = open(“hello.txt”) except OSError as err: print(err) else: # if no error occurs print(f.read()) # this reads everything f.close()

18 With Statement

 Some objects have pre-defined clean-up actions  Special __exit__ method  Makes code look much cleaner

# close called automatically when exiting block

with open(“hello.txt”) as f: print(f.read())

# Note: f still in scope here (but is closed)

19 User-Defined Exception

 Create a class derived from base Exception class  More on creating class in future lectures

class MyError(Exception): pass

>> raise MyError(“It’s bad”) # raise your own exception __main__.MyError: It's bad

pr = analyze_move(mv) if pr > 1.0: # use built-in exception raise ValueError(“probability can’t be > 1!”)

20 Multiple Exceptions def baz(): try: foo() # exceptions can be raised from inside bar() # a function call for caller to handle … except (KeyError, ValueError): # deal with these two the same way return 0 except OSError as err: print(err) return -1 except: print(“unexpected exception!”) raise # re-raise the exception to … # caller of this function

21 ECE326 PROGRAMMING LANGUAGES

Lecture 6 : Review and Python Tidbits Kuei (Jack) Sun ECE University of Toronto Fall 2019 Course Prerequisite

 ECE244: Programming Fundamentals  ECE297: Design and Communication  Not a prerequisite  ECE302: Probability and Applications  ECE345: Algorithms and Data Structures

2 Assignment 2

 Released next week (postponed by 1 week)  Will use basic concepts from high school  Probability  Expected Value  Dynamic Programming  Intent of the course  Practical, programmer perspective  i.e. save result of function and reuse on same argument  Not part of the course  Theory: e.g. asymptotic runtime complexity analysis, amortized … 💤💤 3 Result of Class Poll

4 Updates to Assignments

Before After

Assignment Start End Marks Start End Marks 1 Sept 9 Sept 29 5% Sept 9 Oct 6 7% 2 Sept 16 Oct 9 6% Sept 23 Oct 20 8% 3 Oct 7 Nov 10 7% Oct 14 Nov 17 8% 4 (50% easier) Nov 4 Dec 8 8% Nov 11 Dec 8 7% Quiz 4% Bonus 5%

5 Code Organization

 C/C++  Files (usually) separated into source and header  Header: contains class definition and function prototypes  Meant to be exported, i.e. used by others  Source: contains function definition  Sometimes static functions and opaque user-defined types  Information Hiding  Reduces external complexity  Prevents user from making access outside of provided interface  Results in better abstraction and a stable interface

6 Static Function

 Function only available in the file it is defined in  Similar to private member functions  But not even specified in the header file  Completely hidden from external user foo.c static int foo_helper(int a) { /* complex calculation */ } int foo(int x, int y) { return foo_helper(x) + foo_helper(y); } foo.h int foo(int x, int y); /* no one knows about foo_helper */

7 Opaque Data Type

 Data type only declared, not defined  Concrete representation hidden from its users list.c struct Node { int value; struct Node * next; }; struct List { struct Node * head; }; struct List * create_list(void) { struct List * list = malloc(sizeof(struct List)); list->head = NULL; return list; } list.h Returns an opaque pointer struct List; struct List * create_list(void); struct List * add_to_list(struct List * list, int value); 8 Code Organization

 Module  A Python source file or directory  Contains a collection of definitions and statements  Prevents name conflict  E.g. math.abs vs. bodybuilder.abs  Similar to C++ namespace  Import  To gain access to definitions and functionalities of a module  No information hiding, everything is accessible  Use name of file (minus the .py)  E,g, to import foo.py, use import foo  File executed when importing

9 Python Idiom

 Avoid execution if imported as module foo.py print(“hello world”) def main(): pass # will not run if imported as module if __name__ == “__main__”: main()

bar.py (we run this file) import foo # prints “hello world” print(type(foo)) # prints print(foo.__name__) # prints “foo” print(__name__) # prints “__main__” 10 Import

 A Python statement  Can be called anywhere in code  Convention: prefer at top of source file  Optimization: avoid import until just before use

def unlikely_called_function(): import huge_module huge_module.do_something()  Python tracks which module already imported  Same module will not be re-imported

11 Import

 Import functions and types into local namespace  Don’t have to prefix with module name

from collections import namedtuple, OrderedDict, deque ordered = OrderedDict(zip(“abcde”, range(5)))

# OrderedDict([('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4)]) print(ordered)

# print(OrderedDict)

12 Import

 A directory can also be imported (a.k.a package)  Must have a special file named __init__.py  Good way to organize very large code base mydir/__init__.py from mydir.foo import bar print(“hello import”) mydir/foo.py def bar(): print(“hello bar”) >> import mydir hello import >> mydir.bar() # imported definition is exported hello bar 13 Default Argument

 Default value assigned to missing arguments  In C++, default arguments always recreated

struct A { int main() { int x; foo(); A() : x(0) {} foo(); ~A() { return 0; cout << "destroyed\n"; } } }; $ ./foo 0 void foo(A a=A()) { destroyed cout << a.x << endl; 0 a.x = 5; destroyed } 14 Default Argument

 In Python, only evaluated once, when defined  Beware of mutable default arguments!

def add_topping(budget, toppings=list()): if budget > 4.99: budget -= 4.99 toppings.append(“chipotle steak”) … return toppings

>> pizza1 = add_topping(5) >> pizza2 = add_topping(4) >> add_topping(3) [‘chipotle steak’, ‘grilled chicken’, ‘broccoli’]

15 Default Argument

 Workaround  Convention: use None  Similar to NULL in C/C++

def add_topping(budget, toppings=None): if toppings is None: toppings = list() … return toppings

>> pizza1 = add_topping(5) >> pizza2 = add_topping(4) >> add_topping(3) [‘broccoli’]

16 Function Scope

 Local variable valid until end of function  Can be defined within nested block  Usable outside of block where definition occurred  Global variable  Reassignment requires use of global statement MUSIC = [ “Pop”, “EDM” ] def retro(): global MUSIC MUSIC = [ “Classic”, “Jazz” ] >> retro() >> print(MUSIC) [‘Classic’, ‘Jazz’]

17 Function Scope

 Global variable  Read and update are permitted without global

MUSIC = [ “Pop”, “EDM” ] def retro(): # empties the list and re-populate it MUSIC.clear() MUSIC.extend([ “Classic”, “Jazz” ])

>> retro() >> print(MUSIC) [‘Classic’, ‘Jazz’]

18 Scope and Except

 An “exception” to function scope  Exception variable is deleted at end of block

def try_fail(): e = "hello" try: a = range(2) print(a[3]) except IndexError as e: print(e)

# NameError: name 'e' is not defined print(e + " world")

19 Scope and Except

 Why the “exception”?  Exception variable interferes with garbage collection  Potentially large amount of memory cannot be reclaimed until exception variable is deleted  Technical detail in future lecture

 Workaround keep_me = None  If you want to keep it, try: … reassign it to another except IndexError as e: variable keep_me = e # range object index out of range print(keep_me)

20 List Comprehension

 Creates sequence from an iterable  Form 1:  P(x) for x in iterable  map function:  Applies function, e.g. P(x), to all elements  R eturns a list of results in the same order # function: P(x) def map(function, iterable): output = [] for element in iterable: output.append(function(element)) return output 21 List Comprehension

 Form 2:  x for x in iterable if F(x)  filter function:  From input iterable, returns only elements that satisfies F(x)  i.e. when F(x) returns true  Original ordering is preserved

def filter(function, iterable): output = [] for element in iterable: if function(element): # if F(x) is true output.append(element) return output 22 List Comprehension

>> [ x*x for x in range(1, 6) ] Imperative [1, 4, 9, 16, 25] Programming >> [ x for x in range(10) if x%2 ] Style [1, 3, 5, 7, 9]

def square(x): Functional return x * x Programming >> map(square, range(1, 6)) [1, 4, 9, 16, 25] Style

def odd(x): return x%2 != 0 >> filter(odd, range(10)) [1, 3, 5, 7, 9] 23 ECE326 PROGRAMMING LANGUAGES

Lecture 7 : Object-Oriented Programming Kuei (Jack) Sun ECE University of Toronto Fall 2019 Object-Oriented Programming

 Object  Contains both state (data) and behaviour (code)

Data Code C++ Member variable Member function Java Field Method Python Data attribute Method  Models real world things and concepts  Provides encapsulation  E.g. restricts direct access to some parts of an object

2 Object-Oriented Programming

 Sending messages  object maps messages to values.  Responds to a message by looking up the corresponding value  Class  A “blueprint” to create objects of the same behaviour  Can create instances of a class, which determines their type  Instance  Manifestation of an object  Emphasizes the distinct identity of the object

3 Object-Oriented Programming

 Class-based programming  Inheritance occurs by deriving classes from existing ones  i.e. occurs through subtyping  E.g. Student is a subclass of Person  Prototype-based programming  Inheritance occurs by copying existing object and adding to it  Individual objects would be cloned from the prototype  E.g. Student object is Person object with an extra ID field  jack instance is created by copying student object and setting the names and ID fields to real values (instead of the defaults)

4 Object-Oriented Programming

 Class-based programming  Usually used by statically-type languages: e.g. C++, Java  Prototype-based programming  Usually only possible for dynamically-typed languages  Only one stands out: JavaScript  Python  Class-based programming  BUT  Can support prototype-based programming

5 Python Class

 Declared via class keyword  Can be completely empty  Same as C++, but MUCH MORE POWERFUL

class Foo: pass >> a.baz AttributeError: Foo instance # create an instance of Foo has no attribute 'baz' >> a = Foo() >> type(a) # unlike C++, you can ADD # new fields >> print a >> a.bar = 3 <__main__.Foo instance at …> >> a.bar >> isinstance(a, Foo) 3 True 6 Constructor

 Special __init__ method  Self variable  Variable that references the current instance (this in C++)  Must be first parameter of all instance methods

class Point: def __init__(self, x, y): self.x = x self.y = y

# create an instance of Point (calls Point.__init__) >> a = Point(3, 4) >> a.x 3 7 Instance Method

 Always takes class instance variable as first argument

 self: named by convention, denotes ‘this’ instance

class Point: … def distance(self, other): dx = self.x – other.x dy = self.y – other.y return math.sqrt(dx*dx + dy*dy)

>> a = Point(4, 5) >> b = Point(1, 1) >> a.distance(b) # a is the first argument to distance() 5.0 8 Attribute

 In Python, means either data members or methods  Can add new ones after __init__  Although it may confuse other programmers

class Point: def move(self, dx, dy): self.x, self.y = self.x + dx, self.y + dy self.moved = True

>> a = Point(0, 0) >> a.moved AttributeError: ‘Point’ object has no attribute ‘moved’ >> a.move(5, 5) >> a.moved True 9 Attribute

 Can also remove attributes

class Point: def move(self, dx, dy): self.x, self.y = self.x + dx, self.y + dy self.moved = True if self.x == 0 and self.y == 0: del self.moved

>> a = Point(3, 2) >> a.move(-3, -2) >> a.moved AttributeError: ‘Point’ object has no attribute ‘moved’

10 dir

 Convenience function to learn about an object  Returns list of attributes  Use this as a way to debug your program

class A: def __init__(self): pass

>> dir() [‘A’, ‘__builtins__’, ‘__name__’, …]

>> dir(A) [‘__init__’, ‘__class__’, '__delattr__', '__dict__', …]

11 __dict__

 Let’s dig a little deeper into how an instance works

>> a = [1, 2, 3] >> a.x = 5 AttributeError: 'list' object has no attribute 'x'

>> b = Foo() >> b.x = 5 >> b.__dict__ {‘x’: 5} >> a.__dict__ AttributeError: 'list' object has no attribute '__dict__'

>> b.__dict__[‘y’] = “hello” >> b.y “hello” 12 __slots__

 Denies creation of __dict__  P revents instance from accepting new attributes  Evil: can put __dict__ into __slots__  Also reduce memory consumption

class Point: __slots__ = (‘x’, ‘y’) def __init__(self, x=0, y=0): # uses default arguments self.x, self.y = x, y

>> a = Point() >> a.z = 5 AttributeError: ‘Point’ object has no attribute ‘z’

13 Encapsulation

 There is no private keyword in Python  Cannot (easily) protect attributes from direct access  Convention  Start name of attribute with underscore _  Tells other programmers: “it is private (pretty please)” class Submarine: def _launch_missile(self): … def try_launch_missile(self, password): if self.password == password: self._launch_missile() else: raise PermissionError(“access denied”) 14 Class Attribute

 In Python, class is also an object  An instance can access its class’s attributes  That’s how it calls methods defined in the class  It can also access class member variables

class Point: origin = (0, 0) def __init__(self, x=0, y=0): self.x, self.y = x, y

>> a = Point() >> a.origin (0, 0)

15 Class Attribute

 Class attributes are read only by their instances  Reassignment bounds new attribute to the instance  Typically not what you want to do

class Point: origin = (0, 0) # static class variable in C++

>> a, b = Point(), Point() >> a.origin = (1, 1) # now local to a >> a.origin (1, 1) >> b.origin # still refers to Point.origin (0, 0) >> Point.origin # not changed by a (0, 0) 16 Instance Method

 When instance calls functions defined by its class, it passes itself in as the first argument  Alternatively, you can do it manually

class Point: def move(self, dx, dy): …

>> a = Point() # (0, 0) >> Point.move(a, 3, 4) # (3, 4) >> func = Point.move # similar to member function pointer >> func(a, 2, 2) >> a.x, a.y (5, 6) 17 Class Method

 Takes class as first argument

 Use @classmethod decorator (more on this later)

class Point: origin = (0, 0) @classmethod def debase(cls, dx, dy): cls.origin = (dx, dy)

>> a = Point() >> a.origin (0, 0) >> Point.debase(3, 7) >> a.origin (3, 7) 18 Ad-Hoc Polymorphism

 Ability for an entity to behave differently based on input or contained types  Function Overloading  Functions of same name with different implementations  Not supported by Python (why?)  Operator Overloading  Operator has different implementation based on operand(s)  Allows use of notation/syntax similar to basic types  E.g. use a + b to add two complex numbers instead of a.add(b)

19 Operator Overloading

 In Python, almost every operator has a corresponding special method that can be invoked if defined class Complex: def __init__(self, r=0, i=0): self.r, self.i = r, i def __add__(self, other): if isinstance(other, (int, float)): self.r += other elif isinstance(other, Complex): self.r += other.r self.i += other.i return self

>> Complex(3, 2) + 1.5 (4.5, 2) # I cheated 20 __str__

 Converts objects to string  Automatically done when passed to print class Complex: # continue from previous example def __str__(self): if self.i == 0: return str(self.r) elif self.i > 0: return "{} + {}i".format(self.r, self.i) elif self.i < 0: return "{} - {}i".format(self.r, -self.i) else: return "{}i".format(self.i)

>> print(Complex(3, -2)) 3 – 2i 21 Index Operator

class Bitmap: def __init__(self, data=0): self.data = data def __getitem__(self, idx): return self.data & (1 << idx) def __setitem__(self, idx, val): if val: self.data |= (1 << idx) else: self.data &= ~(1 << idx)

>> bm = Bitmap(0xE3) >> print(bm[4]) >> bm[0] = 0 0 >> bm[4] = 1 >> print(bm[1]) >> print("0x%x"%bm.data) 2 0xf2 22 Operator Overloading

 Relational Operators  __eq__, __ne__, …etc  Reverse Arithmetic Operators  Used when instance at right side of operator  E.g. 3 + Complex(-1, 2)  __radd__, __rsub__, __rmul__, …etc  Assignment Operator  Performs name binding in Python  Cannot be overloaded

23 ECE326 PROGRAMMING LANGUAGES

Lecture 8 : Inheritance and Runtime Polymorphism Kuei (Jack) Sun ECE University of Toronto Fall 2019 Subtyping

 In some languages, means same thing as inheritance  However, subtyping inherits only the interface  Forms a strict “is-a” relationship  Allows for substitution where parent type is expected  Has semantic relationship with parent type  E.g. Dog being a subtype of Animal has semantic meaning  Inheritance  Parent class may have no semantic relationship  E.g. Chat class inherits from Network class to gain its implementation for making connections to other participants over the Internet

2 Inheritance

 Creates new class (subclass) based on existing one(s)  Acquires all attributes and behaviours of parent (base class)  Retains all existing implementation  Enables code reuse  Can replace (override) existing implementation  Can extend to support new behaviours  Add more functionality  Used interchangeably  Subclass – child class – derived class  Super class – parent class – base class

3 Inheritance in Python

class Animal: def __init__(self, age, weight): self.age, self.height = age, height def move(self, location): print(“It moved to %s”%location)

class Dog(Animal): def __init__(self, age, weight, name=“Marley”): self.age, self.height = age, height self.name = name def move(self, location): print(“%s moved to %s”%(self.name, location))

>> dog = Dog(5, 35.2) >> dog.move(“the park”) Marley moved to the park 4 Runtime Polymorphism

 Choosing behaviour through single interface at runtime  E.g. C++ virtual function  Decides which implementation of a virtual function to call  There are no non-virtual functions in Python  Also no pure virtual functions (why?)  Everything can be overridden  By child class or by manual manipulation  Sometimes by accident  If a child class has an attribute of the same name, it will take precedence over the parent’s

5 super

 Allows accessing attribute of the super class without having to specify “which” super class  Very important for multiple inheritance

class Dog(Animal): def __init__(self, age, weight, name=“Marley”): Animal.__init__(self, age, weight) self.name = name

class Dog(Animal): def __init__(self, age, weight, name=“Marley”): super().__init__(age, weight) self.name = name

6 Dynamic Dispatch

 A polymorphic operation has different implementations  Dynamic dispatch determines which to call at runtime based on context  Context can include caller’s type and input types  Static Dispatch  Ad-hoc polymorphism  Knows which function to call based on their signatures  Can be done at compile-time  E.g. function overloading, operator overloading, …etc

7 Single Dispatch

 C++ virtual functions  Context is based solely on type of instance  Not the reference type of the variable struct A { B b = B(); virtual void foo() { cout << “A::foo\n”; // reference type is A } // instance type is B }; A * ap = &b;

struct B : public A { // prints B::foo virtual void foo() override { ap->foo(); cout << “B::foo\n”; } }; 8 Override Keyword

 Available since C++11  Compile-time error if virtual function does not override  Helps detect unexpected bugs on signature change struct A { virtual void foo(long a) { cout << “A::foo ” << a << endl; } Error: B:foo marked }; ‘override’ but does struct B : public A { not override virtual void foo(int a) override { cout << “B::foo” << a << endl; } }; 9 Final Keyword

 Error when attempting to override a final function  Helps prevent accidental overriding

struct B : public A { virtual void foo(long a) final { cout << “B::foo ” << a << endl; } Error: overriding }; final function struct C : public B { B::foo() virtual void foo(long a) { cout << “C::foo” << a << endl; } };

10 Final Keyword

 Error when attempting to inherit from a final class

struct B final : public A { virtual void foo(long a) { cout << “B::foo ” << a << endl; } }; Error: cannot derive from final struct C : public B { base ‘A’ … };

11 Virtual Table

 Implements dynamic dispatch in C++  A lookup table to resolve virtual function calls  Implemented as an array of function pointers

struct A { virtual void foo() {} virtual void bar() {} }; struct B : public A { virtual void foo() override {} }; struct C : public A { virtual void bar() override {} }; 12 Virtual Table

 For each class in the hierarchy, a VTable is created A::vtable struct A { virtual void foo() {} A::foo virtual void bar() {} A::bar }; B::vtable struct B : public A { virtual void foo() override {} B::foo A::bar }; struct C : public A { C::vtable virtual void bar() override {} A::foo }; C::bar

13 Virtual Table Pointer

 In the base class, a hidden pointer is added  Both base and derived class will have this pointer (why?)

struct A { /* points to array of member * function pointers */ void * __vptr; virtual void foo() {} virtual void bar() {} };

14 Virtual Table Pointer

 During instantiation, it will be set based on its type  __vptr will point to the corresponding A::vtable virtual table for its instance type A::foo A::bar struct A { void * __vptr; }; B::vtable B::foo struct B : public A { A::bar void * __vptr; // inherited }; C::vtable struct C : public A { A::foo void * __vptr; // inherited C::bar }; 15 Virtual Function Call

 Instead of calling function directly, goes through VTable  Each virtual function has a fixed index in Vtable  Index based on order of appearance in class definition  Use __vptr and index to call the actual function

B b = B(); A * ap = &b;

// goes through virtual table, calls B::foo ap->foo();

// the actual implementation // uses syntax for member function pointer call (ap->*__vptr[0])(); 16 Multiple Dispatch

 Context also includes input parameter types  Julia – dynamically-typed just-in-time compiled language  Used by scientific communites for its high performance  Example  Suppose we have a polymorphic function, eat, where an instance of type Animal eats some an instance of type Food

Animal end abstract type Food end

eat(eater:Animal, meal:Food) = println(“yum!”)

17 Multiple Dispatch

 Now let’s add some real animals and food…

struct Dog <: Animal end struct Carrot <: Food end struct Lion <: Animal end struct Beef <: Food end struct Sheep <: Animal end  We want to make sure some animals reject some food

eat(eater:Lion, meal:Carrot) = println(“yuk!”) eat(eater:Sheep, meal:Beef) = println(“puke!”)

>> kimba = Lion() >> fido = Dog() >> eat(kimba, Carrot()) >> eat(fido, Beef()) yuk! yum!

18 Multiple Dispatch

 How to do the same thing in C++? struct Animal { virtual void eat(Carrot * meal) { cout << “yum!\n”; } virtual void eat(Beef * meal) { cout << “yum!\n”; } /* Question: why won’t ‘eat(Food * meal)’ work? }; struct Lion : public Animal { virtual void eat(Carrot * meal) { cout << “yuk!\n”; } }; …  What if there were more animals and food?  What if we add other parameters? (e.g. time of day) 19 Multiple Dispatch

 Emulating multiple dispatch in C++  Use N-dimensional array of function pointers enum Animal_ID { enum Food_ID { DOG = 0, CARROT = 0, LION = 1, BEEF = 1, SHEEP = 2, }; }; void (* matrix[3][2])(Animal *, Food *) = { { animal_eat_food, animal_eat_food, }, { lion_eat_carrot, animal_eat_food, }, { animal_eat_food, sheep_eat_beef, }, }; // both Animal and Food class have associated ID field void eat(Animal * a, Food * f) { matrix[a->animal_id][f->food_id](a, f); } 20 Late Binding

 Associates name with an operation at runtime  Type unknown until use (e.g., evaluation)  Early Binding  Type is known at time of instantiation  Usage of term sometimes conflated  Can mean dynamic dispatch or “” (defer to later lecture)  Quote from father of OOP  “OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.” – Alan Kay

21 Late vs. Early

class Point: def __str__(self): return str((self.x, self.y)) def move(self, dx, dy): self.x, self.y = self.x + dx, self.y + dy  Late Binding  Early Binding >> a = Point(2, 3) >> a = Point(2, 3) >> def move2(self, x, y): >> def move2(self, x, y): .. self.x = x … .. self.y = y # assume you can do this >> Point.move = move2 >> Point.move = move2 >> a.move(5, 5) >> a.move(5, 5) >> print(a) >> print(a) (5, 5) (7, 8) 22 Binding vs. Dispatch

 Both are examples of runtime polymorphism  Late binding is concerned with the object  Calling method by name  Name resolved to method by object type  Object behaviour can change after instantiation  Dynamic dispatch is concerned with the operation  Calling method by context  Context determines which implementation to call  Object behaviour remains the same after instantiation

23 ECE326 PROGRAMMING LANGUAGES

Lecture 9 : Inheritance in C++ Kuei (Jack) Sun ECE University of Toronto Fall 2019 Single Inheritance

 All object-oriented languages supports it  Derived class can only inherit from one base class  Java only supports single inheritance  Simplifies compiler implementation

2 Object Layout

 Used by all compiled languages that support inheritance struct A { higher memory address On 64-bit architecture, int x; structures are 8-bytes aligned int y; C }; struct z: 8 bytes sizeof(C) -> 32 struct B : public A { padding: 4 bytes char m[12]; m: 12 bytes sizeof(B) -> 20 }; y: 4 bytes sizeof(A) -> 8 struct C : public B { x: 4 bytes long z; };

3 Alignment

 Some architectures have data alignment requirements  E.g. A 64-bit integer must be 8 bytes aligned  0xFFFFCC00 is 8-byte aligned but 0xFFFFCC02 is not  Unaligned data requires extra instructions to re-align  Padding  An unnamed structure member to align subsequent fields  Note: C/C++ does not allow reordering  Fields must be placed in order of appearance in structure definition  Disable padding  Add __attribute__((packed)) to structure definition

4 Packed

struct Loose { struct Tight { int x; int x; /* 4-bytes padding */ long y; long y; char z[10]; char z[10]; long w; /* 6 bytes padding */ } __attribute__((packed)); long w; }; sizeof(Tight) -> 30 sizeof(Loose) -> 40

5 Abstract Base Class

 Contains one or more pure virtual function(s)  Pure virtual function  Declared, but not defined (implemented)  Cannot be instantiated

struct Shape { /* pure virtual function */ virtual double area() const=0; /* normal virtual function */ virtual const char * get_name() const { return “Shape”; } }; 6 Virtual Table

Shape::vtable Cannot have nullptr in vtable, cannot instantiate these classes nullptr ∴ Shape::get_name struct Shape { virtual double area() const=0; virtual const char * get_name() const; Polygon::vtable ; nullptr Shape::get_name struct Polygon : public Shape { nullptr virtual int sides() const=0; }; Rect::vtable struct Rect : public Polygon { Rect::area double w, h; Shape::get_name virtual double area() const { return w*h }; Rect::sides virtual int sides() const { return 4; } }; 7 Multiple Inheritance

 Derived class has two or more base classes  Use Cases:  Support for multiple interfaces  E.g. Amphibian class is both a Terrestrial and a Swimmer  Implementation inheritance  Base class inherited for its implementation (code reuse)  E.g. Actor class is a Person, and borrows implementation from Singer  Introduces possibility of ambiguity  E.g. both Cowboy and Painter have a draw function  Special NPC character Joe is both a Cowboy and a Painter

8 Object Layout

 Base classes are stacked by order of appearance struct A { higher memory address class B is placed in int p; the middle! int q; }; struct C struct B { t: 8 bytes char s[16]; s: 16 bytes }; q: 4 bytes struct C : public A, public B { p: 4 bytes long t; };

9 Resolving Ambiguity

 When accessing members of same name from different base classes, must specify which base class  tDoes no check function signature struct Cowboy { Joe joe = Joe(); void draw(Target *); }; // error: request for member // ‘draw’ is ambiguous struct Painter { joe.draw(canvas); void draw(Canvas *); }; // ok – base class specified joe.Painter::draw(canvas); struct Joe : public Cowboy, public Painter { // ok – base class specified … joe.Cowboy::draw(victim); }; 10 Upcasting

 Casting a more specific type to a more generic type  i.e. from a subclass to a super class struct A { /* single inheritance */ int x; /* &c == 0xffffcbf0 */ int y; C c = C(); }; /* ap == 0xffffcbf0 */ A * ap = &c; struct B : public A { char m[16]; /* bp == 0xffffcbf0 */ }; B * bp = &c; struct C : public B { Upcasting results in the long z; same pointer location }; 11 Upcasting

 Casting a more specific type to a more generic type  i.e. from a subclass to a super class Upcasting results in the struct A { higher memory address int x; same pointer location int y; }; struct C struct B : public A { z: 8 bytes char m[16]; m: 12 bytes }; &c, ap, bp y: 4 bytes struct C : public B { x: 4 bytes long z; }; 12 Upcasting

 For multiple inheritance, upcasting may require shifting of memory address /* multiple inheritance */ struct A { C c = C(); int p; Upcasting results in the A * ap = &c; int q; different memory address B * bp = &c; }; struct B { struct C char s[16]; t: 8 bytes }; s: 12 bytes bp struct C : public A, public B { q: 4 bytes &c, ap long t; p: 4 bytes }; 13 Downcasting

 Casting a generic type to a specific type  i.e. from a super class to a subclass  Can be potentially dangerous (type unsafe) if coerced  Type coercion: forcefully cast one type to another  Requires special cast operator: e.g. reinterpret_cast  Single inheritance  Safe as long as type is correct  Multiple inheritance  Requires pointer offset

14 Runtime Type Information

 Exposes information about type of object at runtime  Adds runtime overhead, can be turned off  Allows for type-safe downcasting  dynamic_cast  Attempts to cast, return nullptr if not type safe  Offsets pointer correctly for multiple inheritance Penguin p = Penguin(); Animal * ap = &p; // success, pp is a valid pointer Penguin * pp = dynamic_cast(ap); // fail, tp is nullptr Turkey * tp = dynamic_cast(ap); 15 Repeated Base Class

 Appears more than once during inheritances struct Person { const char * name; }; struct Student : struct Teacher : public Person { public Person { int student_id; int class_id; }; }; struct TA : public Student, public Teacher { int hours; };

16 Repeated Base Class

 By default, multiples copies of base class is made struct Person { const char * name; TA::hours }; Teacher::room struct Student : public Person { Person::name int id; }; Student::id Person::name struct Teacher : public Person { int room; }; struct TA : public Student, public Teacher { int hours; };

17 Ambiguous Base

 Occurs when trying to access members of repeated base class – requires disambiguation TA::hours TA ta = TA(); Teacher::room // error: ‘Person’ is an ambiguous Person::name // base of ‘TA’ Student::id Person * pp = &ta; Person::name

// following two lines are fine Person * teacher = (Teacher *)&ta; Person * student = (Student *)&ta;

teacher.name = “Jack”; // Both teacher and student student.name = “Bob”; // have their own copy of name 18 Diamond Problem

 When repeated base classes are undesirable  Each parent class has its own copy of common base class  Causes ambiguity, even after disambiguation!  What we want is shared common base class

Person

Student Teacher

TA

19 Virtual Base Class

 Allows common base class to be shared struct Person { const char * name; }; Person struct Student : virtual public Person { TA int id; Teacher }; Student struct Teacher : virtual public Person { int room; }; struct TA : public Student, public Teacher { int hours; };

20 Object Layout

virtual base class now higher memory address placed at top of struct TA

struct TA TA::vtable Person::name virtual base offset = 32 TA::hours offset to top = 0 Teacher::room typeinfo = typeinfo(TA) Teacher::__vptr ------virtual base offset = 16 padding: 4 bytes offset to top = 16 Student::id typeinfo = typeinfo(TA) Student::__vptr

higher memory address 21 Upcasting

 To access virtual base class from one of the parent classes, consult virtual base offset in table

1. TA ta = TA(); 2. Student * student = &ta; 3. student->name = “Jack”; struct TA Person::name +32 // locate student object (offset = 0) TA::hours 2. student = &ta + vtable.top_offset; +28 Teacher::room // locate shared person object (offset = 32) Teacher::__vptr 3. _person = student + vtable.vbase_offset; // locate name field in person object padding: 4 bytes +16 // (offset 0, first field) Student::id __name = _person + 0; Student::__vptr // set name field 0 *__name = “Jack”; 22 Upcasting

1. TA ta = TA(); 2. Teacher * teacher = &ta; 3. teacher->name = “Jack”; struct TA Person::name +32 // locate teacher object (offset = 16) TA::hours 2. teacher = &ta + vtable.top_offset; +28 Teacher::room // locate shared person object (offset = 16) Teacher::__vptr 3. _person = teacher + vtable.vbase_offset; // locate name field in person object padding: 4 bytes +16 // (offset 0, first field) Student::id __name = _person + 0; Student::__vptr // set name field 0 *__name = “Jack”;

23 Downcasting

 Downcasting in multiple inheritance requires vtable  If base class is not virtual, cannot downcast

Person * person = new TA(); // error: source type ‘person’ is not polymorphic Student * student = dynamic_cast(person);  Force vtable by adding a virtual destructor

struct Person { const char * name; virtual ~Person() {} };

24 Object Layout higher memory address TA::vtable struct TA virtual base offset = 32 Person::name offset to top = 0 Person::__vptr typeinfo = typeinfo(TA) TA::hours ------virtual base offset = 16 Teacher::room offset to top = 16 Teacher::__vptr typeinfo = typeinfo(TA) padding: 4 bytes ------virtual base offset = 0 Student::id offset to top = 32 Student::__vptr typeinfo = typeinfo(TA)

higher memory address 25 ECE326 PROGRAMMING LANGUAGES

Lecture 10 : Composition and Mixins Kuei (Jack) Sun ECE University of Toronto Fall 2019 Fragile Base Class

 Changing base class can break derived classes  Root cause  Implementation inheritance breaks encapsulation  Subclass depends on implementation of base class  Solution?  Never change the base class  Author of base class must control all its subclasses  E.g. all subclasses are contained within same module  Don’t use implementation inheritance

2 New Interface Problem

/* Dictionary with key-value pair type (cstring, int) */ class Dict { /* some sort of hash table implementation */ public: /* both functions return true on success, false on fail */ virtual bool set(const char * key, int value); bool get(const char * key, int & value); }; /* Dictionary that only stores prime numbers */ struct PrimeDict : public Dict { virtual bool set(const char * key, int value) override { if (!is_prime_number(value)) return false; return Dict::set(key, value); } }; 3 New Interface Problem

 What happens if we add a new interface?

class Dict { … public: virtual bool set(const char * key, int value); bool get(const char * key, int & value);

/* new interface */ int & operator[](const char * key); };  We just opened a backdoor to bypass the prime number check made by PrimeDict::set

4 Override Problem class List { // list of integers public: /* both functions return true on success, false on fail */ virtual bool add(int value); virtual bool extend(const List & other); }; /* Stores statistics on the numbers in the list */ struct StatsList : public List { int prime_counter = 0; // and other stats… virtual bool add(int value) override { if (is_prime_number(value)) prime_counter++; return List::add(value); } virtual bool extend(const List & other) override { for (iterator it = other.begin(); it != other.end(); it++) if (is_prime_number(*it)) prime_counter++; return List::extend(other); } 5 }; Override Problem

 What if List::extend uses List::add? bool List::extend(const List & other) { for (iterator it = other.begin(); it != other.end(); it++) if (!this->add(*it)) return false; return true; } bool StatsList::add(int value) { if (is_prime_number(value)) prime_counter++; return List::add(value); } bool StatsList::extend(const List & other) { for (iterator it = other.begin(); it != other.end(); it++) if (is_prime_number(*it)) prime_counter++; return List::extend(other); } 6 Override Problem

 What if List::extend uses List::add? bool List::extend(const List & other) { for (iterator it = other.begin(); it != other.end(); it++) if (!this->add(*it)) return false; return true; prime counter } is incremented twice! bool StatsList::add(int value) { if (is_prime_number(value)) prime_counter++; return List::add(value); } bool StatsList::extend(const List & other) { for (iterator it = other.begin(); it != other.end(); it++) if (is_prime_number(*it)) prime_counter++; return List::extend(other); } 7 New Implementation Problem class Album { std::vector current, archived; public: bool add(const Photo & photo); bool archive(const Photo & photo); };

/* allows for removal of photos from album */ struct SecureAlbum : public Album { void remove(const Photo & photo) { current.erase(std::remove(current.begin(), current.end(), photo), current.end()); archived.erase(std::remove(archived.begin(), archived.end(), photo), archived.end()); } }; 8 New Implementation Problem

 What if we made album track location and people?

using namespace std; class Album { vector current, archived; unordered_map> people; unordered_map> locations; public: /* now a variable argument function */ bool add(const Photo &, const Location * loc=nullptr, int num_people=0, ...); };  Now SecureAlbum will leave behind dangling references to deleted photos, eventually causing a crash!

9 Solution

 Disallow inheritance  final keyword  Prevents class from being inherited  Alternative: composition  Instead of subclassing, make it a field in your class  Instead of is-a, now a has-a  Forward methods to contained instance  Treats existing class as a blackbox  No more dependency of implementation  Enforces encapsulation

10 New Interface struct Dict { bool set(const char * key, int value); Adding new bool get(const char * key, int & value); interface to Dict int & operator[](const char * key); does not affect }; the interface of class PrimeDict { PrimeDict Dict dict; public: bool set(const char * key, int value) { if (!is_prime_number(value)) return false; return dict.set(key, value); } bool get(const char * key, int & value) { return dict.get(key, value); Forwarding } }; 11 No Override struct List { bool add(int value); bool extend(const List & other); Avoid changing behaviour of base }; class when overriding methods by using composition instead. class StatsList { List list; Question: Is there a trade-off here? int prime_counter = 0; public: bool add(int value) { if (is_prime_number(value)) prime_counter++; return list.add(value); } bool extend(const List & other) { for (iterator it = other.begin(); it != other.end(); it++) if (is_prime_number(*it)) prime_counter++; return list.extend(other); } }; 12 No Override struct List { bool add(int value); bool extend(const List & other); Answer: the extend function }; will no longer accept StatsList class StatsList { as an argument because it is List list; not a subclass of List. int prime_counter = 0; public: bool add(int value) { if (is_prime_number(value)) prime_counter++; return list.add(value); } bool extend(const List & other) { for (iterator it = other.begin(); it != other.end(); it++) if (is_prime_number(*it)) prime_counter++; return list.extend(other); } }; 13 Drawback

 Forwarding requires re-implementation  Can be tedious for large number of methods  In contrast, subclassing allows for immediate code reuse  Solutions  Automatic forwarding (delegation)  Type embedding  Mixin  Traits  Protocol (in Java, called Interface)

14 Delegation

 Kotlin  Google’s preferred language for Android app development  Statically-typed language that runs on JVM interface Base { fun print() Automatically forwards } this method call to class BaseImpl(val x: Int) : Base { b.print(), where b is an override fun print() { print(x) } instance of BaseImpl } class Derived(b: Base) : Base by b fun main() { val b = BaseImpl(10) Derived(b).print() } 15 Delegation

 Can also override delegation interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fun print() { print(x) } Prints “abc” instead } class Derived(b: Base) : Base by b { override fun print() { print(“abc”) } } fun main() { val b = BaseImpl(10) Derived(b).print() } 16 Delegation

 Java Delegate Annotation  Allows for even more fine-grained control over delegation public class DelegationExample {

// only allow add and remove to be forwarded private interface SimpleCollection { boolean add(String item); boolean remove(Object item); }

@Delegate(types=SimpleCollection.class) private final Collection collection = new ArrayList(); } 17 Delegation

 Java Delegate Annotation  Can exclude methods from being forwarded public class DelegationExample {

private interface Add { boolean add(String item); boolean addAll(Collection x); }

// do NOT forward add and addAll @Delegate(excludes=Add.class) private final Collection collection = new ArrayList(); } 18 Type Embedding

 Allows for composition with automatic forwarding  Introduced in Go  Does not have (implementation) inheritance  Uses composition for code reuse type Job struct { command string // go syntax is IDENT TYPE *log.Logger // embedded types have no identifier, } // just the type: * log.Logger job := &Job{“take out trash”, log.New(os.Stderr, “”, log.LUTC)}

// job has all methods of log.Logger, including Println job.Println(“working on it now...”) 19 Mixin

 Code reuse without becoming the parent class  Inclusion rather than inheritance  Provides functionality for another class  Can contain states (e.g. have member variables)  Frequently same as implementation inheritance  Convention: do not use mixin as an abstract base type  isinstance(obj, mixin) is semantically meaningless  Also implemented using metaprogramming techniques

20 Mixin

 Python Comparable

# Comparable Mixin, you must supply __lt__ to enable these class Comparable: def __eq__(self, other): return not (self < other) and not (other < self) def __ge__(self, other): return not (self < other) def __ne__(self, other): return self < other or other < self def __le__(self, other): return self < other or not (other < self) def __gt__(self, other): return not (self < other) and other < self 21 Mixin class Student(Person, Comparable): def __init__(self, name, score): Person.__init__(self, name) self.score = score Receives all the other comparison operators def __lt__(self, other): just by implementing return self.score < other.score the less than operator

>> a = Student(“Alice”, 50) >> b = Student(“Bob”, 60) >> a == b, a != b, a >= b, a > b, a <= b, a < b (False, True, False, False, True, True) >> c = Student(“Clive”, 50) >> a == c, a != c, a >= c, a > c, a <= c, a < c (True, False, True, False, True, False)

22 Mixin, Traits, and Protocol

 Provides functionality without subtyping

Mixin Trait Protocol Allows for states Yes No No (member variables) Interface only No No Yes (no method definition) Code Reuse Yes Yes No Composability Inheritance Commutative Commutative

 The Comparable example is also a trait  It contained no states (no member variables were used)

23 Conclusion

 Use inheritance only for interface (i.e. subtyping)  Use composition and other techniques for code reuse  Avoid fragile software, difficult to maintain and extend  Dependency Inversion Principle  High level modules should not depend on low level modules.  Both should depend on interfaces.  Interface should not depend on implementation.  Implementation should depend on interface.  Can swap out any component without behaviour change  Performance change is possible, and improvement is preferred

24 ECE326 PROGRAMMING LANGUAGES

Lecture 11 : Method Resolution Order Kuei (Jack) Sun ECE University of Toronto Fall 2019 Property

 Makes methods appear as data attributes

 Created using property built-in function class Person: def __init__(self, first, last): self.first, self.last = first, last def get_full(self): return self.first + “ ” + self.last def set_full(self, value): self.first, self.last = value.split(“ ”, maxsplit=1) def del_full(self): del self.first del self.last full_name = property(get_full, set_full, del_full) 2 Property class Person: … other definitions … full_name = property(get_full, set_full, del_full)

>> p = Person(“John”, “Doe”) >> p.full_name ‘John Doe’ >> del p.full_name >> p.first AttributeError: ‘Person' object has no attribute 'first' >> p.full_name = “Jane Smith” >> p.last ‘Smith’ >> p.first ‘Jane’

3 Property

 Advice  Don’t use if the function performs expensive computation  The function is called every time you access the field  Property “hides” the fact that it’s actually a function call  Use case  Interface change  E.g. the field was accessed directly. Now you want to change the way it is used or accessed  Make field read-only  Do not supply the setter and deleter

4 Property

 Example: throw an error if Celsius below absolute zero class Temperature: def __init__(self, value): self.celsius = value # this will call the property def get_celsius(self): return self._celsius def get_fahrenheit(self): return self._celsius * 9 / 5 + 32 def set_celsius(self, value): if value < -273.15: raise ValueError(“Cannot go below 273.15 degrees C”) self._celsius = value celsius = property(get_celsius, set_celsius)

5 Multiple Inheritance

 Python supports multiple inheritance  Used extensively for mixins  Also for naturally complex relationships  Review  Python has no pure virtual functions  All attributes in Python objects are virtual (can be overridden)  All base classes in Python are virtual  No repeated base class issue  A subset of the diamond problem still exists

6 Multiple Inheritance class Person: >> TA("jack", def __init__(self, name, age): .. 13, 48957257, self.name = name # print("In Person") .. "hello world") self.age = age class Student(Person): In Student def __init__(self, id, **kwargs): In Teacher self.id = id # print("In Student") In Person super().__init__(**kwargs) class Teacher(Person): def __init__(self, resume, **kwargs): self.resume = resume # print("In Teacher") pass everything as super().__init__(**kwargs) keyword argument class TA(Student, Teacher): def __init__(self, name, age, id, resume): super().__init__(name=name, age=age, id=id, resume=resume) 7 Cooperative Inheritance

 Take the arguments you need and pass the rest on  Power of Python’s variable keyword arguments  super() is used to chain the __init__ calls in different classes  Do not use positional arguments Automatically removed from kwargs class Teacher(Person): def __init__(self, resume, **kwargs): print("In Teacher") Keyword argument self.resume = resume unpacking. Passes super().__init__(**kwargs) unused arguments to the next class

8 Alternative (Bad) class Person: >> TA(“joey", def __init__(self, name, age): .. 21, 63490483, … set name and age … .. “good bye") class Student(Person): def __init__(self, name, age, id): In Student self.id = id # print("In Student") In Person Person.__init__(name, age) In Teacher In Person class Teacher(Person): def __init__(self, name, age, resume): self.resume = resume # print("In Teacher") Person.__init__(name, age) Person’s constructor class TA(Student, Teacher): got called twice… def __init__(self, name, age, id, resume): Student.__init__(name, age, id) Teacher.__init__(name, age, resume) 9 Method Resolution Order

 The order in which attributes are looked up  super() knows which __init__ to call next class Person: >> ta = TA("jack", 13, def vacation(self): 48957257, "hello world") return “Toronto Islands” >> ta.vacation() class Student(Person): Caribbean Islands pass >> TA.__mro__ (, class Teacher(Person): , def vacation(self): , return “Caribbean Islands” , class TA(Student, Teacher): ) pass

10 Depth First Search – Left to Right

 Naïve approach  Used by Python 2.1 and earlier

class TA(Student, Teacher): pass  MRO using DFS-LR: (TA, Student, Person, Teacher)  Not what we intuitively expect  Teacher is more specialized than Person  Huge problem for Python 2.2  object becomes the base class of all classes 11 Refinement

 Python 2.2  Still uses depth-first search, left to right  Delete duplicate if it shows up again later

# pseudo code for new method resolution order def mro_v2(cls): mro = [ cls ] for parent in cls.parents: mro = [ c for c in mro if c not in parent.__mro__ ] mro.extend(parent.__mro__) return mro  Now order is (TA, Student, Teacher, Person, object)  However… 12 Local Precedence Ordering

 Order in which parent classes are inherited  The new MRO does not honor this ordering class Person: Under new algorithm, the MRO is: def vacation(self): (PartTime, Student, Person) return “Toronto Islands” class Student(Person): >> PartTime(…).vacation() def vacation(self): Queen’s Park return “Queen’s Park” class PartTime(Person, Student): Q: But Student is more specialized pass than Person? A: Yes, as such, this inheritance hierarchy is ambiguous.

13 Monotonicity

 Given that C1 and C2 are part of the inheritance hierarchy of C, then if C1 precedes C2 in the linearization of C, then C1 must precedes C2 in the linearization of any subclass of C.  Method Resolution Order (MRO) is the set of rules that constructs the linearization of class.  Hierarchy that fails this criteria is ambiguous for C  Python 2.3 and later will raise TypeError if it detects ambiguous hierarchy

14 Ambiguous Hierarchy

 Using Python 2.2 MRO algorithm

class X: pass Python 2.2 used to accept ambiguous hierarchy, which class Y: pass can lead to subtle bugs because the resolution ordering class A(X, Y): pass changes depending on whether A is a subclass of C or not. class B(Y, X): pass class C(A, B): pass >> a = A() # resolution order changes >> c = C() # when A is a subclass of C L[A] = (A, X, Y, o) L[B] = (B, Y, X, o) L[C] = (C, merge(L[A] + L[B])) L[C] = (C, merge(A, X, Y, o, B, Y, X, o)) L[C] = (C, A, B, Y, X, o)

# this violates monotonicity because in L[C], Y comes before # X but in L[A], X comes before Y! 15 C3 Linearization

 An algorithm designed for Dylan programming language  Maintains local precedence ordering and monotoncity  Used by many languages  E.g., Python, Perl, …etc  The linearization of C is the sum of C plus the merge of the linearizations of the parents and the list of the parents.

 L[C(B1 ... BN)] = (C, merge(L[B1] ... L[BN], B1 ... BN))

16 Terminology

 Head  The first element of the list (i.e. linearization)  E.g. 5 is the head of the list [5, 2, 3, 7]  Tail  The remaining elements of the list (not head)  E.g. [2, 3, 7] is the tail of the list [5, 2, 3, 7]  L[C]  The linearization of the class C  Base case: L[object] = object

17 C3 Linearization

 Good head  A class that does not exist in the tail of any other lists  Algorithm  For each list in local precedence order, remove the head from the merge if it is a good head.  Otherwise try the next list  Repeat until all classes are removed or there is no good head  In latter case, merge is not possible  Error will be raised for ambiguous hierarchy

18 C3 Linearization class F: pass class E: pass E D F class D: pass class C(D,F): pass class B(D,E): pass B C class A(B,C): pass A L[F] = (F, o) L[E] = (E, o) L[D] = (D, o) L[C] = (C, merge((D, o), (F, o), (D, F)) # D is a good head, remove it from all lists and move it out L[C] = (C, D, merge((o), (F, o), (F)) # o is not a good head, but F is L[C] = (C, D, F, merge((o), (o)) # o is now a good head, and we’re done L[C] = (C, D, F, o) 19 C3 Linearization class F: pass class E: pass E D F class D: pass class C(D,F): pass class B(D,E): pass B C class A(B,C): pass L[F] = (F, o) A L[E] = (E, o) L[D] = (D, o) L[C] = (C, D, F, o) L[B] = (B, D, E, o) L[A] = (A, merge((B, D, E, o), (C, D, F, o), (B, C)) L[A] = (A, B, merge((D, E, o), (C, D, F, o), (C)) L[A] = (A, B, merge((D, E, o), (C, D, F, o), (C)) L[A] = (A, B, C, merge((D, E, o), (D, F, o)) L[A] = (A, B, C, D, merge((E, o), (F, o)) L[A] = (A, B, C, D, E, merge((o), (F, o)) L[A] = (A, B, C, D, E, F, merge((o), (o)) L[A] = (A, B, C, D, E, F, o) 20 Ambiguous Hierarchy class X: pass class Y: pass X Y class A(X, Y): pass class B(Y, X): pass class C(A, B): pass A B L[X] = (X, o) L[Y] = (Y, o) C L[A] = (A, merge((X, o), (Y, o))) L[A] = (A, X, Y, o) L[B] = (B, Y, X, o) L[C] = (C, merge((A, X, Y, o), (B, Y, X, o), (A, B)) L[C] = (C, A, merge((X, Y, o), (B, Y, X, o), (B)) L[C] = (C, A, B, merge((X, Y, o), (Y, X, o))

# Uh-oh, cannot continue. Neither X or Y are good heads 21 ECE326 PROGRAMMING LANGUAGES

Lecture 12 : Move Semantics Kuei (Jack) Sun ECE University of Toronto Fall 2019 Assignment 2

 Calculate the optimal strategy for Easy Blackjack  Approach: Divide and Conquer  For every encounter (a particular hand vs. the dealer’s)  Calculate the expected value (EV) of available actions  i.e. surrender, hit, stand, double, split  Select the action with the highest EV  Expected value  Probability weighted average of all possible value  Assume finitely many outcomes  Value: for this assignment, monetary value (not point value of hands)

2 Example

 Standing on hard 18 against dealer’s hard 16

Dealer Outcome Probability EV 17 +$1 1/13 1/13 18 0 1/13 0 19, 20, 21 -$1 3/13 -3/13 bust +1 8/13 8/13

 EV = P1V1 + P2V2 + P3V3 + P4V4 EV = (1/13)(1) + (1/13)(0) + (3/13)(-1) + (8/13)(1) EV = 6/13 = 0.462 3 Stand EV Table

 Once calculated, fill the corresponding entry in table  standing on hard 18 against dealer’s hard 16

4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 AA A2 A3 A4 A5 A6 4 ------5 ------6 ------7 ------dealer‘s------hand ------8 ------9 ------10 ------11 ------12 ------13 ------14 ------15 ------16 ------17 ------18 ------0.462 ------19 ------20 ------21 ------AA ------player‘s ------A2 ------A3 ------hand ------A4 ------A5 ------A6 ------A7 ------A8 ------A9 ------

4 Dynamic Programming

 Used extensively throughout the assignment  Many overlapping subproblems  Example  Reuse the dealer’s probability table for different starting hand  E.g. this time player has hard 19 against the dealer’s hard 16

Dealer Outcome Probability EV 17,18 +$1 2/13 2/13 19 0 1/13 0 20, 21 -$1 2/13 -2/13 bust +1 8/13 8/13

5 Hints and Advice

 Make sure you know the rules of Easy Blackjack well  Calculating expected value  Make sure probabilities of all outcomes add up to 1  Use assert(isclose(psum)) to crash immediately if check fails  Use Python’s debugger  python3 –m pdb main.py  Debug at point of failure  e.g. unhandled error, assertion, etc.  Partially done milestones are still worth marks!

6 Move Semantics and rvalue reference

7 Passing Arguments

 C++ has two ways of passing arguments to functions  Pass by value  The value of an argument is copied into the formal parameter  Example:

Complex add(Complex c, int a) { c.r += a; return c; } … Complex c(5, 2); Complex cp = add(c, 3);  Both c and a are copied before function is invoked

8 Pass by Value

 Arguments are copied to parameters  Arguments can be variable, literal, or expression  If variable is passed, it is guaranteed to be unaffected by the function call  If an object is passed, its copy constructor will be called  To make a copy of the object for the function call  This can be expensive

9 Pass by Reference

 Reference to variable is passed to parameter  Argument can only be a variable  Function can modify value of the argument  Tip: pass by const reference can guarantee to caller that function will not modify the argument

void swap(int & a, int & b) { int temp = a; a = b; b = temp; }  Passing a pointer by value is NOT pass by reference!

10 lvalue vs rvalue

 Notice pass by reference only accepts variable  Not literal or expression  Because it only accepts lvalue  lvalue (i.e. left value)  Value that is stored in memory – has an address  Appears on left side of an assignment (by value)  rvalue (i.e. right value)  Value that is temporary, not necessarily in memory  Appears on right side of an assignment (by value)

11 Example int i, j, *p; i = 6; // OK – i is lvalue, 6 is rvalue j = i; // OK – j is lvalue, i can be converted to rvalue

9 = j; // FAIL – left operand must be lvalue j*2 = 3; // FAIL – j*2 is an rvalue (temporarily calculated) p = &6; // FAIL – rvalue has no address p = &i; // OK – lvalue has an address *p = 5; // OK – dereferenced pointer is an lvalue *((int *)123) = 3; // OK – dereferencing static address

((i < 4) ? i : j) = 6; // OK – operator returns lvalue

12 Example int i, j; int & r = 5; // FAIL – cannot bind non-const lvalue // reference to rvalue int & r = j; // OK – lvalue reference binds to lvalue const int & r = 5; // OK – const lvalue reference to lvalue void foo(int & a) { … } foo(5); // FAIL – argument requires lvalue foo(j); // OK int foo() { return 5; } foo() = 3; // FAIL – foo() returns rvalue int & global() { return i; } global() = 4; // OK – global() returns lvalue

13 Rvalue Reference

 A reference to rvalue!  Extends the lifetime of rvalue until reference expires  E.g. goes out of scope  Allows reference to temporary objects and modify them  Helps reduce making redundant copies

string a = “hello ”; string b = “world”; string c = a + b; // may invoke copy constructor string && r = a + b; // rvalue reference to temporary object // no extra copy is made  Regular references are called lvalue references

14 Example struct Foo { int * p; Foo(int a) : p(new int(a)) {} Foo(const Foo & f) : p(new int (*f.p)) { cout << "copy " << *p << endl; } ~Foo() { delete p; } Foo operator+(const Foo & f) { return Foo(*p + *f.p); } }; >> ./foo Foo a(3); Foo b(5); >> # no output (copy constructor Foo c = a + b; # not called) Foo && r = a + a;

15 Copy Elision

 Optimization technique for avoiding copying of objects  Return value optimization  Instead of copying temporary object to final location, build object at final location directly  Must be object of exactly same type  Can change behaviour of program if copy constructor has side effects (e.g. increment a global variable)  Enabled by default  Can be turned off: -fno-elide-constructors

16 Pass by Rvalue Reference  Copy elision only applies to return values  When pass by value, copy still must be made  For large objects, you should usually pass by const reference  Unless you need to modify a local copy  S earch algorithm where you make a hypothetical move, go deeper in recursion, and undo the move (may be useful in assignment 2!)  Standard C++ library  Uses this to maintain genericity while improving performance  Forwarding  Allows arguments to be forwarded without additional copies

17 Move Constructor struct Foo { int * p; Foo(int a) : p(new int(a)) {} Foo(const Foo & f) : p(new int (*f.p)) { cout << "copy " << *p << endl; } ~Foo() { delete p; } Foo operator+(const Foo & f) { return Foo(*p + *f.p); }

/* move constructor */ Foo(Foo && f) : p(f.p) { /* we “moved” f.p to this->p, so must set f.p to * to nullptr otherwise ~Foo() will delete it! */ f.p = nullptr; } }; 18 Move Assignment struct Foo { int * p; Foo(int a) : p(new int(a)) {} ~Foo() { delete p; } Foo operator+(const Foo & f) { return Foo(*p + *f.p); } …

/* move assignment */ Foo & operator=(Foo && f) { if (this == &f) return *this; delete p; // delete current resource p = f.p; // move resource from other f.p = nullptr; // makes sure this->p is not deleted return *this; // when temporary object f dies } }; 19 std::move

 Forces move semantics on a variable  Convention: moved variable should become “empty”  Reality: some developers may leave moved object in unsafe state

#include

string a = “hello world”; string b = a; // copy constructor is called string c = std::move(a); // move constructor is called

// prints nothing (a is an empty string), could be worse… cout << a << endl;  Use to forward arguments or as part of move constructor

20 std::move bool Rule::can_surrender(Hand hand) { … } struct BJ { std::vector hands; Calls copy constructor to Rule * rule; make a copy of argument /* forwarding example (DO NOT USE) */ bool can_surrender(Hand hand) { return rule->can_surrender(std::move(hand)); } Calls move constructor to move /* move constructor */ argument for function call BJ(BJ && other) : hands(std::move(other.hands)), rule(other.rule) { other.rule = nullptr; } }; 21 std::move

 Use when variable will be “consumed”  i.e. will not be used afterward  Example  Set up an array of strings

std::vector words; while (true) { std::string line; getline(std::cin, line); if (line.length() == 0) break; /* move line to the new element, instead of copy */ words.push_back(std::move(line)); } 22 noexcept

 Move semantics should not acquire new resources  Just reassigning content to new location

 Performance improvement if noexcept specified

struct BJ { std::vector hands; Rule * rule; /* move constructor */ BJ(BJ && other) noexcept : hands(std::move(other.hands)), rule(other.rule) { other.rule = nullptr; } }; 23 Limitation

 POD types  Plain old data types – same as a C struct  Does not use object-oriented features  NO copy constructor, static member fields, virtual table, etc.  Does not benefit as much from move semantics  Unless it holds pointers to larger structures  Should always pass large PODs by lvalue-reference (const or non-const) unless local copy is needed  Primitive types  Always pass by value if read-only

24 ECE326 PROGRAMMING LANGUAGES

Lecture 13 : Generic Programming Kuei (Jack) Sun ECE University of Toronto Fall 2019 Container

 A collection of objects  Commonly used containers  Dynamic arrays  Constant time random access, best performance when used as stack  Doubly Linked List  C onstant time deletion, best performance when used as queue  Map  Stores key-value pairs  Priority Queue  Automatically sorts data during insertion

2 Generic Container

 In C, typically done using void * pointers struct vec1 { Sample usage: void ** array; struct vec1 * v = new_vector1(); int bytes; struct point * p = new_point(1, 2); int count; v1append(v, p); }; … p = (struct point *)v1get(v, 1);

int v1append(struct vec1 * v, void * element) { if (v->count >= v->bytes/sizeof(void *)) { if (!(v->array = realloc(v->array, v->bytes *= 2))) return –ENOMEM; } v->array[v->count++] = element; return 0; } 3 Generic Container

 Specially managed buffer (element embedded in buffer)

struct vec2 { char * buffer; void * v2get(struct vec2 * v, int i) { int bytes; if (i >= count) return NULL; int count; return (void *)(v->array + i*v->size); int size; } };

int v2append(struct vec2 * v, void * element) { if ((v->count+1)*v->size > v->bytes) { if (!(v->buffer = realloc(v->buffer, v->bytes *= 2))) return –ENOMEM; } memcpy(v->buffer+(v->count++)*v->size, element, v->size); return 0; } 4 Generic Container

element 1 element 2 element 3

vec1:

vec2: element 1 element 2 element 3

5 Linux Doubly Linked List

 Used extensively by Linux Kernel struct list_head { struct list_head *next, *prev; }; struct bar b = { 0 }; struct foo { INIT_LIST_HEAD(&b.head); int x, y; struct foo q, p; struct list_head node; list_add(&q.node, &b.head); }; … // get the item at head of list struct bar { struct foo * f; int a, b; f = list_first_entry( struct list_head head; &b.head, struct foo, node }; );

6 Circular Linked List typedef struct list_head { struct list_head *next, *prev; } LH; static inline void INIT_LIST_HEAD(LH *list) { list->next = list; list->prev = list; }

#define list_add(new, head) \ __list_add((new), (head), (head)->next) static inline void __list_add(LH *new, LH *prev, LH *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } 7 Circular Linked List

 list_head structures point to themselves, and not to their containing object(s) b empty list head: prev next list of size 2

b p q head: node: node: prev next prev next prev next

8 container_of

 How does list_first_entry cast list_head to struct foo? struct foo * f = list_first_entry(&b.head, struct foo, node);

#define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member)

#define list_entry(ptr, type, member) \ container_of(ptr, type, member)

#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) ); \ })

9 typeof

 Returns type of expression (at compile time) int x = 5; typeof(&x) y; // y is of type “int *”

#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) ); \ })  container_of((&b.head)->next, struct foo, node) expands to: typeof(((struct foo *)0)->node) *__mptr = ((&b.head)->next); (struct foo *)((char *)__mptr - offsetof(struct foo, node));

10 offsetof

 Returns byte offset of member variable in structure h igher address #define offsetof(TYPE, MEMBER) \ 4 a: int ((size_t) &((TYPE *)0)->MEMBER) 1 struct baz { int a; char s; char t; }; s: char // prints 5 1 t: char printf(“%d”, offsetof(struct baz, t)); typeof(((struct foo *)0)->node) *__mptr = ((&b.head)->next); (struct foo *)((char *)__mptr - offsetof(struct foo, node)); expr struct list_head * __mptr = ((&b.head)->next); p /* this is the value of the expression */ x, y (struct foo *)((char *)__mptr – 8; __mptr node: prev next 11 Generics in C

 Use generic pointer (void *)  Use generic buffer  Use container_of to reference arbitrary parent object

 Lots of low level pointer manipulation  Type-unsafe  Easy to make mistakes

12 Generic Programming

 Writing program that makes minimal assumption about the structure of data  Maximize code reuse across different data types  Parametric polymorphism  Ability to handle values without depending on their types  Concatenating two lists can be done without knowing type of element  Metaprogramming  Writing program that modifies programs  Generics often implemented with metaprogramming

13 Parametric Polymorphism

 Problem with statically typed languages  Redundant implementation of common algorithm

/* assumed operator< implemented */ int min(int a, int b) { Complex min(Complex a, Complex b) { return a < b ? a : b; return a < b ? a : b; } }  Imagine if common code is complex (like a container)  Tedious  Time consuming  Error prone (one mistake means fixing all other versions)

14 Generics in Java

 Before J2SE 5.0

List v = new ArrayList(); v.add(“hello world”); // inserting a string into v Integer i = v.get(0); // ERROR: runtime type error  After  Compile-time type checking added to generics  Underlying implementation remains same (shared generics)

List v = new ArrayList(); v.add(“hello world”); // ERROR: cannot add String v.add(5); // OK: can add int to Integer list Integer i = v.get(0); // OK: list guaranteed to store Int 15 Generics in C++

 Template Metaprogramming  For each template instantiation, new code is generated

vector v; v.push_back(5); // insert 5 into list cout << v[0] << endl; // print first element of list (5)

vector f; // another instance of template f.push_back(2.3);  Can seriously bloat size of program  Abuse of templates can result in very large executable  Need proper balance with Java/C’s approach

16 C++ Template Programming

an introduction

17 Template Function

 Behaves like function except function is created when function is used (template instantiation)  Template must fully implement (define) function  Cannot just declare the function (why?)

template template parameter T T min(T a, T b) { T is called parameterized type return a < b ? a : b; (type that is a parameter) }

std::cout << min(4, 9); // prints 4 std::cout << min(3, 7); // can omit (type inferred)

18 C++ Template

 Type-safe at compile time  Template instantiated (code generated) on use  Type must support all used operations in template  E.g. operator< must be supported by type T

template T min(T a, T b) { return a < b ? a : b; }  If not, must use template specialization

19 Template Specialization

 Allows alternative implementation for a particular type  Benefit  Code does not make it to executable if not used! template T min(T a, T b) { return a < b ? a : b; }

/* use strcmp to compare cstrings */ template<> const char * min(const char * a, const char *b) { return strcmp(a, b) < 0; } 20 Multiple Parameters

 Templates can have multiple parameters  Instantiation may require disambiguiation

/* template declaration – must define within same file */ template T convert(F v); template A distance(A a, B b); double foo(double d) { /* C++ does *not* infer from return type */ int i = convert(d); // from double to int char c = convert(i); // from int to char /* instantiate convert */ double(*func)(char) = convert; // function pointer return distance(func(c), 5); } 21 Other Parameters

 C++ templates do not restrict on parameter type

template vector & partial_sort(vector & v) { if (v.size() < A) return v; int max = v.size(); if (max >= B) max = B; for (int i = A; i <= max-1; i++) { for (int j = i+1; j <= max; j++) { if (v[j] < v[i]) swap(v[j], v[i]); // pass by reference } } return v; } 22 Function Overloading

1. Non-template function overload 2. Template specialization 3. More specific and specialized template 4. Base template template void f(T); // #1: template overload template void f(T*); // #2: template overload void f(double); // #3: non-template overload template<> void f(int); // #4: specialization of #1 f('a'); // calls #1, f(char) f(new int(1)); // calls #2, f(int *) f(1.0); // calls #3, f(double) f(1); // calls #4, f<>(int) 23 ECE326 PROGRAMMING LANGUAGES

Lecture 14 : C++ Class Template

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Class Survey

 Need more examples  Examples are hard to follow  Laser pen ordered  Need extra reading  Practice midterm  In class, Friday 25th, solution will be posted afterwards  Tuesday 29th class will be for review only  More correlation between assignment and lecture  Subsequent assignments (2 and onward) are related 2 Class Template

 Template for creating classes  Typically used to implement generic constructs  E.g. containers  Reuse source code instead of object code  Allows full spectrum of C++ features  Inheritance  Dynamic dispatch  Ad-hoc polymorphism

3 Generic Stack template // same as typename T class Stack { public: Stack(unsigned cnt=8); ~Stack() { delete [] array; } bool push(const T &); // push element to top of stack bool pop(); // remove current top of stack T * top(); // return current top of stack int empty() const { return end == -1; } int full() const { return end == count - 1; } private: int count; // capacity of stack int end; // points to top of stack T * array; } ;

4 Member Functions

 Can be defined outside of class template  But must be defined together, preferably in same header file template Stack::Stack(unsigned cnt) : count((int)cnt), end(-1), array(new T[cnt]) {} template bool Stack::push(const T & item) { if (full()) return false; array[++end] = item; return true; }

5 Member Functions template bool Stack::pop() { if(empty()) return false; --end; return true; } // general rule of thumb: // return a pointer if you want to indicate error with nullptr // return by reference (T &) if no error is possible, OR // if you plan to raise exception upon error template T * Stack::top() { if (empty()) return nullptr; return &array[end]; } 6 typedef

 Creates an alias for the type name  Common use  Indicate different usage  E.g. size_t instead of unsigned long confer context  reduce length of type names, or improve appeal  Again, do not abuse this feature

/* this is bad – hides the fact that it’s a pointer */ typedef char * cstring; typedef Stack DoubleStack; typedef vector > IntVector; typedef int (* compare_f)(int, int); 7 Sample Use

DoubleStack ds = DoubleStack(); double * dp, d = 1.1; cout << "Pushing elements onto stack" << endl; while (ds.push(d)) { cout << d << ' '; d += 0.7; } cout << endl << "stack full" << endl << endl << "Popping elements from stack" << endl ; while ((dp = ds.top())) { cout << *dp << ' '; ds.pop(); } cout << endl << "stack empty" << endl ; Pushing elements onto stack Popping elements from stack 1.1 1.8 2.5 3.2 3.9 4.6 5.3 6 6 5.3 4.6 3.9 3.2 2.5 1.8 1.1 stack full stack empty 8 Generic Container

 Duplicate code if many template instantiations  Can cause code bloat – large executable size  Goal  Can we use a generic Stack implementation, and use templates to provide type-safety?  Approach  Mix template and inheritance  Note  the following example actually worsens executable size

9 Generic Stack class Generic { public: Generic(unsigned size, void (*init)(char *), void(*dest)(char *), unsigned cnt=8); ~Generic(); /* pop, empty and full are omitted – same implementation */ private: void(*dest)(char *); // function pointer to destructor int end; // points to top of stack int size; // size of each element protected: int count; // capacity of stack char * buffer; void * push(); // returns address to place new element void * top(); // return current top of stack }; 10 Generic Stack Constructor

Generic::Generic(unsigned size, void (* init)(char *), void (* dest)(char *), unsigned cnt) : dest(dest) , end(-1) , size(size) , count(cnt) , buffer(new char[size*cnt]) { char * ptr = buffer; for (int i = 0; i < count; i++) { // calls constructor for each element init(ptr); ptr += size; } }

11 Generic Stack Destructor

Generic::~Generic() { char * ptr = buffer; for (int i = 0; i < count; i++) { /* calls destructor for each element */ dest(ptr); ptr += size; }

delete [] buffer; }

12 Generic Stack Functions

// returns address to place new element void * push() { if (full()) return nullptr; return buffer + (++end)*size; }

// return current top of stack void * top() { if (empty()) return nullptr; return buffer + end*size; }

13 Stack Template template class Stack : public Generic { static void initialize(char * ptr); static void destroy(char * ptr); public: Stack(unsigned cnt=8) : Generic(sizeof(T), &Stack::initialize, &Stack::destroy, cnt) {} bool push(const T & elem) { T * item = (T *)Generic::push(); if (item == nullptr) return false; *item = elem; // invokes copy constructor return true; } T * top() { return (T *)Generic::top(); } }; 14 Placement New

 Calling constructor of type at preallocated address  When used, destructor must be called manually

template void Stack::initialize(char * ptr) { new (ptr) T(); }

template void Stack::destroy(char * ptr) { T * item = (T *)ptr; item->~T(); }

15 Sample Use typedef Stack PointStack; Assume a class PointStack ps; named Point with Point * pp; members x and y int i = 1; Point p(i, i); cout << "Pushing elements onto stack" << endl; while (ps.push(p)) { cout << p << ' '; p = Point(i, i); i++; } cout << endl << "stack full" << endl; Pushing elements onto stack (1, 1) (2, 2) (3, 3) (4, 4) (5, 5) (6, 6) (7, 7) (8, 8) stack full

16 Static Members

 Static member functions  Function shared across all instances of same class  Doest no have a this pointer  Static member variable  Variable shared across all instances of same class  Static function variable  Has global lifetime but has same scope as local variables  Each template class/function has its own copy of static members, not shared across templates!

17 Static Class Members class Puppy { static int num_puppies; public: Puppy() { num_puppies++; } ~Puppy() { num_puppies--; } static void status() { cout << “I have ” << num_puppies << “ puppies” << endl; } }; int Puppy::num_puppies = 0; // instantiate static variable … Puppy a, b, c, d; Puppy::status();

I have 4 puppies

18 Static Function Variable

 Persists through function calls  Initialized once (unless changed by function)  Can be useful if your function cannot return null  Must instead return default or error value template class List { T array[C]; public: const T & operator[](int idx) { // makes elements read-only static T defval = T(); if (idx < 0 || idx >= C) return defval; return array[idx]; } }; 19 Static Assertion

 Compile time check for certain guarantees  Helps you find bugs at compile time rather than runtime #include // includes definition for is_base_of struct Shape { virtual double area() const=0; }; template class Box { public: Box() { static_assert(std::is_base_of::value, "S must inherit from Shape"); } }; /* static assertion failed: S must inherit from Shape */ class Box foobox; 20 Default Template Parameter

 Template parameters can take defaults too template class Stack : public Generic { static void initialize(char * ptr); static void destroy(char * ptr); public: Stack() : Generic(sizeof(T), &Stack::initialize, &Stack::destroy, C) {} … };

/* creates an integer stack with capacity of 16 */ Stack<> stack;

21 Partial Specialization

 Specialize only some of the template parameters template class Map { struct Pair { K key; V value; } array[S]; public: … V * find(const K & k) { int i; for (i = 0; i < S; i++) { if (array[i].key == k) return &array[i].value; return nullptr; } 22 }; Partial Specialization

 Specialize only some of the template parameters template class Map { V values[S]; public: … V * find(int k) { if (k < 0 || k >= S) return nullptr; return &values[i]; } };

23 Template Aliases

 Similar to typedef, except for partial templates  Not the same as partial specialization  Only gives a different name to templates

template class Map { … };

template using StringMap = Map;

24 ECE326 PROGRAMMING LANGUAGES

Lecture 15 : Reflective Programming

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Introspection

 The ability to examine the type or attribute of a value  At runtime  Compile-time introspection is called static introspection  Python examples  isinstance(object, cls)  Checks if object is an instance of class class A : pass >> isinstance(obj, A) class B(A) : pass True >> isinstance(obj, int) >> obj = B() False >> isinstance(obj, B) >> isinstance(A, B) True False 2 Introspection

 issubclass(cls1, cls2)  Checks if class is a subclass of another

class A : pass >> issubclass(A, B) class B(A) : pass False >> obj = B() >> issubclass(B, A) >> issubclass(obj, B) True TypeError: obj must be a class

 dir(object=None)  Returns a list of object’s attributes >> dir(A) [‘__init__’, ‘__class__’, '__delattr__', '__dict__', …]

3 Introspection

 hasattr(object, name)  Checks if string name is the name of one of the object’s attribute

class A: >> hasattr(A, ‘y’) x = 5 False def foo(): pass >> my_name = ‘foo’ >> hasattr(A, my_name) >> hasattr(A, ‘x’) True True  type(object)  Returns type of object >> type(A.foo) >> a = A() >> type(a) >> type(a.foo) 4 Introspection

 C++ Example  Runtime Type Information (RTTI)  typeid  Returns the type id of an object

if (typeid(Student) == typeid(*object)) { return hash_student(object); }  dynamic_cast  Downcasts a base class pointer to a subclass pointer, if valid

Animal * ap = animals.pop(); Lion * lp = dynamic_cast(ap);

5 Static Introspection

 Introspection at compile time  Treating compiler as a white box  The compiler reveals what it knows about an entity  type, variable, expression, …etc  Make use of how compiler internally represents an entity  C++ example  decltype  Returns the type of an expression at compile time  typeof is the non-standard version of decltype decltype(7/2) a = 5; // a is of type int 6 Reflection

 The ability for a process to introspect and modify itself  Changes its own code, such as structure and behavior  Can even change the programming language itself  E.g. syntax, semantic, implementation  Process  A running instance of a program  Static reflection  Generates compile-time meta-objects  E.g. dir from Python for C++, only accessible at compile-time

7 Reification

 Turns abstract representation into concrete data types and/or addressable objects  Simpler definition  Converting compile time types into run-time entities  Java Example  Type information kept to perform runtime type checking

String strings[] = {"a", "b"}; Object objects[] = strings; // allowed at compile time objects[0] = 5; // allowed at compile time java.lang.ArrayStoreException: java.lang.Integer 8 Type Erasure

 Removal of type information/checks at runtime  Type checking at compile-time, none at runtime  C++ Example struct data { int normalized(struct data * d, int i) { int norm; return d->sample[i] / d->norm; int sample[16]; } } ; movslq %edx, %rdx Generated code assumes movl 4(%rcx,%rdx,4), %eax correct structure (struct data) cltd is passed in. No type checking Idivl (%rcx) is made at runtime, which ret improves performance

9 Reflection and Reification

 Statically-typed, interpreted or byte-compiled languages  Anything that uses JVM, E.g. Java, Kotlin

Reflection

Executable Source Code Code (Compile (Run time) time)

Reification 10 Static Reflection

 Statically-typed, systems programming languages

Type introspection Reflection

Executable Compiler Source Code Code Internal

Type erasure Reification

11 Python Reflection

 setattr(object, name, value)  Set an object’s attribute with name to arbitrary value

class A: >> setattr(A, ‘y’, 7) x = 5 >> A.y 7 >> setattr(A, ‘x’, set) # equivalent to A.z = None >> A.x >> setattr(A, ‘z’, None) set([])  getattr(object, name, default=None)  Retrieves an attribute by name, return default if not found. If default is not specified, raise AttributeError

>> getattr(A, ‘y’, 0) # equivalent to A.y 0 >> getattr(A, ‘y’) 12 Python Reflection

 delattr(object, name)  Delete an object’s attribute by name

class A: >> hasattr(A, ‘x’) x = 5 False # equivalent to del A.z >> delattr(A, ‘x’) >> delattr(A, ‘z’,)  globals(), locals(), vars(object)  Returns a dictionary of all global/local/instance variables

>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__annotations__': {}, '__builtins__': , …}

13 Managed Attributes

 Provides control over attribute access  E.g. fetch (get), assignment (set), or deletion (del)  Property  Allows attribute access to invoke methods  Makes calling methods appear as a data attribute access

class Person: def get_full(self): return self.first + “ ” + self.last def set_full(self, value): self.first, self.last = value.split(“ ”, maxsplit=1) full_name = property(get_full, set_full)

14 Descriptor

 A class that customizes get, set, and/or delete of another object’s attribute  Similar to property, except more flexible  Since it’s a class, it can be subclassed, or inherit another

class Descriptor: def __get__(self, instance, owner): … def __set__(self, instance, value): … def __delete__(self, instance): … class Foo: managed = Descriptor() f = Foo() f.managed = 5 # calls Descriptor.__set__ 15 Descriptor

 __get__(self, instance, owner)  instance is the instance variable, None if attribute is accessed through the class (Foo.attr instead of f.attr)  owner is always the class (e.g. Foo)

>> f.managed # self: Descriptor instance, instance: f, owner: Foo >> Foo.managed # self: Descriptor instance, instance: None, owner: Foo

 __set__(self, instance, value)  If not defined, allows attribute to be overwritten!  Unlike property, default behaviour makes attribute read-only 16 Descriptor class CreditCard: NUM_DIGITS = 16 def __init__(self, name, number): self.name, self.number = name, number class Number: def __get__(self, instance, owner): return self.number[:-4] + '****' def __set__(self, instance, value): value = value.replace('-', '') if len(value) != instance.NUM_DIGITS: raise TypeError('invalid credit card number') self.number = value number = Number() card = CreditCard("Jack", "1234-3453-5256-1758") print(card.number) # prints 123434535256**** 17 __setattr__

 Intercepts all assignments to the object’s attribute  Example

class Immutable: def __init__(self, x, y): self.x, self.y = x, y

def __setattr__(self, name, value): raise AttributeError(“cannot update read-only object”)

>> obj = Immutable(5, 6) >> obj.x = 3 AttributeError: cannot update read-only object

18 __getattr__

 Intercepts all fetch (get) from an object that results in attribute not found  Before the AttributeError is raised  Use case  Returning default values on attribute not found  Automatic forwarding  Caveat  Does not intercept if method overloads an operator  Anything that starts and ends with __ (e.g. __getitem__)

19 Automatic Forwarding class Hand: def __init__(self, cards=tuple()): self.cards = list(cards) # copy the list

def _points(self): return sum(self.cards) points = property(_points)

def __getattr__(self, name): return getattr(self.cards, name)

>> p = Hand([2, 3, 4]) >> p.append(9) # goes through __getattr__ >> print(p.points) # points exists – does not go 19 # through __getattr__

20 __getattribute__

 Intercepts all fetch (get) from an object  Also includes those not found (i.e. __getattr__)  Danger – improper use will result in infinite recursion  Use super() instead of self to avoid infinite recursion  Similar caveat as __getattr__  May be bypassed by operator overloading  Use case  Disable access to “private” members

21 Private Members class Protected: def __init__(self, x, y): self._x, self._y = x, y

def getX(self): return vars(self)['_x'] # same as self.__dict__[‘_x’]

def __getattribute__(self, name): val = super().__getattribute__(name) if name != "__dict__" and name.startswith("_"): raise AttributeError(name + “ is a private member") return val

>> p = Protected(5, 7) >> p.getX() >> p._x 5 AttributeError: _x is a private member 22 ECE326 PROGRAMMING LANGUAGES

Lecture 16 : Python Decorator

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Decorator

 A way to augment an existing function or class  E.g. Do something before and/or after a function call  E.g. modify a class in some ways

@function_decorator @class_decorator def foo(): class Foo: pass pass  Decorator is a syntactic sugar  A callable that returns a callable object (e.g. function or class)

# equivalent to the definitions above foo = function_decorator(foo) Foo = class_decorator(Foo)

2 Callable

 An entity that can be called  Accepts some argument(s) and returns some value(s)  Functor  A function object  In Python, an instance of a class that implements __call__  In C++, an instance of a class that implements operator()  Has a different meaning in mathematics, do not confuse!

class Foo: >> f = Foo() def __call__(self, arg): >> f(5) # works just like return arg + 2 7 # a function

3 Inner Function

 A function defined inside a function  Also known as a nested function  Have access to variables in enclosing function  Known as outer variables  Requires nonlocal statement to reassign outer variables def outside(): def outer(): outsideList = [1, 2] stuff = None def nested(): def inner(): outsideList.append(3) nonlocal stuff nested() stuff = 5 print(outsideList) inner() # [1, 2, 3] return stuff # returns 5 4 Closure

 An inner function that is returned by the outer function that uses outer variables  Accessible even after the outer function finishes!  Retain values they had at the time the closure was defined

def adder(outer_argument): # outer function def inner(inner_argument): # inner function # ** notice outer_argument ** return outer_argument + inner_argument return inner add5 = adder(5) # a function that adds 5 to its argument add7 = adder(7) # a function that adds 7 to its argument print add5(3) # prints 8 print add7(3) # prints 10 5 Decorator

 Wraps a function and modify its behaviour def time_me(func): def wrapper(*args, **kwargs): start = datetime.now() ret = func(*args, **kwargs) diff = datetime.now() - start print("Took”, diff, “to run”, func.__name__)) return ret return wrapper @time_me def non_recursive_expensive_function(n): … # does some expensive computation >> non_recursive_expensive_function(100) Took 0:01:02.423414 to run non_recursive_expensive_function 6 Decorator

 Decorator permanently alters the function  All later calls are also “decorated”  Make sure that’s the behaviour you want @time_me def fib(n): return 1 if n < 2 else fib(n-1) + fib(n-2) >> fib(5) Took 0:00:00.000004 to run fib Took 0:00:00.000006 to run fib Took 0:00:00.000281 to run fib … @time_me def fibonacci(n): # fixed version return fib(n) # assume fib(n) no longer decorated 7 Pros and Cons

 Benefits  Explicit syntax  easy to notice augmentation on function/class  Code reuse  Same decorator can be used for many functions/classes  Drawbacks  Performance  Decorator requires additional function call(s)  Type change  Function/class now exists inside a wrapper, may break existing usage

8 Decorator with Arguments

 Decorators can take arguments def repeat(ntimes): def decorator(func): def repeater(*args, **kwargs): for __ in range(ntimes): func(*args, **kwargs) # no return value return repeater decorator return >> a = foo(“hi”) # same as foo = repeat(5)(foo) hi hi hi hi hi @repeat(5) >> a None def foo(msg): print(msg, end=“ ”) return len(msg) Decorator can “eat” your return value, beware 9 Caveat

 Decorator breaks some introspection attributes def foo(msg): “foo prints msg without new line” This is called a doc print(msg, end=“ ”) string, which allows you return len(msg) to use help(object) during interactive use . >> foo.__name__, foo.__doc__ (‘foo’, ‘foo prints msg without new line’)

@repeat(5) def foo(msg): … >> foo.__name__, foo.__doc__ (‘repeater’, None) 10 wraps Decorator

 If it matters to you, fix it via wraps decorator import functools def twice(func): ntimes = 2 @functools.wraps(func) # fixes introspection def repeater(*args, **kwargs): for __ in range(ntimes): func(*args, **kwargs) return repeater @twice def foo(msg): … >> foo.__name__, foo.__doc__ (‘foo’, ‘foo prints msg without new line’) 11 Decorator Example

 Decorator does not have to alter function  Can be used to register certain functions for other use

EXPORTS = {} def register(func): EXPORTS[func.__name__] = func return func

@register def mangle(text): … # scrambles text and returns it

def manipulate_text(transformer, text): if transformer in EXPORTS: return EXPORTS[transformer](text) return text 12 Decorator Example

 Modifying output of function def add_unit(unit): def decorator(func): def transform(*args, **kwargs): # assume this class keeps magnitude and unit return Quantity(func(*args, **kwargs), unit) return transform return decorator # assume we know this function returns speed in km/hr @add_unit(“km/hr”) def speed(distance, time): … >> print(speed(10, datetime.timedelta(minutes=5)) 120 km/hr 13 Input Type Checking def type_check(**argchecks): def decorator(func): code = func.__code__ allargs = list(code.co_varnames[:code.co_argcount]) def on_call(*pargs, **kargs): positionals = allargs[:len(pargs)] for argname, argtype in argchecks.items(): if argname in kargs: assert(isinstance(kargs[argname], argtype)) elif argname in positionals: pos = positionals.index(argname) assert(isinstance(pargs[pos], argtype)) return func(*pargs, **kargs) return on_call return decorator

14 __code__

 Contains information about the function’s code  code.co_varnames: local variables, first N are arguments  code.co_argcount: number of arguments

@type_check(a=int, b=float) def foo(a, b): # co_varnames = (‘a’, ‘b’, ‘c’) c = 5 # co_argcount = 2 return a + b + c >> foo(2, 2.3) # OK >> foo(5, b=3.3) # OK >> foo(3, 4) # FAIL – b is not a float AssertionError

15 Built-in Decorators

 Decorators we have seen (or used)

class Foo: default = 0 @property def value(self): return getattr(self, ‘_v’, self.default) @value.setter def value(self, v): # method name MUST be the same! self._v = v @classmethod def setDefault(cls, v): cls.default = v

16 Decorator with Functor class counter: def __init__(self, func): self.func = func self.count = 0

def __call__(self, *args, **kwargs): self.count += 1 return self.func(*args, **kwargs)

@counter def fibonacci(n): return 1 if n < 2 else fib(n-1) + fib(n-2) >> fibonacci(10) >> fibonacci.count 177 17 Problem with Method

 Decorator made with functor requires self in __call__  When decorating method, which self does it refer it?  The decorator, or the instance of the decorated method?

class Foo: @counter # this doesn’t work def add(self, n): self.x += n  Solution  Must use a proxy object

18 Workaround class proxy: def __init__(self, desc, inst): self.desc, self.inst = desc, inst

def __call__(self, *args, **kwargs): return self.desc(self.inst, *args, **kwargs) class counter: def __init__(self, func): >> f = Foo() self.func = func >> f.add(2) self.count = 0 >> f.add(6) # must go through desc # from descriptor! >> f.add.desc.count def __get__(self, instance, owner): 2 return proxy(self, instance)

19 Class Decorator

 Example: Singleton def singleton(aClass): instance = None def onCall(*args, **kwargs): # On instance creation nonlocal instance if instance == None: instance = aClass(*args, **kwargs) else: raise RuntimeError(“Cannot instantiate ” + \ aClass.__name__ + “ more than once”) return instance return onCall >> a = Calculator() >> b = Calculator() @singleton RuntimeError: Cannot instantiate Calculator class Calculator: more than once … 20 Class Wrapper def tracer(Cls): # On @ decorator class Tracer: def __init__(self, *args, **kargs): self.fetches = defaultdict(int) self.wrapped = Cls(*args, **kargs) def __getattr__(self, name): self.fetches[name] += 1 return getattr(self.wrapped, name) return Tracer >> sue = Employee(“Sue”, 40, 39.99) @tracer >> print(sue.name) class Employee: Sue … >> sue.pay() def pay(self): 1599.6 return self.hours * \ >> sue.fetches self.rate {'name': 1, 'pay': 1} 21 Limitation

 __getattr__ does not work for special built-in methods  Workaround  Use a mixin that to force routing through __getattr__ class BuiltinMixin: def __init__(self): self._getattr = self.__class__.__getattr__ def __add__(self, other): return self._getattr(self, '__add__')(other) def __str__(self): return self._getattr(self, '__str__')() def __getitem__(self, index): return self._getattr(self, '__getitem__')(index) 22 Updated Tracer def tracer(Cls): class Tracer(BuiltinMixin): def __init__(self, *args, **kargs): # initialize super class super().__init__() self.fetches = defaultdict(int) self.wrapped = Cls(*args, **kargs) def __getattr__(self, name): self.fetches[name] += 1 return getattr(self.wrapped, name) return Tracer >> sue = Employee(“Sue”, 40, 39.99) >> print(sue) # calls __str__ … >> sue.fetches {'name': 1, 'pay': 1, '__str__': 1} 23 ECE326 PROGRAMMING LANGUAGES

Lecture 17 : Python Metaclass

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Metaclass

 A class whose instances are classes  Defines behaviour of classes and their instances  Similar to class decorator in purpose  Differences  Class decorator  Rebinds class name to a callable  Can modify the existing class or add a wrapper around existing class  Metaclass  Inserts or routes specialized logic during class creation

2 type

 The base metaclass  Creates all classes, including metaclasses  All classes are instance of ‘type’  type(name, bases, attrs)

>> Foo = type(‘Foo’, (object, ), {‘a’ : 1}) >> Foo.a 1 >> type(Foo)

# equivalent to this: class Foo: a = 1

3 __class__

 The class type of the instance

>> Foo = type(‘Foo’, (object, ), {‘a’ : 1}) >> f = Foo() >> type(f) >> f.__class__ >> Foo >> f.__class__.__bases__ (,) >> f.__dict__ {'__module__': '__main__', 'count': 1, '__dict__': , '__doc__': None, …}

4 Metaclass

 Basic example  Class that does not need an __init__ method class NoInit(type): # metaclass inherits from type def __call__(cls, *pargs, **kwargs): inst = type.__call__(cls, *pargs) [ setattr(inst, k, v) for k, v in kwargs.items() ] return inst class Car(metaclass=NoInit): def __str__(self): return " ".join([k+"="+v for k, v in vars(self).items()]) >> car = Car(make="Mazda", model="CX-5", year="2019", color="White") >> str(car) ‘make=Mazda model=CX-5 year=2019 color=White’ >> type(car), type(Car) type of Car (, ) is NoInit! 5 What Happened? main NoInit type Car __call__() __call__() __new__()

ob

ob.__init__() ob ob

6 __new__

 The actual “constructor”  __init__ is actually just an initializer  Customizes instantiation of the object  __new__(c ls)  Default implementation class Foo: # __new__ is a static method (does not take Foo as 1st argument) def __new__(cls): # super() will eventually call object.__new__ # object is the base class of all classes return super().__new__(cls) 7 Singleton class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class Factory(metaclass=Singleton): def __init__(self, name): self.name = name

def __str__(self): return "" >> print(Factory("chocolate")) >> print(Factory("shoe")) 8 Metaclass

 Can be used as parent to supply class methods class Counter(type): class Animal(metaclass=Counter): def __init__(cls, name, def __init__(self, species): bases, attrs): self.species = species cls.counter = 0 Animal.up()

def up(cls): def __del__(self): cls.counter += 1 Animal.down()

def down(cls): def animal_test(): cls.counter -= 1 a = Animal("monkey") print(repr(Animal)) def __repr__(cls): return ""%( >> animal_test() cls.counter) >> print(repr(Animal)) 9 Name Lookup

 An instance inherits its class’s attributes  Also including attributes of super classes of its class  A class inherits its metaclass’s attributes  Also including attributes of its super classes  But – an instance does not inherit metaclass attributes

>> a = Animal(“horse”) >> a.up() # defined in Counter AttributeError: 'Animal' object has no attribute 'up'

10 __del__

 Called when instance is about to be deleted  Typically used to do additional clean-up  E.g. close log files  E.g. update global variables (such the counter)  E.g. release ownership of resources (such as cache entry)  Careful  It is not guaranteed to be executed  It all depends on the garbage collector  Do not confuse with __delete__ (used by descriptor)

11 Class Creation module M __prepare__() class A(metaclass=M): a = 1 dict def foo(self): pass Meta2 __call__(…, dict) __new__(…, dict) Meta2 metametaclass cls

cls.__init__(…, dict) cls

12 Metametaclass

 The metaclass of a metaclass  Begins the process of creating a new class  Via __call__  In contrast, a metaclass’s __call__ function initiates the process of creating a new instance

 Usually, type is the metaclass of other metaclasses  Unless metaclass is specified when defining a metaclass  Similar to instance creation, type.__call__ will execute __new__ and __init__ of the metaclass to create a new class

13 __prepare__

 Provides a dictionary-like object to store attributes  By default, returns Python dictionary – dict()  Exists for performance reasons  Special features  E.g. ordered dictionary for fast lookup

class Meta(type): @classmethod def __prepare__(mcs, name, bases, **kwargs): return {}

14 __new__ and __init__

 Constructor and initializer for the class

class Meta(type): … def __new__(mcs, name, bases, attrs, **kwargs): return super().__new__(mcs, name, bases, attrs)

def __init__(cls, name, bases, attrs, **kwargs): return super().__init__(name, bases, attrs)  __init__ is rarely used since __new__ does more  However, it is useful when inheritance is involved  In contrast, class decorator cannot be subclassed

15 Metaclass Inheritance

 A derived class can have many base classes  Each base class may have its own metaclass  Subclasses inherits base class’s metaclass  The inheritance tree of metaclass must be linear!

>> class Meta1(type): pass >> class Meta2(type): pass >> class Base1(metaclass=Meta1): pass >> class Base2(metaclass=Meta2): pass >> class Foobar(Base1, Base2): pass TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases 16 Metaclass Inheritance

 Rationale  Each class can only have one metaclass  Resolving metaclass must be unambiguous  Most specialized metaclass is chosen

>> class Meta(type): pass >> class SubMeta(Meta): pass >> class Base1(metaclass=Meta): pass >> class Base2(metaclass=SubMeta): pass >> class Foobar(Base1, Base2): pass >> type(Foobar)

17 Name Resolution

 Metaclasses come last in name resolution order  After all super classes have been checked  i.e. done after method resolution order fails  Then metaclasses are checked, in reverse order of inheritance

>> class N(type): foo = 3 # metaclass >> class M(N): attr = 1 # metaclass >> class A: attr = 2 # super class >> class B(A): pass # super class >> class C(B, metaclass=M): pass >> inst = C() >> inst.attr, C.attr, C.foo (2, 2, 3)

18 Metafunction

 Metaclass only needs to be callable  If __prepare__ is not defined, Python dict is used  If inheritance not needed, can use a function!  Can also use a functor def MetaFunc(cls, bases, attrs):  attrs["hello"] = 5 Caveat return type(cls, bases, attrs)  The function is used as a metametaclass class Foo(metaclass=MetaFunc): pass  The type is the type >> Foo.hello of the return value 5 of the function >> type(Foo) 19 Operator Overloading

 With metaclasses, classes can also have their operators overloaded, similar to an instance

class A(type): def __getitem__(cls, i): return cls.data[i] def __getattr__(cls, name): return getattr(cls.data, name) class B(metaclass=A): data = 'spam’ >> B[0] ‘s’ >> B.upper() ‘SPAM’

20 Object Attribute Lookup

cls.__getattribute__(‘x’) default A descriptor has YES ‘x’ in cls.__dict__ __get__ and one or both of NO __set__ and ‘x’ is a descriptor NO __delete__ YES return cls.x.__get__ YES ‘x’ in inst.__dict NO return inst.x YES ‘x’ in cls.__dict__ NO YES ‘x’ has __get__ cls.__getattr__(‘x’) NO default return cls.x.__get__ return cls.x AttributeError 21 Class Attribute Lookup

mcs.__getattribute__(‘x’) default A descriptor YES ‘x’ in mcs.__dict__ has __get__ and one or YES ‘x’ is a descriptor NO NO both of __set__ and return mcs.x.__get__ ‘x’ in cls.__dict YES __delete__ YES ‘x’ has __get__ NO return cls.x.__get__ ‘x’ in mcs.__dict__ NO YES NO return cls.x ‘x’ has __get__ mcs.__getattr__(‘x’) YES NO default return mcs.x.__get__ return mcs.x AttributeError 22 Type Slot

 A table of built-in methods  Operator overloading methods  E.g. __add__, __str__  Attribute interception methods  E.g. __getattr__, __setattr__  Attribute descriptors  Look up for these methods go through type slots  Much simpler and faster  Not all built-in methods go through type slots  E.g. __prepare__

23 ECE326 PROGRAMMING LANGUAGES

Lecture 18 : Type System

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Type System

 A set of types and the rules that govern their use  Controls how types affect program semantics  Helps reduce possibility of bugs in programs  Can also enable certain compiler optimization  Statically typed  Types of variables checked before runtime  Dynamically typed  Types are checked at runtime

2 Conversion

 Changing an expression from one type to another  Type system decides whether it is legal to do so  Sometimes it is not safe to do so, but legal  Precision may be lost  E.g. integer to float  32 -bit integer can store 9 decimal digits, 32-bit float can only store 7 int a = 123456789; float f = (float)a; cout << (int)f << endl; 123456792

3 Conversion

 Widening conversion  Can include all the values of the original type  Always safe  Except for possible loss in precision  Narrowing conversion  Converts to type that cannot store the entire range of value  May cause truncation, saturation or overflow

short b = (short)1.25e25; // 32767 (saturation) int a = (int)123456789.9; // 123456789 (truncation) cout << (short)a << endl; // -13035 (overflow)

4 Coercion

 Implicit type conversion  In safe cases, it is sometimes called a promotion  Some type systems allow unsafe conversions implicitly  Signed to unsigned  Narrowing conversion  Generic pointer to typed pointer

unsigned a = 5; void * v = &a; if (a > -1) { # C++ allows this (C does not) int * p = v; # C allows this (C++ does not) } 5 Implicit Conversion

#include class String { char * buf; public: String(unsigned size) : buf(new char[size]) { memset(buf, '?', size-1); // set size-1 bytes of buf to ? buf[size-1] = '\0'; } operator const char *() const { return buf; } }; void print(String s) { cout << (const char *)s << endl; } print(5); // this works: prints ???? 6 explicit Keyword

 Prevents constructor from implicit conversion

class String { char * buf; public: explicit String(unsigned size); … };  Detail  Compile can use single parameter constructor to convert from one type to another to get the right type for argument.

void print(String s); print(5); // this no longer works because constructor is explicit

7 Casts

 Explicit type conversion  Requires user intervention  Usually required for potentially unsafe conversion  Narrowing conversion  Type punning  T echnique that subverts or circumvents the type system  Changing type without altering in-memory representation

int a = 5; float * b = reinterpret_cast(&a); cout << *b << endl; 7.00649e-45 8 Strict Aliasing

 Compiler assumes pointers of different types will not alias each others for optimization purposes

int foo(int *x, long *y) { foo: movl $0, (%rdi) *x = 0; xorl %eax, %eax *y = 1; movq $1, (%rsi) return *x; ret } foo is optimized to  Without strict aliasing, this can happen always return 0 long l; printf("%d\n", foo((int *)&l, &l)); $ gcc-5 -O2 -o strict strict.c ; ./strict 0 9 Type Safety

 Accessing data in only well-defined manner  Allows only operation condoned by its type  E.g. type-safe code will not access private members  Data will always have value appropriate for its type  Requires memory safety  Data must always be initialized  Arbitrary pointer arithmetic cannot be allowed  Type error  Contravention of type safety  Can result in undefined behaviours, including crashes 10 Type Checking

 The process of verifying and enforcing type safety  Ensures that the operands of an operator are of compatible types  O ne that either is legal or is allowed to be implicitly converted  Undecidable problem  Impossible to construct an algorithm that always leads to a correct yes-or-no answer  Requires constraint solving on infinite set of input  In practice, type systems are imperfect

11 Static Type Checking

 Verifying type safety by analyzing source code  Inherently conservative  Will reject all incorrect programs  May reject some correct programs (w.r.t the type system)

if (always_true()) { /* do something */ } else { /* type error in dead code still causes compile error */ }  Limited in what can be feasibly checked  E.g. division by zero error  Most languages will not check it, even if it’s obvious int b = 0; int a = 5 / b; // C++ compiler will not complain 12 Dynamic Type Checking

 Verifying type safety at runtime  Incurs performance and/or memory overhead  Examples  Division by zero  Requires runtime exception handling  Downcasting  Requires runtime type information  Array bound checking  Requires array to have boundary information (e.g. size of array)

13 Strong Typing

 Two definitions  Not always clear which one is meant 1. A type-safe language  Type errors can always be detected and/or prevented  Language does not allow type punning at all  Requires specialized code to access in-memory representation  E.g. Python struct library 2. Stricter typing rules  Limited use of type coercion

14 Weakly Typed

 Two definitions 1. A type-unsafe language  Allows type punning  Allows arbitrary pointer arithmetic  Does not perform some essential runtime checks  E.g. Array bound check 2. Excessive use of implicit type conversion  E.g. JavaScript

15 JavaScript

16 Type Equivalence

 If operand one type can be substituted with another  Without conversion or coercion  Strict form of type compatibility  Three main type systems  Nominal  E.g. C/C++, Java  Duck  E.g. Python, Ruby  Structural  E.g. Go, Scala

17 Nominal Typing

 Name type equivalence  Most restrictive form  Variables have same type if their types have same name  Requirement  All types must be given their own unique name  Anonymous structures are given compiler internal names

struct { int a; int b; } x; // x is an object of an anonymous type

18 typedef

 typedef allows creating type name alias  At a syntactic level  The underlying type is still the same

typedef int apple, orange; // apple and orange are alias apple a = 5; // for int orange b = a; // OK – apple and orange are equivalent  Can cause loss of type safety when two semantically distinct types share the same primitive type  E.g. char [] can be a C-string or an array of bytes  Python has distinct types for str, unicode, and bytearray

19 Duck Typing

 Least restrictive form  “If it walks like a duck and it quacks like a duck, then it must be a duck”  Suitability is based only on presence of attribute def delay_appointment(app, num_days): app.date += num_days class FruitBasket: def __init__(self): … self.date = 0 basket = FruitBasket() delay_appointment(basket, 5) # OK – basket has field date 20 Structural Typing

 Structure type equivalence  Two variables are the same if they have same structure

struct Point { struct Complex { float x; float r; float y; float i; } p = { 0., 0. }; } c = { 0., 0. };

if (p == c) // OK – p and c are structurally equivalent …  Some languages restrict it to interface only  Sometimes known as “compile-time duck typing”

21 Java Interface

/* a contrived example, Java already has Object.toString() */ interface Stringer { public String to_string(); } class User implements Stringer { String name; User(String name) { this.name = name; } public String to_string() { return "User: name = " + name; } public String toString() { return to_string(); }

public static void main(String[] args) { User user = new User(“foo”); System.out.println(user); // User: name = foo } } 22 Structural Typing

 Same example in Go

// in fmt type Stringer interface { String() string } type User struct { name string } // automatically implements interface! func (user User) String() string { return fmt.Sprintf("User: name = %s", user.name) } func main() { user := User{name: "foo"} fmt.Println(user) // User: name = foo } 23 Manifest vs. Inferred

 Manifest typing  Explicit type required for all variable declaration  Inferred typing  Also known as  Can omit type information on variable declaration

auto a = 5; // a is an integer double foo(); auto b = foo(); // b is a double  Sometimes may fail due to other language features  Can always fall back on explicit type annotation

24 ECE326 PROGRAMMING LANGUAGES

Lecture 19 : Variance and Data Types

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Enumerated Type

 A data type consisting of named values  Each named value behaves as a constant  A variable of an enumerated type can be assigned any of the named value

enum Suit { Suit suit = SPADE; CLUB, DIAMOND, // can be compared HEART, if (suit > HEART) { SPADE, win(); }; }

2 Enumerated Type

 In principle, their in-memory representation should not be exposed by the programming language  In practice, C++ treats them like integers (mostly)

int a = CLUB; Suit b = HEART;

cout << a << " " << b << endl; // prints 0 2

b = (Suit)5; // this is OK in C++

Suit * sp = &b; int * ip = sp; // this is not OK without a cast

3 C Enum

 Named values can be assigned an integer expression  The type’s size can be altered via base specifier  If you prefer your enum to take up less memory

enum Suit : char { CLUB = 1,

// 2, always previous value + 1 if not specified DIAMOND, HEART = 5,

// this is very bad but is allowed: DIAMOND == SPADE SPADE = CLUB + 1, }; 4 Enum Class

 A more type-safe version of C Enum  Disallows coercion to integer and other enums  Named values are also “scoped”

enum class Suit { // no longer allowed CLUB, int a = Suit::CLUB; DIAMOND, HEART, enum Foo { A = 1 }; SPADE = 5, enum Bar { B = 1 }; }; // allowed // requires name resolution bool b = A == B; Suit suit = Suit::SPADE; // not allowed b = A == Suit::DIAMOND; 5 Union

 Stores different data types in same memory location  Type-unsafe construct, but can save memory  Can be used in C to implement private members

// size of union is always the largest member // in this case it’s the double (8 bytes) union Mangle { int i; union Private { unsigned char c; const int readonly; double d; int __writeable; }; };

Mangle m = { .i = 0xffff }; cout << (unsigned)m.c << endl; // prints 255 6 Union Cast

 Used to bypass strict aliasing rules  C/C++ does not type check other union members  Used to see memory representation of other types

union Pun { double d; unsigned char c[sizeof(double)]; };

Pun p = { .d = 1.0 }; for (unsigned i = 0; i < sizeof(double); i++) cout << (unsigned)p.c[i] << “ ”; 0 0 0 0 0 0 240 63 7 Tagged Union

 A union that has a tag field to indicate which union member is being used  Also known as variant type, or algebraic type struct Tagged { enum { INT, STR } tag; union { // anonymous union (members int * i; // can enter parent scope) string * s; }; Tagged(int i) : tag(INT), i(new int(i)) {} Tagged(const char * s) : tag(STR), s(new string(s)) {} ~Tagged() { if (tag == INT) delete i; else delete s; } }; 8 Rust Enums

 Natively supports tagged union by mixing it with enum let tree = Tree::Node(2, enum Tree { Box::new(Tree::Node(0, Leaf, Box::new(Tree::Leaf), Node(i64, Box::new(Tree::Leaf), Box, )), Box) Box::new(Tree::Node(3, } Box::new(Tree::Leaf), Box::new(Tree::Node(4, 2 Box::new(Tree::Leaf), Box::new(Tree::Leaf), )), 3 0 )), ); 4 9 Rust Enums enum Tree { Leaf, 2 Node(i64, Box, Box) } 3 0 fn add_values(tree: Tree) -> i64 { match tree { Tree::Node(v, a, b) => { 4 v + add_values(*a) + add_values(*b) }, You do not need to say return in Rust. Tree::Leaf => 0 Just leave an expression by the end of a } function. In the case of multiple } branches, all branches must return the same type. assert_eq!(add_values(tree), 9); 10 Subtype Compatibility and Contract Programming

11 Variance

 Compatibility of types and their subtypes  Substitutability  If S is a subtype of T, then objects of type T may be replaced with objects of type S without altering program correctness  Complication arises with dealing with complex types  Refers to how subtyping relation changes  E.g. when used as a parameter in a virtual function  E.g. when used as a return type in a virtual function  E.g. when used as a parameterized type

12 Variance

 Suppose Apple is a subtype of Fruit  Is a list of apple a subtype of list of fruit? template struct Fruit { struct List { int calories() const=0; T * get(unsigned i); float weight() const=0; void add(T *); float sweetness() const=0; }; unsigned count() const; }; int total_calories(const List & basket) { int total = 0; for (int i = 0; i < basket->count(); i++) total += basket->get(i)->calories(); return total; } 13 Covariance

 Allows use of more derived type than specified  Ordering of types is preserved  type can be substituted by subtype int total_calories(const List & basket) { int total = 0; for (int i = 0; i < basket->count(); i++) total += basket->get(i)->calories(); return total; } List apples = { new Apple(“Gala”, 0.3), new Apple(“Fuji”, 0.4), … }; // covariant, but NOT allowed in C++ cout << total_calories(apples) << endl; 14 Covariance

 For parameterized types, only safe if immutable

List apples = { new Apple(“Gala”, 0.3), new Apple(“Fuji”, 0.4), … };

List * fruits = &apples;

// THIS IS UNSAFE fruits->add(new Orange(“Navel”, 0.5));  However, Java allows the above to occur  Will cause a runtime java.lang.ArrayStoreException  Unless List is actually implemented as a list of assorted fruits

15 Contravariance

 Allows use of more generic type than specified  Ordering of types is reversed struct Canine { struct Food {}; virtual void eat(Meat *)=0; struct Meat : public Food {}; }; struct Fruit : public Food {}; struct Apple : public Fruit {}; struct Wolf : public Canine { … }; eat(Food *) is a subtype of eat(Meat *). In other words, eat is struct Dog : public Canine { contravariant because the parameter virtual void eat(Food *) override; of its subtype is more generic. }

Dog fido; Apple * apple = new Apple(“Honeycrisp”, 0.33); fido.eat(apple); // contravariant, but NOT allowed in C++ 16 Contravariant Parameter struct Canine { struct Food {}; virtual void eat(Meat *)=0; struct Meat : public Food {}; }; struct Fruit : public Food {}; struct Apple : public Fruit {}; struct Dog : public Canine { virtual void eat(Food *) override; }

Dog fido; Apple * apple = new Apple(“Honeycrisp”, 0.33); fido.eat(apple); // this is type safe, a dog can eat any food

Canine * canine = &fido; // now fido is a canine

// still type safe, fido can only eat meat as a canine, // but meat is also food so a dog can eat it canine->eat(new Meat(“Beef”, “10oz”)); 17 Covariant Parameter struct Canine { struct Food {}; virtual void eat(Food *)=0; struct Meat : public Food {}; }; struct Fruit : public Food {}; struct Apple : public Fruit {}; struct Wolf : public Canine { virtual void eat(Meat *) override; }

Wolf kevin; // this is type safe, a wolf eats meat only kevin.eat(new Meat(“Venison”, “8oz”);

Canine * canine = &kevin; // now kevin is a canine

// NOT TYPE SAFE, wolf cannot eat anything but meat! canine->eat(new Apple(“Red Delicious”, 0.5)); 18 Invariance

 Nonvariant  Only the specified type is accepted  Most conservative, least flexible  C++ allows covariant return types

struct AnimalShelter { virtual Animal * adopt(); };

struct CatShelter : public AnimalShelter { // OK – return type is a subtype of Animal virtual Cat * adopt() override; } 19 Liskov’s Substitution Principle

 Correctness of function subtyping is guaranteed if:  Method parameters are contravariant  Method return type is covariant  Correctness of behavioural subtyping is guaranteed if:  Precondition  Condition that must be true before execution of some code  Cannot be strengthened in a subtype  Postcondition  Condition that must be true after execution of some code  Cannot be weakened in a subtype

20 Square Rectangle Problem

 Base class can incorrectly mutate its derive class  Violation of Liskov’s substitution principle  Square has a stronger precondition than Rectangle class Rectangle { int width, height; public: Rectangle(int w, int h) : width(w), height(h) {} void set_width(int w) { width = w; } void set_height(int h) { height = h; } subclass cannot }; properly adhere struct Square : public Rectangle { to the interface Square(int s) : Rectangle(s, s) {} of the interface }; Square sq(5); sq.set_width(6); // oops 21 Example

 Weakened post-condition

class Shipment { double dimensions[3]; public: Shipment(double w, double h, double d); virtual double cost() const; }; class DiscountShipment : public Shipment { double discount; public: virtual double cost() const override; }; Shipment * package = new DiscountShipment(…);

package->cost(); // -$10.00 !?!? 22 Contract Programming

 Language support for specifying precondition, postcondition, errors, and invariants  Allows business logic to be written more “aggressively”  Without having to type check or verify assumptions  Opposite of “defensive programming”  Fills the gap that type checking cannot accomplish  Some can be done statically  Most are done at runtime

23 Assertion

 Verifies that an expression is true at runtime

assert(i > 0); // crashes program if expression not true assert(j < 0, “j must be negative”); // supported by D language

 Static version available

// syntax in D programming language static assert(Message.sizeof <= 1024, “size of message exceeds maximum packet size”);

// in C++ static_assert(sizeof(Message) <= 1024);

24 Contracts

 Defining precondition and postcondition

int fun(ref int a, int b) in { assert(a > 0); assert(b >= 0, "b cannot be negative!"); } out (r) { // r binds to the return value of fun assert(r > 0, "return must be positive"); assert(a != 0); } do { a = (b – 3)*(b + 2) + 1; return b*b; }

25 Invariants

 Characteristics of a class that must always be true  Except in methods, when temporary violation can occur class Date { int day, hour; this(int d, int h) { day = d; hour = h; } void add_hours(int h) { hour += h; if (hour >= 24) { // does not trip the invariant! d += 1; hour -= 24; } } invariant { assert(1 <= day && day <= 31); assert(0 <= hour && hour < 24, "hour out of bounds"); } 26 } ECE326 PROGRAMMING LANGUAGES

Lecture 20 : C Preprocessor Macro

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Metaprogramming

 Writing code that will generate more code  Generic programming  Writing code with minimum assumption about data types  Reflective programming  Access and/or modify program structure and/or behaviour  Has knowledge of compiler/interpreter’s internals  Different approaches  Can have overlaps  E.g. C++ template programming encompasses all three

2 Metaprogramming

 Macro systems  Maps certain input sequence into replacement output  E.g. text-based replacement  “int a = 5;”.replace(“int”, “long long”)  Generative programming  Purpose of a program is to generate code for another program  May be same or different target language  Haskell compiler can generate C code from Haskell source code  Template programming  Parameterized code which can be instantiated upon use

3 C Preprocessor Macro

 Rudimentary support for metaprogramming in C/C++  Provides text substitution of tokens  Token  A lexical unit, comprised of a type and value  E.g. int a;  int is a “keyword” token, of value “int” Preprocessor C/C++ Compiler  a is an “identifier” token, of value “a” Source Object  ; is a “separator” token, of value “;” code (.c) File (.o)

 Preprocessor Preprocessed code  Done before C source code is compiled (Macro-expanded)

4 C Preprocessor Macro

 Macro  A fragment of code with a name  Macro expansion  Name replaced by content of macro whenever name is used  Preprocessor  Scans the source code in multiple passes until no more replacement can be made  Has no knowledge of the C language  DANGER – can even use C keywords for macro names

5 Macro Constant

 Also known as object-like macro  Typically used to give name to a special literal

#define BUFFER_SIZE 1024

foo = (char *) malloc (BUFFER_SIZE); // becomes this foo = (char *) malloc (1024);  Macros can be used after it is defined, but not before

foo = X; foo = X; #define X 4 bar = X; bar = 4; 6 Macro Function

 A macro that takes zero or more parameters  Looks like a normal function using parentheses

#define hello() printf(“hello world”) // macro with two parameters #define min(X, Y) ((X) < (Y) ? (X) : (Y))

// function macro can be nested min(min(a, b), c) → min(((a) < (b) ? (a) : (b)), c) → ((((a) < (b) ? (a) : (b))) < (c) ? (((a) < (b) ? (a) : (b))) : (c)) 7 Macro Function

 Avoid expressions with side effects when using macro

#define min(X, Y) ((X) < (Y) ? (X) : (Y))

min(a++, b) → ((a++) < (b) ? (a++) : (b)) // a++ got called twice when macro is used // not the same behaviour if min() is a C function

// GNU C only solution (not part of official C standard) #define min(X, Y) \ ({ typeof(X) x_ = (X); \ ({ … }) acts like an expression. typeof(Y) y_ = (Y); \ Its value is the value of the (x_ < y_) ? x_ : y_; }) last statement. Similar to Rust

8 Macro Function

 Multiline macro requires use of continuation \  Emulating void functions

#define print_array(array) do { \ unsigned i; \ for (i = 0; i < sizeof(array)/sizeof(*(array)); i++) \ printf("%ld ", (long)*((array)+i)); \ printf("\n"); \ } while(0) do { … } while(0) is necessary to allow short a[] = { 2 , 3, 5, 46, 345, 1, -3 }; natural use of print_array(a); semicolon at end of function. Just { … } will 2 3 5 46 345 1 -3 cause syntax error! 9 Macro Function

 do { … } while(0)

#define bad_compound() { \ printf(“hello\n”); \ printf(“world\n”); }

if (x > 0) bad_compound(); else Stray semicolon printf(“x too small”); → if (x > 0) { printf(“hello\n”); printf(“world\n”); }; else printf(“x too small”);

10 Macro Function

 Wrap all arguments that can be an expression  To avoid problems with operator precedence

// round up an integer division: divroundup(11, 5) = 3 #define divroundup(x, y) (x + y – 1) / y

a = divroundup(b & c, sizeof (int)); → a = (b & c + sizeof (int) – 1) / sizeof (int); /* C’s operator precedence works like this */ a = (b & (c + sizeof (int) – 1)) / sizeof (int);

// better version (also wraps the entire expression) #define divroundup(x, y) (((x) + (y) – 1) / (y))

11 Stringification

 Macro functions can turn arguments into a string  Use # operator in front of the expression

#define WARN_IF(EXP) do { \ if (EXP) fprintf (stderr, "Warning: " #EXP "\n"); \ } while (0)

WARN_IF(x == 0); → do { if (x == 0) /* C automatically joins string literals */ fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);

12 Stringification  To stringify the value of a macro, use a helper

#define stringify_value(s) stringify(s) #define stringify(s) #s #define FOO 4 stringify_value(FOO) stringify(FOO) → → stringify_value(4) “FOO” → stringify(4) → "4"  Macro arguments are expanded before substitution, UNLESS they are stringified or “pasted” 13 Concatenation

 Pasting macro argument with another token  Use ## operator between macro and another token

#define COMMAND(NAME) { #NAME, NAME ## _command }

struct command commands[] = { struct command { COMMAND (quit), const char *name; COMMAND (help), void (*function)(); … }; }; → struct command commands[] = { { "quit", quit_command }, { "help", help_command }, … }; 14 Variadic Macro

 Macro function that takes any number of arguments  When you put ## in front of vargs, it deletes a comma in front of it if vargs is empty

#define eprintf(format, vargs...) \ fprintf(stderr, format, ##vargs)

eprintf("success!\n") → fprintf(stderr, "success!\n");

eprintf(“%s:%d: ", input_file, lineno) → fprintf(stderr, "%s:%d: ", input_file, lineno)

15 ECE326 PROGRAMMING LANGUAGES

Lecture 21 : C/C++ Macro Programming

Kuei (Jack) Sun ECE University of Toronto Fall 2019 C Preprocessor Macro

 Provides text substitution of tokens  Name replaced by content of macro whenever name is used  Faster than inline functions  Basically same as copy pasting code  Requires care as it does not understand host language  Preprocessor  Done before C source code is compiled  Scans the source code in multiple passes until no more replacement can be made

2 X Macro

 Technique for maintaining list of tokens

#define ACTIONS \ #define X(e) e, X(STAND) \ enum Action { X(HIT) \ ACTIONS X(SURRENDER) \ }; X(DOUBLE) \ #undef X X(SPLIT) #undef #define X(e) #e, deletes a const char * action_str[] = { ACTIONS }; macro. #undef X

printf(“%s %s\n”, action_str[HIT], action_str[STAND]); HIT STAND 3 Include Directive

 Adds content of file to current file  E.g. action.xmc

X(STAND) void action_str2(Action e) X(HIT) { X(SURRENDER) if (e == STAND) X(DOUBLE) return “STAND”; X(SPLIT) else if (e == HIT) void action_str2(Action e) return “HIT”; #define X(name) if (e == name) \ … return #name ; else else if (e == SPLIT) #include “action.xmc” return “SPLIT”; #undef X else {} // the last else uses this {} return “ERROR”; return “ERROR”; } } 4 Predefined Macros

 __FILE__  The current input file name (where macro is used)  __LINE__  The current line number (where macro is used)  Can be used to generate descriptive error messages

#define error(fmt, args...) \ fprintf(stderr, “%s:%d – ” fmt, __FILE__, __LINE__, \ ##args)

error(“hello %s”, “world”); macro.c:24 – hello world 5 Predefined Macros

 __FUNCTION__  Name of function the macro is in  Helpful for debugging  __DATE__  A string that represents date of compilation  __TIME__  A string that represents time of compilation  Use these for serious projects (e.g. system library)

6 For Each

 C programmers use macros to emulate foreach loop

struct point { int x, y; };

#define FOREACH(ptr, idx, array, size) \ for ((i) = 0, (ptr) = &array[i]; (i) < (size); \ (ptr) = &array[++(i)])

unsigned i; struct point * p; struct point arr[10]; FOREACH(p, i, arr, 10) { cout << “(” << p->x << “, ” << p->y << “)” << endl; }  Note that ptr can temporarily have OOB address

7 Define Function

 Can be used to define functions with same arguments

#define DEFINE_COMMAND(name) \ void name ## _command(int nargs, const char * args[])

DEFINE_COMMAND(quit) { // quit_command // exit program with exit code exit(atoi(args[0])); }

DEFINE_COMMAND(get) { // get_command FILE * f = fopen(args[0], “rt”); int c; while((c = fgetc(f)) != EOF) fputc(c, stdout); fclose(f); } 8 Comma in Arguments

 Only comma inside parentheses are preserved  Brackets or braces do not prevent separating arguments

#define STR(EXP) #EXP cout << STR((1, 2, 3)) << endl; # prints (1, 2, 3)

cout << STR([1, 2, 3]) << endl; error: macro "STR" passed 3 arguments, but takes just 1

// args preserves the comma and spacing between arguments #define VSTR(args...) #args cout << VSTR([1, 2, 3]) << endl; # prints [1, 2, 3]

9 Parenthesis Detection

 Checks to see if argument is inside parenthesis

#define SECOND(x, n, ...) n #define CHECK(...) SECOND(__VA_ARGS__, 0,) #define PROBE(x) x, 1, #define IS_PAREN(x) CHECK(IS_PAREN_PROBE x) #define IS_PAREN_PROBE(...) PROBE(!)

IS_PAREN((1, 2)) → CHECK(IS_PAREN_PROBE (1 , 2)) → CHECK(PROBE(!)) → CHECK(!, 1,) IS_PAREN_PROBE → SECOND(!, 1, 0,) is not a macro constant → 1 IS_PAREN(hi) → CHECK(IS_PAREN_PROBE hi) → SECOND(IS_PAREN_PROBE hi, 0,) → 0 10 Self-Referential Macros

 Not possible  Prevents infinite recursion during macro expansion

#define foo (4 + foo) foo → (4 + foo) # expansion stops here  This includes indirect self reference

#define x (4 + y) x → (4 + y) #define y (2 * x) → (4 + (2 * x)) y → (2 * x) → (2 * (4 + y)) 11 Self-Referential Macros

 When a macro expands, it is disabled, which prevents further expansion of same macro in the same scan  Can cause another macro to not expand  E.g. deferred expression #define EMPTY() #define DEFER(x) x EMPTY() #define EXPAND(...) __VA_ARGS__ int A() { return 456; } // not affected A() macro #define A() 123 DEFER(A)() → A EMPTY()() // A cannot expanded here → A () // requires one more scan printf(“%d”, DEFER(A)()); // prints 456 12 Self-Referential Macros

 Forcing another scan

#define EMPTY() #define DEFER(x) x EMPTY() #define EXPAND(...) __VA_ARGS__ int A() { return 456; } // not affected A() macro #define A() 123 EXPAND(DEFER(A)()) → EXPAND(A EMPTY()()) → EXPAND(A ()) → A () → 123 printf(“%d”, EXPAND(DEFER(A)())); // prints 123  This behaviour can be used to implement recursion 13 Advanced Concatenation

 Can be used to create token that is another macro

#define CAT(a, args...) a ## args #define IFF(c) CAT(IFF_, c) #define IFF_0(t, ...) __VA_ARGS__ #define IFF_1(t, ...) t #define FALSE 0 #define CAN_DO() 1

IFF(FALSE)(5, 9) → IFF(0)(5, 9) → IFF_0(5, 9) → 9 IFF(CAN_DO())(5, 9) → IFF(1)(5, 9) → IFF_1(5, 9) → 5 14 When Statement

 Previous example  Only works if macro expands to 1 or 0  We want a generalized when statement

WHEN(cond, true-expression, false-expression)  Idea:  !! operator (double negation)  Converts a number to 1 or 0, E.g. !!12 = !0 = 1  Can be achieved using macro’s pattern matching

15 When Statement

 Try 1:

#define SECOND(a, b, ...) b #define CHECK(...) SECOND(__VA_ARGS__, 0) #define PROBE() ~, 1 #define NOT(x) CHECK(_NOT_ ## x) #define _NOT_0 PROBE() #define BOOL(x) NOT(NOT(x))

BOOL(123) → NOT(NOT(123)) → CHECK(_NOT_ ## NOT(123)) → CHECK(_NOT_NOT(123)) → SECOND(_NOT_NOT(123), 0) → 0

/facepalm 16 When Statement

 Try 2:

#define CAT(a, args...) a ## args #define SECOND(a, b, ...) b #define CHECK(...) SECOND(__VA_ARGS__, 0) #define PROBE() ~, 1 #define NOT(x) CHECK(CAT(_NOT_, x)) #define _NOT_0 PROBE() #define BOOL(x) NOT(NOT(x))

BOOL(123) → NOT(NOT(123)) → NOT(CHECK(CAT(_NOT_,123))) → NOT(CHECK(_NOT_123))) → NOT(SECOND(_NOT_123, 0)) → NOT(0) → CHECK(CAT(_NOT_, 0)) → CHECK(_NOT_0) → CHECK(PROBE()) → CHECK(~, 1) → SECOND(~, 1, 0) → 1 17 When Statement

 Joining with previous example

#define CAT(a, args...) a ## args #define SECOND(a, b, ...) b #define CHECK(...) SECOND(__VA_ARGS__, 0) #define PROBE() ~, 1 #define NOT(x) CHECK(CAT(_NOT_, x)) #define _NOT_0 PROBE() #define BOOL(x) NOT(NOT(x)) #define IFF(c) CAT(IFF_, c) #define IFF_0(t, ...) __VA_ARGS__ #define IFF_1(t, ...) t #define WHEN(cond, t, f) IFF(BOOL(cond))((t), (f)) int a = WHEN(12, 5, 7), b = WHEN(0, 3, 8); a = 5, b = 8 18 Optional Compilation

 Enable or disable parts of the code  Not even compiled at all, won’t make it to final executable

int take_action(Hand hand, Action a) { if (a == SURRENDER) { #ifdef ALLOW_SURRENDER hand.profit = hand.bet / 2.0; hand.state = COMPLETE; return ERR_OK; // action accepted #else return ERR_INVALID; // action rejected #endif } … return ERR_INVALID; } 19 Optional Compilation

 Used in header to avoid being included more than once

/* if SHOE_H is not defined */ #ifndef SHOE_H #define SHOE_H

/* declaration of functions and definition of classes */

#endif

#include “shoe.h” // OK – SHOE_H not defined #include “shoe.h” // nothing included this time

 Some compilers support #pragma once  Same effect, shorter to write, but requires compiler support 20 __cplusplus

 A predefined macro  Used if mixing C and C++ code  This requires lots of care, because C is not a subset of C++  Extern “C”  Code within this block are C code, not C++

#ifdef __cplusplus extern “C” { #endif struct hand * alloc_hand(struct shoe * new); #ifdef __cplusplus } #endif 21 Version Control

 Integer, comparison, relational operators are supported

#if EASYDB_VERSION > 1 #define ALLOW_SURRENDER #endif int foo() { #if VERBOSE >= 2 printf(“entering foo"); #endif … } #if !(defined __LP64__ || defined __LLP64__) || \ defined _WIN32 && !defined _WIN64 // we are compiling for a 32-bit system #endif 22 Static Assertion

 C language does not have static_assert until C11  Useful to check compile-time constants  E.g. sizeof(struct packet) <= 1024  Solution: exploit conditional operator in C

#define STATIC_ASSERT(COND,MSG) typedef char \ static_assertion_##MSG[(COND)?1:-1]

STATIC_ASSERT(1, this_should_be_true); STATIC_ASSERT(0, this_will_be_false);

error: size of array ‘static_assertion_this_will_be_false’ is negative 23 Conclusion

 C macros provide some metaprogramming capability  Uses token based substitution  Invoked by compiler as first part of translation  Inherently unsafe, requires care  Reasonably powerful, when coupled with existing C constructs  C preprocessor  Helps manage code into files  Allows for optional compilation  Can be abused – code will become very difficult to read  If executable size not a concern, should use inheritance instead

24 ECE326 PROGRAMMING LANGUAGES

Lecture 22 : C++ Metaprogramming

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Constant Expression

 Can be evaluated at compile time

const int a = 5 + 7; // compiler would generate a = 12 directly  constexpr keyword  Declares a compile-time variable, function, or class  May not exist at runtime (unlike constant variables)  Variable  Can only be assigned constant expression  Function  Arguments must only be constant expression

2 Constexpr constexpr int a[] = { 1, 2, 3, 4 }; constexpr int sum(const int a [], unsigned n) { return (n == 0) ? 0 : a[0] + sum(a+1, n-1); }

// a good compiler should generate x = 10 directly int x = sum(a, sizeof(a)/sizeof(int)); template // template argument only accepts void print_const() { // compile-time constant values cout << X << endl; } print_const(); // OK, prints 6 print_const(); // FAIL error: array subscript value is outside the bounds of array 3 Constexpr Function

 Tells compiler to evaluate function at compile time  Can significantly increase compile time  Compiler must ensure computation cannot crash itself  Performs extensive type-checking  E .g. Array out of bound check  C++11  Restrictive on what’s allowed in a constexpr function  No loops – must rely on recursion  Exactly one return statement allowed in body  No local variables, arguments only

4 Constexpr Examples constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); }

/* lexicographical comparison of two constant strings */ /* returns positive if a > b, negative if a < b, 0 if equal */ constexpr int constcmp(const char * a, const char * b, unsigned i=0) { return (a[0] == '\0') ? (a[0] - b[0]) : ( (a[0] == b[0]) ? constcmp(a+1, b+1, i+1) : a[0] - b[0] ); } constcmp("he", "hello") // -108 constcmp("hello", "hell“) // 111 (ASCII for o) 5 Constexpr Function

 Depends on compiler implementation  May or may not be turned into a runtime function  Depends on argument

print_const(); error: ‘argv’ is not a constant expression

// function is also used at runtime cout << constcmp("hello", argv[0]) << endl; // 58  Upgrade to C++14  Allows loops and local variables!

6 Compile-Time Function

 Useful for pre-calculating values  E.g. crc64 hash of constant strings  Can be used in conjunction with templates  Referentially transparent  Does not have side effects  Note: this is only true if the function is run at compile time. If it is converted to a run time function, it can modify global variables!  Haskell does this a lot  The entire program may be optimized down to constants

7 Constexpr Class

 Its instances can be compile-time objects  Same restrictions apply to methods, but can use members

class Rectangle { int _h, _w; public: // a constexpr constructor constexpr Rectangle (int h, int w) : _h(h), _w(w) {} constexpr int area () { return _h * _w; } };

constexpr Rectangle rekt(10, 20); // compile-time print_const(); // 200

Rectangle rect(5, argc); // runtime Rectangle cout << rect.area() << endl; // 5 (if argc == 1) 8 Static Introspection

 Making programming decisions based on types  At compile time (hence “static”)  Limited support in C  E.g. sizeof and typeof (non-standard)  C++ template  Originally designed for generic programming  Its implementation allows for some introspection capability  Requires exploiting template substitution rules  Originally part of Boost library, now standardized for C++11

9 Type Trait

 #include  is_integral  Checks if type is some kind of integer (int, char, long, …etc)

template T f(T i) { static_assert(std::is_integral::value, “invalid type"); return i; }  >is_array

10 SFINAE

 Substitution Failure Is Not An Error  An invalid substitution of template parameters is not an error  C++ creates a set of candidates for overload resolution  E.g. during function overloading  For templates, if parameter substitution fails, then that template will be removed from the candidate list  without stopping on compilation error  Note: error in template body is not detected before resolution  No error is produced if more than one candidate exists

11 SFINAE example struct Test { typedef int foo; // internal type to Test }; template void f(typename T::foo) {} // Definition #1 Use of internal typedef in templates requires prefixing the type alias with typename template int does not have a void f(T) {} // Definition #2 type named foo (1st substitution fails) int main() { f(10); // Call #1. f(10); // Call #2 without error. (SFINAE) } 12 sizeof operator

 Returns size of an expression at compile time

typedef char type_test[42]; type_test& f();

// f() won't actually be called at runtime cout << sizeof(f()) << endl; // 42  Can be exploits by SFINAE  Running example  Want to check if class has serialize function  If yes, call it, otherwise, call to_string() instead

13 Member Function Pointer

 Similar to function pointer, except must specify class  Has a different type than normal functions

struct A { string serialize() const { return "I am a A!"; } };

typedef string (A::* afunc_t)();

A a; afunc_t af = &A::serialize; cout << (a.*af)() << endl; // call member function A * ap = &a; cout << (a->*af)() << endl; // call member function

14 Method Check template struct has_serialize { typedef char yes[1]; typedef char no[2]; static char tm[2];

/* checks if class T really has serialize method (not field) */ template struct really;

/* class T has serialize */ template static yes& test( really*) { return tm; } template static yes& test( really*) { return tm; }

/* SFINAE - class t does not have serialize */ template static no& test(...) { return tm; }

// The constant used as a return value for the test. static const bool value = sizeof(test(0)) == sizeof(yes); }; 15 Test 1 struct A { string serialize() const { return "I am a A!"; } }; cout << has_serialize::value << endl; // 1 – it has serialize template struct really; // (for reference)  3 candidates for Test(0)

// 1. NO: type U = string(A::*)() != typeof(&A::serialize) template static yes& test(really*)

// 2. YES: type U = string(A::*)() const == typeof(&A::serialize) template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 16 Test 1 struct A { string serialize() const { return "I am a A!"; } }; cout << has_serialize::value << endl; // 1 – it has serialize templateCompiler structcandidatereally 2,; // which (for reference)returns yes. sizeof(test(0)) == sizeof(yes) is true. so // 1. NO: type U = string(A::*)() != typeof(&Ais:: serialize)also true. templatehas_serialize::value static yes& test(really*)

// 2. YES: type U = string(A::*)() const == typeof(&A::serialize) template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 17 Test 2 struct B { int x; /* does not have serialize method */ }; cout << has_serialize::value << endl; // 0 – does not have serialize template struct really;  3 candidates for Test(0)

// 1. NO: B::serialize does not exist! template static yes& test(really*)

// 2. NO: B::serialize does not exist! template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 18 Test 2 struct B { int x; /* does not have serialize method */ }; cout << has_serialize::value << endl; // 0 – does not have serialize templateCompiler structcandidatereally ;3, // which (for reference) returns no.  3 candidates for Test(0) sizeof(test(0)) == sizeof(yes) is false, so // 1. NO: B::serialize does not exist! is also false. templatehas_serialize::value static yes& test(really*)

// 2. NO: B::serialize does not exist! template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 19 Test 3 struct C { string serialize; /* serialize is not a method */ }; cout << has_serialize::value << endl; typeof(&C::serialize) is string * (pointer to a template struct really; string), not a member  3 candidates for Test(0) function pointer.

// 1. NO: type U = string(C::*)() != typeof(&C::serialize) template static yes& test(really*)

// 2. NO: type U = string(C::*)() const != typeof(&C::serialize) template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 20 ECE326 PROGRAMMING LANGUAGES

Lecture 25 : SFINAE (C++98)

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Assignment 3

 Object Relational Mapping  Maps database rows into in-memory objects  Relational Database  Data are organized into tables (analogous to classes) and rows (analogous to instances)  Like classes, format of database tables must be specified  The specification is called database schema  EasyDB  Very simple in-memory database, does not save to disk  You will implement a (small) part of it for assignment 4 2 Database Schema

 EasyDB Schema Language  Support four data types User {  firstName: string; string (any length) lastName: string;  float (64 bits) height: float;  age: integer; integer (64 bits) }  foreign key Account {  Foreign Key user: User; // foreign type: string;  Analogous to a pointer balance: float;  References another row in } another table 3 Database Client

 First milestone  Write the client code to communicate with the server  Over the network, through TCP/IP  Requires sending and receiving packets  Packet  Data sent over the network  Requires serialization and deserialization  Converting in-memory data to/from network format

4 ORM Layer

 Second milestone  Convert raw formats to/from Python objects  Provides object-oriented interface instead of database commands  Performs extensive type safety checks  Coding requires use of advanced Python features  Metaclass  Descriptors

5 Database Schema

 Written in Python for the ORM  Can be exported to EasyDB schema language  Includes options for more rigorous type checking class User(orm.Table): firstName = orm.String() lastName = orm.String() height = orm.Float(blank=True) age = orm.Integer(blank=True) class Account(orm.Table): user = orm.Foreign(User) type = orm.String(choices=["Savings", "Chequing",], default="Chequing") balance = orm.Float() 6 ORM Interface

 Underlying database abstracted away  Provides user with a way to work with any database

# create new object # update existing object >> joe = User(db, firstName="Joe", >> joe.height = 7.4 .. lastName="Harris", age=32) >> joe.save() >> joe.save() # save to database # delete an object # search for objects in database >> result[1].delete() >> result = User.filter(db, .. lastName="Harris") # verify deletion >> result >> User.count(db, [, ] .. lastName=“Harris”) >> joe = result[0] 1 >> joe.age 32 7 Type Support

 Most database only supports few data types  EasyDB only supports integer, float, and string  At the ORM layer, we can support more!  Third milestone  DateTime Field  Corresponds to Python’s DateTime class  Coordinate Field  A tuple of two values: longitude and latitude

8 SFINAE

For C++98

9 Static Introspection

 Making programming decisions based on types  At compile time (hence “static”)  Limited support in C  E.g. sizeof and typeof (non-standard)  C++ template  Originally designed for generic programming  Its implementation allows for some introspection capability  Requires exploiting template substitution rules  Originally part of Boost library, now standardized for C++11

10 Type Trait

 #include  is_integral  Checks if type is some kind of integer (int, char, long, …etc)

template T f(T i) { static_assert(std::is_integral::value, “invalid type"); return i; }  >is_array

11 SFINAE

 Substitution Failure Is Not An Error  An invalid substitution of template parameters is not an error  C++ creates a set of candidates for overload resolution  E.g. during function overloading  For templates, if parameter substitution fails, then that template will be removed from the candidate list  without stopping on compilation error  Note: error in template body is not detected before resolution  No error is produced if more than one candidate exists

12 SFINAE example struct Test { typedef int foo; // internal type to Test }; template void f(typename T::foo) {} // Definition #1 Use of internal typedef in templates requires prefixing the type alias with typename template int does not have a void f(T) {} // Definition #2 type named foo (1st substitution fails) int main() { f(10); // Call #1. f(10); // Call #2 without error. (SFINAE) } 13 sizeof operator

 Returns size of an expression at compile time

typedef char type_test[42]; type_test& f();

// f() won't actually be called at runtime cout << sizeof(f()) << endl; // 42  Can be exploits by SFINAE  Running example  Want to check if class has serialize function  If yes, call it, otherwise, call to_string() instead

14 Member Function Pointer

 Similar to function pointer, except must specify class  Has a different type than normal functions

struct A { string serialize() const { return "I am a A!"; } };

typedef string (A::* afunc_t)();

A a; afunc_t af = &A::serialize; cout << (a.*af)() << endl; // call member function A * ap = &a; cout << (a->*af)() << endl; // call member function

15 Method Check (C++98) template struct has_serialize { typedef char yes[1]; typedef char no[2]; static char tm[2];

/* checks if class T really has serialize method (not field) */ template struct really;

/* class T has serialize */ template static yes& test( really*) { return tm;} template static yes& test( really*) { return tm;}

/* SFINAE - class T does not have serialize */ template static no& test(...) { return tm;}

// The constant used as a return value for the test. static const bool value = sizeof(test(nullptr)) == sizeof(yes); }; 16 Test 1 struct A { string serialize() const { return "I am a A!";} }; cout << has_serialize::value << endl; // 1 – it has serialize template struct really; // (for reference)  3 candidates for Test(nullptr)

// 1. NO: type U = string(A::*)() != typeof(&A::serialize) template static yes& test(really*)

// 2. YES: type U = string(A::*)() const == typeof(&A::serialize) template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 17 Test 1 struct A { string serialize() const { return "I am a A!"; } }; cout << has_serialize::value << endl; // 1 – it has serialize templateCompiler structcandidatereally 2,; // which (for reference)returns yes. sizeof(test(nullptr)) == sizeof(yes) is // 1.true. NO: typeso U = string(A::*)() != typeof(&A::serialize)is also true. template statichas_serialize::value yes& test(really*)

// 2. YES: type U = string(A::*)() const == typeof(&A::serialize) template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 18 Test 2 struct B { int x; /* does not have serialize method */ }; cout << has_serialize::value << endl; // 0 – no serialize template struct really; // (for reference)  3 candidates for Test(nullptr)

// 1. NO: B::serialize does not exist! &B::serialize fails. template static yes& test(really*)

// 2. NO: B::serialize does not exist! template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 19 Test 2 struct B { int x; /* does not have serialize method */ }; cout << has_serialize::value << endl; // 0 – does not have serialize templateCompiler structcandidatereally ;3, // which (for reference) returns no.  3 candidates for Test(0) sizeof(test(nullptr)) == sizeof(yes) is // false,1. NO: B::serialize so does not exist! is also false. template statichas_serialize::value yes& test(really*)

// 2. NO: B::serialize does not exist! template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 20 Test 3 struct C { string serialize; /* serialize is not a method */ }; cout << has_serialize::value << endl; typeof(&C::serialize) is string * (pointer to a template struct really; string), not a member  3 candidates for Test(nullptr) function pointer.

// 1. NO: type U = string(C::*)() != typeof(&C::serialize) template static yes& test(really*)

// 2. NO: type U = string(C::*)() const != string * template static yes& test( really*)

// 3. YES: this template cannot fail, but has lowest precedence template static no& test(...) 21 Test 4

 Current template does not support functors  It should, we will fix this in C++11 struct D { struct Functor { string operator()() { return "I am a D!"; } }; Functor serialize; };

D d; cout << d.serialize() << endl; // “I am a D!” cout << has_serialize::value << endl; // Output 0. 22 Applying Method Check

 make_packet function  Calls serialize if class has it, otherwise use to_string() template string make_packet(const T& obj) { if (has_serialize::value) { // error: no member named 'serialize' in 'A'. return obj.serialize(); } else { return to_string(obj); } }

A a; make_packet(a);

23 Applying Method Check

 It doesn’t work  Static type system is conservative.  Dead code is still type checked. template string make_packet(const T& obj) { /* has_serialize::value is 0 at compile time */ if (0) { // error: no member named 'serialize' in 'A'. return obj.serialize(); } else { return to_string(obj); } }

24 Workaround

 enable_if  If condition B is true, typedef T in the struct named type

// This struct doesn't define "type" and will trigger SFINAE template struct enable_if {};

// partial specialization if B is true, struct defines “type” to T template struct enable_if { typedef T type; };

// OK enable_if defines ‘type’ to int enable_if::type t1; enable_if::value, int>::type t2;

// FAIL - enable_if does not define ‘type’ enable_if::type t3; enable_if::value, int>::type t4; 25 Final Solution

 Use enable_if to switch between function overload

template typename enable_if::value, string>::type make_packet(const T& obj){ return obj.serialize(); } template typename enable_if::value, string>::type make_packet(const T& obj) { return to_string(obj); } A a; B b; C c; cout << make_packet(a) << endl; // calls a.serialize() cout << make_packet(b) << endl; // calls to_string(b) cout << make_packet(c) << endl; // calls to_string(c) 26 ECE326 PROGRAMMING LANGUAGES

Lecture 26 : Template Metaprogramming (C++11)

Kuei (Jack) Sun ECE University of Toronto Fall 2019 decltype

 Returns type of expression

A a; decltype(a.serialize()) test = "test"; // requires ‘a’ decltype(A().serialize()) t2 = "test 2"; // prefer this  What if A does not have default constructor?

struct Default { int foo() const { return 1; } }; struct NoDefault { NoDefault(const NoDefault&) {} int foo() const { return 1; } }; decltype(NoDefault().foo()) nd = 2; // FAIL 2 Fake Reference

 Recall in container_of macro

#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type, member) ); \ })  We can use this to create a fake reference to avoid constructing objects for the expression

#define FAKEREF(T) (*(T *)nullptr) decltype(FAKEREF(NoDefault).foo()) ndf = 5; // OK cout << test << ndf << endl; 5

3 std::declval

 Template version of FAKEREF  Should only be used in unevaluated context  Inside sizeof or decltype  Use this instead of FAKEREF in C++

decltype(std::declval().foo()) nd2 = ndf; // OK decltype(std::declval().foo()) nd3; // OK

// error: cannot declare pointer to ‘struct NoDefault&’ decltype(FAKEREF(NoDefault &).foo()) nd3 = 7;

4 Method Check (C++11)

 Using constexpr, decltype, and declval template struct has_serialize { // Test if T has serialize using decltype and declval // comma operator returns the value of the latter expression template static constexpr decltype(std::declval().serialize(), bool()) test(int){ return true; }

template static constexpr bool test(...) { return false; }

// Argument is used to give precedence to first overload of ‘test’ static constexpr bool value = test(0); }; 5 Tests

 New has_serialize now works for functors too! struct A { string serialize() const { return "I am a A!";} }; struct B { int x; }; struct C { string serialize; }; struct D { struct Functor { string operator()() { return "I am a D!";} } serialize; }; cout << has_serialize::value << endl; // 1 – has serialize function cout << has_serialize::value << endl; // 0 – no serialize cout << has_serialize::value << endl; // 0 – serialize not function cout << has_serialize::value << endl; // 1 – serialize is callable 6 Alternative Implementation

 :stdtrue_type:

struct true_type { enum { value = true }; };  std:false_type:

struct false_type { enum { value = false }; };  Use partial template specialization for precedence

template struct can_serialize : false_type {};

template struct can_serialize().serialize())> : true_type {}; 7 General Approach

template struct can_do_x : false_type {};

template struct can_do_x> : true_type{};  Example

template struct is_incrementable : std::false_type { };

template struct is_incrementable())> > : std::true_type { }; 8 std::void_t

 Maps a sequence of any types to void  Enables partial template specialization  Used to detect ill-formed types in SFINAE context template struct is_incrementable())> > : std::true_type { };

cout << is_incrementable::value << endl; // 1 cout << is_incrementable::value << endl; // 0 cout << is_incrementable::value << endl; // 0

9 Example

 Two template parameters  Can type T be assigned type U template struct can_assign : std::false_type { }; template struct can_assign() = std::declval())> > : std::true_type { }; cout << can_assign::value << '\n'; // 1 cout << can_assign::value << '\n'; // 0 cout << can_assign::value << '\n'; // 1 int i; double d = 5.312; i = d; d = 123L; // valid assignment without casts 10 ECE326 PROGRAMMING LANGUAGES

Lecture 27 : Variadic Template

Kuei (Jack) Sun ECE University of Toronto Fall 2019 Variadic Function

 Function with variable number of parameters  E.g. printf, scanf  Supported all the way back in C  Denoted by the ellipsis syntax

int eprintf(const char * fmt, ...);  Custom-built variadic functions are type-unsafe  Type checking not done at compile time  Note: GCC extension __attribute__((format(printf, 1, 2)))

2 cstdarg

 Provides macro functions to extract arguments  Limitation: requires a “pivot” argument  i.e. must have at least one known argument

#include // provides variable argument handling #include int eprintf(const char * fmt, ...) { va_list args; // stores variable argument list va_start(args, fmt); // like fprintf, but takes va_list instead of ... int ret = vfprintf(stderr, fmt, args); va_end(args); return ret; } 3 cstdarg

 va_start(va_list ap, T pivot)  Initialize ap with the pivot argument (can be of any type)  va_arg(va_list ap, T)  Retrieves next argument and cast it to type T  va_end(va_list ap)  End using ap and clean up resource  va_copy(va_list dst, va_list src)  Copy src to dst in its current state  May be halfway through the arguments when copied

4 Example

 Finds largest number out of n integers

int find_max(int n, ...) { va_arg is type- int i, val, largest; unsafe! It assumes va_list vl; the caller is passing va_start(vl, n); in the expected type. largest = va_arg(vl, int); for (i = 1; i < n; i++) { val = va_arg(vl, int); largest = (largest > val)? largest : val; } va_end(vl); return largest; }

find_max(7, 702, 422, 631, 834, 892, 104, 772); // 892 5 C Macro Trick

 Count number of arguments and pass to first argument  Note: this macro can be improve to support more arguments

#define PP_NARG(...) PP_NARG_(__VA_ARGS__, PP_RSEQ_N()) #define PP_NARG_(...) PP_ARG_N(__VA_ARGS__) /* PP_ARG_N() returns the 10th argument! */ #define PP_ARG_N( _1, _2, _3, _4 _5, _6, _7, _8, _9, N, ...) N /* PP_RSEQ_N() counts from 9 down to 0 */ #define PP_RSEQ_N() 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define max(args...) find_max(PP_NARG(args), ##args) max(702, 422, 631, 834, 892, 104, 772) → find_max(PP_NARG(…), 702, 422, 631, 834, 892, 104, 772) → find_max(PP_NARG_(702, 422, …, 772, PP_RSEQ_N()), 702, 422, …) /* 1, 2, …, 7, 8, 9, 10, …*/ → find_max(PP_ARG_N(702, 422, …, 772, 9, 8, 7, …, 2, 1, 0), 702, …) → find_max(7, 702, 422, 631, 834, 892, 104, 772) 6 Variadic Template

 Template with variable number of parameters

template  Introduced in C++11  Provides more type-safety by checking argument types  If pattern matching fails, code will not compile  Enables many powerful templates  Compile-time recursive function/structure definitions  Function arguments forwarding  Template arguments forwarding 7 Example

 From assignment 1 starter code, shoe.cpp template bool is_in(T & a, T b){ return a == b; }

template bool is_in(T & a, T b, Args... args){ return a == b || is_in(a, args...); }

/* read next character from file, see if it’s a valid card */ char c = getc(file); if (is_in(c, 'A', 'T', 'J', 'Q', 'K')) { return c; } 8 Example template // base template bool is_in(T & a, T b){ return a == b; } template bool is_in(T & a, T b, Args... args){ return a == b || is_in(a, args...); } is_in(c, 'A', 'T', 'J', 'Q', 'K') → c == 'A' || is_in(c, 'T', 'J', 'Q', 'K') → c == 'T' || is_in(c, 'J', 'Q', 'K') → c == 'J' || is_in(c, 'Q', 'K') → c == ‘Q' || is_in(c, 'K') → c == ‘K' 9 emplace_back

 New method for std::vector in C++11  Builds object directly within the vector  Requires neither move or copy  In contrast, vector::push_back requires premade objects  Requires forwarding arguments to constructor  Without a priori knowledge of constructor signature of type T

10 std::forward

 dSimilar to st ::move, but for variable arguments  Syntax requires ... after the variable argument expansion

template T make_and_print(Args&& ... args){ /* create object of type T using forwarded arguments */ T obj(std::forward(args)...); cout << obj << endl; return obj; }

auto c = make_and_print(5, 7); 5 + 7i

11 Example

 Print the content of template containers  E.g. std::vector, std::list  These containers usually have an optional second parameter  Custom allocators are used for performance reasons

/* 1st parameter is a templated class with two parameters */ template