From 5eb0bd3defe2f5e41cdd75c91bed08279bf8fe9c Mon Sep 17 00:00:00 2001 From: "Juan F. R. Herrera" Date: Fri, 24 Mar 2023 15:37:44 +0000 Subject: [PATCH 01/91] Update README links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 631e008..3b2be73 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Modern C++ for Computational Scientists -Repository view: +[Repository view](https://github.com/EPCCed/archer2-cpp/) -Pages view: +[Pages view](https://epcced.github.io/archer2-cpp/) Since the 2011 revision to the C++ language and standard library, the ways it is now being used are quite different. Used well, these From aadf07412a27ddb3d8d80ec34a63ae61a53e5c19 Mon Sep 17 00:00:00 2001 From: "Juan F. R. Herrera" Date: Fri, 24 Mar 2023 15:39:16 +0000 Subject: [PATCH 02/91] Create AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) create mode 100644 AUTHORS diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..dbb4fd9 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Rupert Nash From f0b8566bcb0ab0513ccb5be4fe94b1c3dc250361 Mon Sep 17 00:00:00 2001 From: "Juan F. R. Herrera" Date: Fri, 24 Mar 2023 15:57:08 +0000 Subject: [PATCH 03/91] Add license --- LICENSE | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2f244ac --- /dev/null +++ b/LICENSE @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. From bfa24ea5cba1f73e2a5cc5bb3795b0bb50d9f1ff Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 7 Apr 2023 16:57:44 +0100 Subject: [PATCH 04/91] Update eigen lecture & exercises --- AUTHORS | 1 + exercises/eigen/explicit.cpp | 71 +++---- exercises/eigen/implicit.cpp | 56 +++--- exercises/eigen/movie.py | 28 +++ exercises/eigen/sparse.cpp | 58 +++--- lectures/eigen/README.md | 347 ++++++++++++++++++++++++++++++----- lectures/eigen/index.html | 2 +- 7 files changed, 435 insertions(+), 128 deletions(-) create mode 100644 exercises/eigen/movie.py diff --git a/AUTHORS b/AUTHORS index dbb4fd9..2b27819 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ Rupert Nash +Joseph Lee \ No newline at end of file diff --git a/exercises/eigen/explicit.cpp b/exercises/eigen/explicit.cpp index ba27e73..3e0003e 100644 --- a/exercises/eigen/explicit.cpp +++ b/exercises/eigen/explicit.cpp @@ -6,40 +6,43 @@ int main() { - int n = 12; + int n = 20; + int steps = 200; + std::vector Avec(n * n); + + + // Set up matrix A + Eigen::Map A(Avec.data(), n, n); + A = Eigen::MatrixXd::Identity(n, n); + double delta = 0.4; + for (int i = 0; i < n - 1; ++i) + { + A(i + 1, i) += delta; + A(i + 1, i + 1) += -delta; + + A(i, i) += -delta; + A(i, i + 1) += +delta; + } + + std::cout << "A = \n" << A << std::endl + << std::endl; + + + // T_n + Eigen::VectorXd b(n); + b.setZero(); + b.head(n / 2).array() = 1.0; + + std::ofstream f; + f.open("explicit_sim.txt"); + for (int i = 0; i < steps; ++i) + { + f << b.transpose() << std::endl; + // update time-step T_{n+1} = A.T_{n} + b = A * b; + } - std::vector Avec(n * n); - - Eigen::Map A(Avec.data(), n, n); - A = Eigen::MatrixXd::Identity(n, n); - - double delta = 0.4; - - for (int i = 0; i < n - 1; ++i) - { - A(i + 1, i) += delta; - A(i + 1, i + 1) += -delta; - - A(i, i) += -delta; - A(i, i + 1) += +delta; - } - - std::cout << A << std::endl << std::endl; - - Eigen::VectorXd b(n); - b.setZero(); - b.head(n / 2).array() = 1.0; - - std::ofstream f; - f.open("a.txt"); - for (int i = 0; i < 20; ++i) - { std::cout << b.transpose() << std::endl; - f << b << std::endl << std::endl << std::endl; - b = A * b; - } - - std::cout << b.transpose() << std::endl; - return 0; -} + return 0; +} \ No newline at end of file diff --git a/exercises/eigen/implicit.cpp b/exercises/eigen/implicit.cpp index 2ae3a1d..e2b0a39 100644 --- a/exercises/eigen/implicit.cpp +++ b/exercises/eigen/implicit.cpp @@ -5,38 +5,42 @@ int main() { - int n = 12; + int n = 20; + int steps = 100; - Eigen::MatrixXd A(n, n); - A = Eigen::MatrixXd::Identity(n, n); + // Set up matrix A + Eigen::MatrixXd A(n, n); + A = Eigen::MatrixXd::Identity(n, n); - double delta = -0.4; + double delta = -0.4; - for (int i = 0; i < n - 1; ++i) - { - A(i + 1, i) += delta; - A(i + 1, i + 1) += -delta; + for (int i = 0; i < n - 1; ++i) + { + A(i + 1, i) += delta; + A(i + 1, i + 1) += -delta; - A(i, i) += -delta; - A(i, i + 1) += +delta; - } + A(i, i) += -delta; + A(i, i + 1) += +delta; + } - std::cout << A << std::endl << std::endl; + std::cout << "A = \n" + << A << std::endl + << std::endl; - Eigen::VectorXd b(n); - b.setZero(); + Eigen::VectorXd b(n); + b.setZero(); - b.head(n / 2).array() = 1.0; + b.head(n / 2).array() = 1.0; - std::ofstream f; - f.open("a.txt"); - for (int i = 0; i < 20; ++i) - { - std::cout << b.transpose() << std::endl; - f << b << std::endl << std::endl << std::endl; - b = A.colPivHouseholderQr().solve(b); - } + std::ofstream f; + f.open("implicit_sim.txt"); + for (int i = 0; i < 200; ++i) + { + // Solve A.T_{n+1} = T_{n} for T_{n+1} + f << b.transpose() << std::endl; + b = A.colPivHouseholderQr().solve(b); + } - std::cout << b.transpose() << std::endl; - return 0; -} + std::cout << b.transpose() << std::endl; + return 0; +} \ No newline at end of file diff --git a/exercises/eigen/movie.py b/exercises/eigen/movie.py new file mode 100644 index 0000000..01e149d --- /dev/null +++ b/exercises/eigen/movie.py @@ -0,0 +1,28 @@ +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.animation import FuncAnimation + +n = 20 +steps = 200 + +fig, ax = plt.subplots() +xdata = np.arange(0, n) +ydata = [] +ln, = ax.plot([], [], 'ro') + +data = np.loadtxt("implicit_sim.txt") +def init(): + ax.set_xlim(0,n) + ax.set_ylim(0,1.1) + return ln, + +def update(frame): + ydata = data[frame] + ln.set_data(xdata, ydata) + return ln, + +ani = FuncAnimation(fig, update, frames=np.arange(0, 200), + init_func=init, blit=True) + + +plt.show() diff --git a/exercises/eigen/sparse.cpp b/exercises/eigen/sparse.cpp index 66cded5..da6f7d9 100644 --- a/exercises/eigen/sparse.cpp +++ b/exercises/eigen/sparse.cpp @@ -1,42 +1,50 @@ #include #include +#include #include +#include int main() { - int n = 10; + int n = 20; + int steps = 200; - Eigen::SparseMatrix A; - A.resize(n, n); + Eigen::SparseMatrix A; + A.resize(n, n); - double delta = 0.4; + double delta = 0.4; - std::vector> fill; - fill.reserve(n * 4); + std::vector> fill; + fill.reserve(n * 4); - for (int i = 0; i < n - 1; ++i) - { - fill.push_back(Eigen::Triplet(i + 1, i, delta)); - fill.push_back(Eigen::Triplet(i + 1, i + 1, -delta)); - fill.push_back(Eigen::Triplet(i, i, 1.0 - delta)); - fill.push_back(Eigen::Triplet(i, i + 1, delta)); - } - fill.push_back(Eigen::Triplet(n - 1, n - 1, 1.0)); - A.setFromTriplets(fill.begin(), fill.end()); + for (int i = 0; i < n - 1; ++i) + { + fill.push_back(Eigen::Triplet(i + 1, i, delta)); + fill.push_back(Eigen::Triplet(i + 1, i + 1, -delta)); + fill.push_back(Eigen::Triplet(i, i, 1.0 - delta)); + fill.push_back(Eigen::Triplet(i, i + 1, delta)); + } + fill.push_back(Eigen::Triplet(n - 1, n - 1, 1.0)); + A.setFromTriplets(fill.begin(), fill.end()); - std::cout << A << std::endl; + std::cout << A << std::endl; - Eigen::VectorXd b(n); - b.head(n / 2).array() = 1.0; - b.tail(n / 2).array() = 0.0; + Eigen::VectorXd b(n); + b.head(n / 2).array() = 1.0; + b.tail(n / 2).array() = 0.0; - std::cout << b.transpose() << std::endl; + std::cout << b.transpose() << std::endl; - for (int i = 0; i < 100; ++i) - b = A * b; + std::ofstream f; + f.open("sparse_sim.txt"); + for (int i = 0; i < steps; ++i) + { + f << b.transpose() << std::endl; + b = A * b; + } - std::cout << b.transpose() << std::endl; + std::cout << b.transpose() << std::endl; - return 0; -} + return 0; +} \ No newline at end of file diff --git a/lectures/eigen/README.md b/lectures/eigen/README.md index c6fe6dd..ecc1039 100644 --- a/lectures/eigen/README.md +++ b/lectures/eigen/README.md @@ -1,23 +1,111 @@ template: titleslide -# Using Eigen -## Chris Richardson, Rupert Nash -## chris@bpi.cam.ac.uk, r.nash@epcc.ed.ac.uk -## CC-BY +# Linear Algebra for C++ (using Eigen) +## Joseph Lee, EPCC +## j.lee@epcc.ed.ac.uk --- -# What is Eigen3 and why use it? -- C++ library for matrix arithmetic -- “Header only” implementation: no libraries to compile and install (easy) -- Provides some “missing” features needed for scientific computing in C++ -- Contains optimisations to get good performance out of ARM & Intel processors -- Easy to use interface -- Support for dense and sparse matrices, vectors and “arrays” -- Some support for ‘solvers’ (A.x = b) -- Download from https://eigen.tuxfamily.org or e.g. `apt install libeigen3-dev` -- If you know Python, it is a bit like a NumPy for C++ +# Source +Original: +- Chris Richardson (chris@bpi.cam.ac.uk) +- Rupert Nash (r.nash@epcc.ed.ac.uk) --- -# Basics + +# Rundown + +- C++ Linear Algebra libraries: what and why? + +- Eigen3: + + - Getting started + + - Matrices: Dense, Sparse, Geometry + + - Solvers: Dense, Sparse, Iterative + + - General notes + + - Diffusion equation demo + exercise + + +--- + +# Linear Algebra: What do we need? + +__What for?__ + +- Scientific computing, PDE, simulations, finance, graphics, ML and more + +__What we expect?__ + +Numeric types: `complex` `complex` + +Object types: + +- Vectors + +- Matrix: (Dense / Sparse / Diagonal / Symmetric / Column/Row major) + +- Operations: Matrix (Multiplication/transpose/geometric rotation) + +- Solvers (`\(Ax = b\)`, Eigenvalues) : Direct (LU/QR factorization) / Indirect (CG) + +--- + +# Why use a library? + +- Simple & easy to use : clean API + +- Fast Performance + +- Correctness + +- Expressiveness + +- Don’t reinvent the wheel + +--- + +# Linear Algebra on C++ + +- Simple: **Eigen** + +- Big & popular: **PETSc** (C) + +- Standard: **LAPACK/LAPACK++/ScaLAPACK** + +- GPU: Nvidia/AMD **(cu/roc)(BLAS/SPARSE/SOLVER)** + +- CPU: **Intel MKL**, **OpenBLAS** + +- Future: C++26 std library? + +- vs: Python Numpy/Scipy + +- +- +--- + +# Eigen3 + +## Numpy, but C++ + +“Header only” no library to link + - `g++ -I /path/to/eigen/ my_program.cpp -o my_program` + +Contains optimisation for ARM & Intel processors (Vectorization - SIMD instructions) + +Easy to use interface + +Support for dense and sparse matrices, vectors, and “arrays” (coefficient-wise ops) + +Support for some solvers + +Download from or e.g. apt install libeigen3-dev + +--- + +# Matrix: Basic ```C++ #include @@ -51,18 +139,26 @@ std::cout << A.rows() << “ x ” << A.cols() << std::endl; So this is more like a 2D version of `std::vector` --- -# Convenience aliases +# Convenience typedefs ```C++ -using Eigen::Matrix3d = Eigen::Matrix -using Eigen::Matrix3i = Eigen::Matrix -using Eigen::MatrixXd = Eigen::Matrix -using Eigen::VectorXd = Eigen::Matrix -using Eigen::RowVectorXd = Eigen::Matrix - +MatrixNt = Matrix +MatrixXNt = Matrix +MatrixNXt = Matrix +VectorNt = Matrix +RowVectorNt = Matrix ``` +``` +N = {2, 3, 4, X = dynamic} +t = {i = int, f = float, d = double, cf = complex, cd = complex} +``` +e.g.: +- `Matrix3d` = 3x3 double matrix +- `Matrix3i` = 3x3 int matrix +- `MatrixXd` = (Dynamic)x(Dynamic) double matrix +- `VectorXd` = (Dynamic) double vector --- -# You can do Matrix arithmetic... +# Matrix arithmetics ```C++ Eigen::MatrixXd A(5, 10); @@ -73,7 +169,16 @@ Eigen::MatrixXd C = A * B; Eigen::VectorXd w = A * vec; ``` -Also dot and cross products for vectors, transpose, and usual scalar arithmetic `+ - * /` +More: + +Dot product: `v.dot(w)` + +Cross product: `v.cross(w)` + +Transpose: `A.transpose()` + +Set constant: +`A.setZeros(rows, cols)`, `A.setOnes(rows, cols)`, `A.setConstant(rows, cols, value)`, `A.setRandom(rows, cols)` --- # Element-wise ops with `Array`s @@ -107,16 +212,147 @@ a_eigen(10, 0) = 1.0; Eigen::Map a2_eigen(a.data(), 10, 100); ``` +--- +# Sparse matrix: Basic + +When dealing with very large matrices with many zeros (e.g. from Differential Equations), store as sparse matrices + +```C++ +#include + +SparseMatrix > mat(1000,2000); +// declares a 1000x2000 column-major compressed sparse matrix of complex + +SparseMatrix mat(1000,2000); +// declares a 1000x2000 row-major compressed sparse matrix of double + +SparseVector > vec(1000); +// declares a column sparse vector of complex of size 1000 + +SparseVector vec(1000); +// declares a row sparse vector of double of size 1000 +``` + +--- +# Sparse matrix +Simplest way to create a sparse matrix is to build a triplet: +```C++ +typedef Triplet T; +std::vector tripletList; +tripletList.reserve(estimation_of_entries); +for(...) +{ +// ... +tripletList.push_back(T(i,j,v_ij)); +} +SparseMatrixType m(rows,cols); +m.setFromTriplets(tripletList.begin(), tripletList.end()); +// m is ready to go! +``` +Operators: +- Sparse: `SM.transpose()`, `SM.adjoint()`… +- Sparse-Sparse: `SM1 + SM2`, `SM1 *SM2` … +- Sparse-Dense: `DM2 = DM1 + SM1` , `DM2 = SM1* DM2` + +--- +# Matrix: Geometry + +```C++ +#include +``` +- 2D Rotations: +```C++ + Rotation2D rot2(angle_in_radian) + ``` +- 3D Rotaions: +```C++ +AngleAxis AA(angle_in_radian, Vector3f(ax,ay,az)) +``` +- Quarternion, ND-Transformations… + +--- +# Solvers + +Simple example: `\(Ax = b\)` +```C++ +#include +#include + +int main() +{ + Eigen::Matrix3f A; + Eigen::Vector3f b; + A << 1,2,3, 4,5,6, 7,8,10; + b << 3, 3, 4; + std::cout << "Here is the matrix A:\n" << A << std::endl; + std::cout << "Here is the vector b:\n" << b << std::endl; + Eigen::Vector3f x = A.colPivHouseholderQr().solve(b); #HERE + std::cout << "The solution is:\n" << x << std::endl; +} +``` +2 steps: +- Decompose +- Solve + +--- +# Decompositions: + +E.g. +- PartialPivLU + +- FullPivLU + +- HouseholderQR + +- ColPivHousehoulderQR + +Varying matrix requirements, speed, reliability, accuracy, optimization + + + + +--- +# Other solvers +### Singular values +E.g. JacobiSVD, BDCSVD + +### Eigenvalues/vectors +E.g. SelfAdjointEigenSolver, ComplexEigenSolver + +### Sparse Solver +E.g. SparseLU, SparseQR, SimplicialLLT, SimplicialLDLT + +--- + +# Iterative Solvers + +```C++ +#include +``` +Useful for solving `\(Ax = b\)` where `\(A\)` is large and sparse + +E.g. +- ConjugateGradient + +- LeastSquaresConjugateGradient + +- BICGSTAB + +Use with preconditioner --- -# Efficiency: Eigen does lots of checks in debug mode +# General Notes: +Eigen does a lot of checks in debug mode: + +- Turn optimisation on: `-O2` etc -Turn optimisation on: `-O2` etc. +- Turn off debug: `-DNDEBUG` -Turn off debug: `-DNDEBUG` +Can use external BLAS/LAPACK library (e.g. MKL, OpenBLAS) by enabling macro +- e.g. `EIGEN_USE_BLAS` --- -# Walk through example +# Demo: diffusion equation Solve diffusion equation @@ -124,16 +360,16 @@ $$\frac{\partial T}{\partial t} = k \frac{\partial^2 T}{\partial x^2}$$ in 1D using an explicit method -Each timestep can be solved by using `\(T_{n+1} = A T_n\)` +Each timestep can be iterated by using `\(T_{n+1} = A T_n\)` -1. Create an initial vector for `T` -2. Create a dense matrix for `A` -3. Matrix multiply several times -- Convert to an implicit method: `\(A.T_{n+1} = T_n\)` -- Sparse matrices +1. Create an initial vector for `\(T\)` + +2. Create a dense matrix for `\(A\)` + +3. Matrix multiply `\(A\)` several times -Type along with me - see `exercises/eigen` +See `exercises/eigen/explicit.cpp` --- # Diffusion equation (explicit) @@ -150,9 +386,6 @@ Left-hand side is unknown (next time step) Let: `\(\delta = k\Delta t/ \Delta x^2\)` ---- -# Matrix - ``` A = [ 1-ẟ ẟ 0 0 0 … ] [ ẟ 1-2ẟ ẟ 0 0 … ] @@ -161,9 +394,9 @@ A = [ 1-ẟ ẟ 0 0 0 … ] [ 0 0 0 ẟ 1-2ẟ ẟ 0 …] ``` - --- # Diffusion equation (implicit) +More stable: Subscript means time, square brackets position @@ -172,7 +405,37 @@ Subscript means time, square brackets position Left-hand side is unknown (next time step) `$$A.T_{n+1} = T_n$$` -Let: $$\delta = k \Delta t/\Delta x^2$$ - -??? +``` +A = [ 1+ẟ -ẟ 0 0 0 …] + [ -ẟ 1+2ẟ -ẟ 0 0 …] + [ 0 -ẟ 1+2ẟ -ẟ 0 …] + [ 0 0 -ẟ 1+2ẟ -ẟ …] + [ 0 0 0 -ẟ 1+2ẟ …] + +``` The matrix A is very similar - just flip the sign of the delta terms + +--- +# Exercise: Diffusion equation (sparse) + +Hints: + +```C++ +#include +``` +```C++ +std::vector> fill; + fill.reserve(...); +``` +```C++ +for (int i = 0; i < n - 1; ++i) + { + fill.push_back(Eigen::Triplet(...); + ... + } +``` +```C++ +A.setFromTriplets(fill.begin(), fill.end()); +``` + +See `exercises/eigen/sparse.cpp` \ No newline at end of file diff --git a/lectures/eigen/index.html b/lectures/eigen/index.html index 0b675fd..d5bd99d 100644 --- a/lectures/eigen/index.html +++ b/lectures/eigen/index.html @@ -14,7 +14,7 @@ - - - - - - - - - - - diff --git a/lectures/frameworks-kokkos/lammps_lj.png b/lectures/frameworks-kokkos/lammps_lj.png deleted file mode 100644 index 949bdc292df02b80c25a30a4a8cc7773fec019c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4232 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 y61IzCtZTR9)w^EBAt->-fIcm&~B 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 z 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 XDMY^Jhlr0H?91Y+jcN52kB9#URHyd6 From 9c79e54510517d5a8cb633d4321b2a020f54f625 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 19 Feb 2024 16:51:43 +0000 Subject: [PATCH 10/91] Edits to remove portable frameworks section. --- exercises/README.md | 1 - lectures/README.md | 1 - 2 files changed, 2 deletions(-) diff --git a/exercises/README.md b/exercises/README.md index 3961f8f..2da6e5f 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -10,4 +10,3 @@ See each subdirectory for further instructions * [Using algorithms](algorithm/) * [Eigen](eigen/) * [Simple use of threads](threads/) -* [Portable performance with Kokkos](kokkos/) diff --git a/lectures/README.md b/lectures/README.md index cf76c43..8e80894 100644 --- a/lectures/README.md +++ b/lectures/README.md @@ -8,4 +8,3 @@ * [Algorithms, lambdas and traits](algorithms-lambdas-traits) * [Linear algebra with Eigen](eigen) * [Threads with C++](threads-1) and [Further topics with threads](threads-2) -* [C++ frameworks for portable performance](frameworks-kokkos) From a1bc52196a738c3fd784b1d010be9d4821c9ec45 Mon Sep 17 00:00:00 2001 From: Maurice Jamieson Date: Sun, 25 Feb 2024 17:33:46 +0000 Subject: [PATCH 11/91] Updated C++23 draft version, ARCHER2 default GCC version --- lectures/cpp-intro/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index a3b8c18..c124b70 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -126,7 +126,7 @@ Every three years there is a new update to the International Standard Latest one, C++20, still not fully implemented by an compiler. Major new features are ranges, coroutines, concepts, and modules -C++23 is still in draft (N4944). Major features likely to include +C++23 is still in draft (N4950). Major features likely to include networking, string formatting, executors, and consolidation of new C++20 features @@ -253,12 +253,12 @@ __ARCHER2__: You have log in details. Once connected you need to load the up-to-date compilers: ``` -module load gcc/10.2.0 +module load gcc/11.2.0 ``` ??? -There are later versions of GCC on ARCHER2 but 10.2.0 is the default version +There are later versions of GCC on ARCHER2 but 11.2.0 is the default version --- # Getting the source code @@ -408,7 +408,7 @@ Go look them up on CPP Reference as you need to # Strings -The standard library has a class* called `string` that holds a string +The standard library has a class called `string` that holds a string of text characters. You have to `#include ` to use it which includes the "header From 2e182069bbcae5d6448936fe92a153e301dae6be Mon Sep 17 00:00:00 2001 From: Maurice Jamieson Date: Sun, 25 Feb 2024 20:39:51 +0000 Subject: [PATCH 12/91] Minor updates to formatting --- lectures/classes/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lectures/classes/README.md b/lectures/classes/README.md index feeb6ae..30980ef 100644 --- a/lectures/classes/README.md +++ b/lectures/classes/README.md @@ -249,7 +249,7 @@ Note that an object in C++ may not be an object in the OOP sense! --- # Member functions -Typically these are declared in the class definition... +Typically these are *declared* in the class definition... ```C++ // complex.hpp @@ -269,7 +269,7 @@ If anyone asks, discussion of const is coming up! --- # Member functions -... and defined out of line +... and *defined* out of line ```C++ // complex.cpp From e8674193fe1aade2d05238c2169ceb0b7501657f Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 8 Apr 2024 18:27:01 +0100 Subject: [PATCH 13/91] test --- lectures/cpp-intro/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index a3b8c18..856dbe7 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -1,7 +1,7 @@ template: titleslide # A brief introduction to C++ -## Maurice Jamieson, EPCC +## James Maurice Jamieson, EPCC ## m.jamieson@epcc.ed.ac.uk --- From 7424eeffa055e92490fc87f76c04fe8d5d345cf4 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 8 Apr 2024 22:13:00 +0100 Subject: [PATCH 14/91] edits to formatting a examples --- exercises/complex/README.md | 15 +++++++++ exercises/eigen/modules.sh | 10 +++--- exercises/morton-order/instructions.md | 32 ++++++++++++-------- lectures/algorithms-lambdas-traits/README.md | 6 ++-- lectures/classes/README.md | 4 +-- lectures/cpp-intro/README.md | 6 ++-- lectures/eigen/README.md | 29 +++++++++++++++--- lectures/loops-containers/README.md | 6 ++-- lectures/resources/README.md | 7 +++++ lectures/templates/README.md | 4 +-- lectures/threads-1/README.md | 4 +++ 11 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 exercises/complex/README.md diff --git a/exercises/complex/README.md b/exercises/complex/README.md new file mode 100644 index 0000000..6aa5885 --- /dev/null +++ b/exercises/complex/README.md @@ -0,0 +1,15 @@ +In your clone of this repository, find the `complex` exercise and list the files + +``` +$ cd archer2-cpp/exercises/complex$ ls Makefile complex.cpp complex.hpp test.cpp +``` + +The files `complex.hpp` and `complex.cpp` contain a partially working complex number class and `test.cpp` holds some basic unit tests. + +You can compile and run with: + +``` +$ make && ./testc++ --std=c++14 -I../include -c -o complex.o complex.cppc++ --std=c++14 -I../include -c -o test.o test.cppc++ complex.o test.o -o test===============================================================================All tests passed (34 assertions in 6 test cases) +``` + +But to get to this point you need to complete the code and fix a few bugs! diff --git a/exercises/eigen/modules.sh b/exercises/eigen/modules.sh index a9262e9..dcc0da0 100644 --- a/exercises/eigen/modules.sh +++ b/exercises/eigen/modules.sh @@ -1,7 +1,5 @@ #!/bin/bash -module purge -module load spack -module load eigen-3.3.7-gcc-6.3.0-qka5wf6 -module load clang -module load gnuplot/5.0.5-x11 -module load intel-compilers-18 +module load PrgEnv-gnu +module load cray-python +module load eigen/3.4.0 + diff --git a/exercises/morton-order/instructions.md b/exercises/morton-order/instructions.md index 07014ca..05fbeec 100644 --- a/exercises/morton-order/instructions.md +++ b/exercises/morton-order/instructions.md @@ -1,11 +1,15 @@ + # Morton order matrices in C++ -## Rupert Nash -## r.nash@epcc.ed.ac.uk +## James Richings +## j.richings@epcc.ed.ac.uk + + +# Sample code Source for this can be obtained from Github. Get a new copy with: ``` -git clone https://github.com/EPCCed/archer2-CPP-2021-07-20 +git clone https://github.com/EPCCed/archer2-cpp ``` or update your existing one with @@ -17,15 +21,17 @@ git pull then you can ``` -cd archer2-CPP-2021-07-20/exercises/morton-order +cd archer2-cpp/exercises/morton-order ``` +# Morton ordering + The Morton ordering (or z-ordering) of a matrix lays out the elements along a recursive z-shaped curve, as shown in the figure of four iterations of the Z-order curve (from -[Wikipedia](https://en.wikipedia.org/wiki/Z-order_curve)). +[z ordered curve](https://en.wikipedia.org/wiki/Z-order_curve)). -![Morton order](mortonorder.png) +# Indices You can compute the Morton index `z` from the x- and y-indices (`i` and `j` respectively) by interleaving their bits. An example is shown @@ -33,10 +39,10 @@ in the table. | | 0 | 1 | 2 | 3 | |----|----|----|----|----| -| 0 | 0| 1| 4| 5| -| 1 | 2| 3| 6| 7| -| 2 | 8| 9| 12| 13| -| 3 | 10| 11| 14| 15| +| 0 | 0| 1| 4| 5 | +| 1 | 2| 3| 6| 7| +| 2 | 8| 9| 12| 13| +| 3 | 10| 11| 14| 15| Mapping between `x-y` indexes and Morton index for a 4 by 4 matrix. Decimal on the left and binary on the right. @@ -51,6 +57,8 @@ matrix. Decimal on the left and binary on the right. Mapping between `x-y` indexes and Morton index for a matrix of size 4-by-4. Decimal on the left and binary on the right. + + The advantage of laying out data in this way is that it improves data locality (and hence cache use) without having to tune a block size or similar parameter. On a modern multilevel cache machine[^1], this @@ -76,7 +84,7 @@ number. Go to the step 1 directory: ```bash -cd archer2-CPP-2021-07-20/exercises/morton-order/step1 +cd archer2-cpp/exercises/morton-order/step1 ``` Using the partial implemenation in `matrix.hpp`, your task is to @@ -95,7 +103,7 @@ supplied `Makefile` should work. Go to the step 2 directory: ```bash -cd archer2-CPP-2021-07-20/exercises/morton-order/step2 +cd archer2-cpp/exercises/morton-order/step2 ``` I have a potential solution to part 1 here, but feel free to copy your diff --git a/lectures/algorithms-lambdas-traits/README.md b/lectures/algorithms-lambdas-traits/README.md index 24772fb..4d367cc 100644 --- a/lectures/algorithms-lambdas-traits/README.md +++ b/lectures/algorithms-lambdas-traits/README.md @@ -1,8 +1,8 @@ template: titleslide # Algorithms, lambdas, traits -## Rupert Nash -## r.nash@epcc.ed.ac.uk +## James Richings +## j.richings@epcc.ed.ac.uk --- @@ -534,7 +534,7 @@ void Comm::send(const std::vector& data, int dest, int tag) { # Real example -Then we can then provide a specialised defintion for all the types we +Then we can then provide a specialised definition for all the types we can handle: ```C++ diff --git a/lectures/classes/README.md b/lectures/classes/README.md index feeb6ae..0fd0d61 100644 --- a/lectures/classes/README.md +++ b/lectures/classes/README.md @@ -1,7 +1,7 @@ template: titleslide # Classes -## Maurice Jamieson, EPCC -## m.jamieson@epcc.ed.ac.uk +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk --- diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index 856dbe7..3a43988 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -1,8 +1,8 @@ template: titleslide # A brief introduction to C++ -## James Maurice Jamieson, EPCC -## m.jamieson@epcc.ed.ac.uk +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk --- @@ -243,7 +243,7 @@ template: titleslide --- # Machine choice -You can use your laptop or Cirrus +You can use your laptop or ARCHER2 __Your machine__ : you need a C++ compiler that supports at least C++11. If you use Windows and MSVC we may not be able to help diff --git a/lectures/eigen/README.md b/lectures/eigen/README.md index 9ec1dc1..f2cb0c0 100644 --- a/lectures/eigen/README.md +++ b/lectures/eigen/README.md @@ -1,7 +1,7 @@ template: titleslide # Linear Algebra for C++ (using Eigen) -## Joseph Lee, EPCC -## j.lee@epcc.ed.ac.uk +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk --- # Source @@ -9,6 +9,7 @@ template: titleslide Original: - Chris Richardson (chris@bpi.cam.ac.uk) - Rupert Nash (r.nash@epcc.ed.ac.uk) +- Joseph Lee (j.lee@epcc.ed.ac.uk) --- # Rundown @@ -448,8 +449,9 @@ The matrix A is very similar - just flip the sign of the delta terms --- # Exercise: Diffusion equation (sparse) -Hints: +There is also a way to implement this example using the sparse matrix interface in eigen. +Some changes to look out for in the `sparse.cpp` example: ```C++ #include ``` @@ -468,4 +470,23 @@ for (int i = 0; i < n - 1; ++i) A.setFromTriplets(fill.begin(), fill.end()); ``` -See `exercises/eigen/sparse.cpp` \ No newline at end of file +See `exercises/eigen/sparse.cpp` + +--- +# Exercise: Diffusion equation 3 ways + +- Use `modules.sh` to load correct environment on ARCHER2 + +- Compile the examples using `make` + +- Run each of the three examples explicit, implicit and sparse + +- Generate the movie using the provided python script + +To view the movie you will need to either: + - Download the data and generate it locally + + or + + - Set up a python virtual environment on ARCHER2 with matplotlib + - User `ssh -X` to view graphics \ No newline at end of file diff --git a/lectures/loops-containers/README.md b/lectures/loops-containers/README.md index 978b0d2..f115c7a 100644 --- a/lectures/loops-containers/README.md +++ b/lectures/loops-containers/README.md @@ -1,7 +1,7 @@ template: titleslide # Containers, loops, and iterators -## Rupert Nash -## r.nash@epcc.ed.ac.uk +## James Richings +## j.richings@epcc.ed.ac.uk ??? @@ -464,7 +464,7 @@ In your clone of this repository, find the `containers` exercise and list the files ``` -$ cd archer2-CPP-2021-07-20/exercises/containers +$ cd archer2-cpp/exercises/containers $ ls Makefile test.cpp vector_ex.cpp vector_ex.hpp ``` diff --git a/lectures/resources/README.md b/lectures/resources/README.md index ea34ebf..8f8f795 100644 --- a/lectures/resources/README.md +++ b/lectures/resources/README.md @@ -1,5 +1,8 @@ template: titleslide + # Resource management +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk --- # Resources @@ -779,3 +782,7 @@ etc wrapped here. Try out some of this with exercises/morton-order +Instructions can be found here: + +archer2-cpp/exercises/morton-order/instructions.md + diff --git a/lectures/templates/README.md b/lectures/templates/README.md index aeb4441..0680b47 100644 --- a/lectures/templates/README.md +++ b/lectures/templates/README.md @@ -1,7 +1,7 @@ template: titleslide # Templates -## Rupert Nash -## r.nash@epcc.ed.ac.uk +## James Richings +## j.richings@epcc.ed.ac.uk ??? diff --git a/lectures/threads-1/README.md b/lectures/threads-1/README.md index e1ee954..0ce71d5 100644 --- a/lectures/threads-1/README.md +++ b/lectures/threads-1/README.md @@ -1,7 +1,11 @@ template: titleslide + # C++ Threads - Basics +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk --- + # Overview - Introduction From f27dfbb74f9688ca2f3c9f99861dd96e3ddf4c17 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Tue, 9 Apr 2024 13:54:34 +0100 Subject: [PATCH 15/91] update exercise path --- exercises/threads/instructions.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/exercises/threads/instructions.md b/exercises/threads/instructions.md index ff36f3d..0f8d185 100644 --- a/exercises/threads/instructions.md +++ b/exercises/threads/instructions.md @@ -5,7 +5,7 @@ Source for this can be obtained from Github. Get a new copy with: ``` -git clone https://github.com/EPCCed/archer2-CPP-2021-07-20 +git clone https://github.com/EPCCed/archer2-cpp ``` or update your existing one with @@ -17,11 +17,9 @@ git pull then you can ``` -cd archer2-CPP-2021-07-20/exercises/threads +cd archer2-cpp/exercises/threads ``` - - `area.cpp` contains a C++ version of the Mandelbrot example which you used in Threaded Programming. Parallelise the outer loop of the main computation using C++ From 468ff0247115fcebcd64cc007e95005cb6be236c Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Fri, 11 Oct 2024 15:01:18 +0100 Subject: [PATCH 16/91] Update intro chapter to use new module structure --- lectures/cpp-intro/index.html | 13 +------------ lectures/template/cpptheme.js | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lectures/cpp-intro/index.html b/lectures/cpp-intro/index.html index 3b8bc49..0ac278f 100644 --- a/lectures/cpp-intro/index.html +++ b/lectures/cpp-intro/index.html @@ -5,18 +5,7 @@ - - + - - - - - - \ No newline at end of file diff --git a/lectures/template/cpptheme.js b/lectures/template/cpptheme.js index a181302..b582b3d 100644 --- a/lectures/template/cpptheme.js +++ b/lectures/template/cpptheme.js @@ -1,10 +1,16 @@ +import {epcc, Theme} from "https://EPCCed.github.io/remark_theme/latest.js"; epcc.footer_text = "© Rupert Nash, The University of Edinburgh, CC-BY"; -cpptheme = new Theme( - (str => str.substring(0, str.lastIndexOf("/")))(document.currentScript.src), +var cpptheme = new Theme( + (str => str.substring(0, str.lastIndexOf("/")))(import.meta.url), '$BASEURL/style.css', { - thumb: function () { - return '.thumb[\n.thumbtxt[\n' + this +'\n]\n]'; - } - }); + thumb: function () { + return '.thumb[\n.thumbtxt[\n' + this +'\n]\n]'; + } + } +); + +epcc.install(); +cpptheme.install(); +globalThis.slideshow = remark.create({sourceUrl: 'README.md'}); From 285a27bb9cbd5164fb5effc18bd1b32eb1f16969 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Tue, 15 Oct 2024 11:57:22 +0100 Subject: [PATCH 17/91] Allow default footer text to be overridden --- lectures/template/cpptheme.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lectures/template/cpptheme.js b/lectures/template/cpptheme.js index b582b3d..6594a71 100644 --- a/lectures/template/cpptheme.js +++ b/lectures/template/cpptheme.js @@ -1,6 +1,12 @@ import {epcc, Theme} from "https://EPCCed.github.io/remark_theme/latest.js"; -epcc.footer_text = "© Rupert Nash, The University of Edinburgh, CC-BY"; +var footer = new URL(import.meta.url).searchParams.get("footer"); + +if (!footer) { + footer = "© Rupert Nash, The University of Edinburgh, CC-BY"; +} +epcc.footer_text = footer; + var cpptheme = new Theme( (str => str.substring(0, str.lastIndexOf("/")))(import.meta.url), '$BASEURL/style.css', From cd26aeef1b2830986291355f49e4bebbc2ddccc2 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Tue, 15 Oct 2024 11:58:39 +0100 Subject: [PATCH 18/91] Update remaining chapters for the new remark theme --- lectures/algorithms-lambdas-traits/index.html | 24 +++-------------- lectures/classes/index.html | 24 +++-------------- lectures/eigen/index.html | 27 +++++-------------- lectures/loops-containers/index.html | 15 ++--------- lectures/resources/index.html | 15 ++--------- lectures/template/mathjax-setup.js | 8 ++++++ lectures/templates/index.html | 13 +-------- lectures/threads-1/index.html | 16 +++-------- lectures/threads-2/index.html | 16 +++-------- 9 files changed, 32 insertions(+), 126 deletions(-) create mode 100644 lectures/template/mathjax-setup.js diff --git a/lectures/algorithms-lambdas-traits/index.html b/lectures/algorithms-lambdas-traits/index.html index b2fe5d7..868058d 100644 --- a/lectures/algorithms-lambdas-traits/index.html +++ b/lectures/algorithms-lambdas-traits/index.html @@ -1,31 +1,13 @@ -C++ for numerical computing part 2 +Algorithms, lambdas, traits - - + + - - - - - - diff --git a/lectures/classes/index.html b/lectures/classes/index.html index 32819ae..a112137 100644 --- a/lectures/classes/index.html +++ b/lectures/classes/index.html @@ -5,27 +5,9 @@ - - - + + + - - - - - - \ No newline at end of file diff --git a/lectures/eigen/index.html b/lectures/eigen/index.html index 8d0e7f5..b2e2432 100644 --- a/lectures/eigen/index.html +++ b/lectures/eigen/index.html @@ -1,30 +1,17 @@ -A brief introduction to C++ +Linear Algebra for C++ - - - - - - + + + + + - + - diff --git a/lectures/loops-containers/index.html b/lectures/loops-containers/index.html index bdd2a97..6988ae3 100644 --- a/lectures/loops-containers/index.html +++ b/lectures/loops-containers/index.html @@ -1,22 +1,11 @@ -C++ for numerical computing +Containers, loops, and iterators - - + - - - - - - diff --git a/lectures/resources/index.html b/lectures/resources/index.html index 3b8bc49..bdcc5ee 100644 --- a/lectures/resources/index.html +++ b/lectures/resources/index.html @@ -1,22 +1,11 @@ -A brief introduction to C++ +Resource Management - - + - - - - - - \ No newline at end of file diff --git a/lectures/template/mathjax-setup.js b/lectures/template/mathjax-setup.js new file mode 100644 index 0000000..1421317 --- /dev/null +++ b/lectures/template/mathjax-setup.js @@ -0,0 +1,8 @@ +// Setup MathJax +MathJax.Hub.Config({ + tex2jax: { + skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'] + } +}); + +MathJax.Hub.Configured(); \ No newline at end of file diff --git a/lectures/templates/index.html b/lectures/templates/index.html index f14fe01..30abb8a 100644 --- a/lectures/templates/index.html +++ b/lectures/templates/index.html @@ -5,18 +5,7 @@ - - + - - - - - - \ No newline at end of file diff --git a/lectures/threads-1/index.html b/lectures/threads-1/index.html index b895d67..ce289e1 100644 --- a/lectures/threads-1/index.html +++ b/lectures/threads-1/index.html @@ -5,19 +5,9 @@ - - - - - - -var slideshow = remark.create({sourceUrl: 'README.md'}); - - - + \ No newline at end of file diff --git a/lectures/threads-2/index.html b/lectures/threads-2/index.html index f149417..e41742d 100644 --- a/lectures/threads-2/index.html +++ b/lectures/threads-2/index.html @@ -5,19 +5,9 @@ - - - - - - -var slideshow = remark.create({sourceUrl: 'README.md'}); - - - + \ No newline at end of file From 39dcd360d8dca1711367b78b7e00c3dd274960c3 Mon Sep 17 00:00:00 2001 From: nmannall Date: Fri, 18 Oct 2024 12:22:09 +0100 Subject: [PATCH 19/91] Update cpp intro slides Replace deprecated main(void) with main() Update info on supported C++ standards --- lectures/cpp-intro/README.md | 44 ++++++++++++++++-------------- lectures/cpp-intro/hello/hello.cpp | 2 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index cbc8ca4..43fa197 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -1,8 +1,8 @@ template: titleslide # A brief introduction to C++ -## James Richings, EPCC -## j.richings@epcc.ed.ac.uk +## Nathan Mannall, EPCC +## n.mannall@epcc.ed.ac.uk --- @@ -123,10 +123,10 @@ C++ is a work in progress. Every three years there is a new update to the International Standard -Latest one, C++20, still not fully implemented by an compiler. Major +C++20 is only fully supported by MSVC (although GCC is nearly there). Major new features are ranges, coroutines, concepts, and modules -C++23 is still in draft (N4950). Major features likely to include +Latest one, C++23 has been released. Major features include networking, string formatting, executors, and consolidation of new C++20 features @@ -136,7 +136,7 @@ C++20 features (2nd Ed.). Assumes very little but it's long - Bjarne Stroustrup, "A Tour of C++". Assumes you're an experience - programmer and is quite brief - recently updated for C++17 + programmer and is quite brief - targets C++17 - Best online *reference* is (comes in other human languages too!) @@ -164,7 +164,7 @@ name: hello ```C++ #include -int main(void) { +int main() { std::cout << "Hello, world!" << std::endl; return 0; } @@ -174,7 +174,7 @@ int main(void) { template: hello ``` -$ g++ --std=c++11 hello.cpp -o hello +$ g++ --std=c++17 hello.cpp -o hello $ ./hello Hello, world! ``` @@ -201,7 +201,7 @@ template: hello - The `return 0` statement indicates to the OS that no error occured. -- (You can also get the command line arguments but `void` here means we +- (You can also get the command line arguments but the empty brackets here means we aren't using them). ??? @@ -246,7 +246,7 @@ template: titleslide You can use your laptop or ARCHER2 __Your machine__ : you need a C++ compiler that supports at least -C++11. If you use Windows and MSVC we may not be able to help +C++11 (ideally C++17). If you use Windows and MSVC we may not be able to help much... Sorry! __ARCHER2__: You have log in details. @@ -292,7 +292,7 @@ emacs hello.cpp Compile the program: ``` -g++ --std=c++11 hello.cpp -o hello +g++ --std=c++17 hello.cpp -o hello ``` No output means success! @@ -385,9 +385,9 @@ template: titleslide | Type | Description | |-------------|-------------| -| `void` | Nothing - used to indicate a function takes and/or returns no value.| +| `void` | Nothing - used to indicate a function returns no value.| | `bool` | `true` or `false` | -| `int` | Standard *signed* integer for your machine. *At least* 16bits. *Usually* 32 bits.| +| `int` | Standard *signed* integer for your machine. *At least* 16 bits. *Usually* 32 bits.| | `double` | Double-precision floating point. *Usually* an IEEE 754 64 bit number.| | `std::byte` | Raw untyped memory | @@ -419,7 +419,7 @@ use it. #include #include -int main(void) { +int main() { std::string message = "Hello, world"; std::cout << message << std::endl; return 0; @@ -429,7 +429,7 @@ int main(void) { ??? Character encoding in the standard library is a bit of a mess. -Partially fixed in C++20 +Partially fixed in C++20 and further in C++23 Find a library e.g. Boost, if you need to do serious text handling because Unicode is super complicated @@ -445,7 +445,7 @@ A function encapsulates a piece of code between braces (curly brackets, `{}`) and gives it a name so you can use it later. ```C++ -void say_hello(void) { +void say_hello() { std::cout << "Hello, world!" << std::endl; } @@ -456,14 +456,15 @@ int main(int argc, char* argv[]) { ``` ??? -You declare function by first giving the return type (`void`) +You declare function by first giving the return type (`void`). This marks that +the function doesn't return a value. Then the name (`say_hello`) Then the list of zero or more parameters. If the parameter list is zero, -then you can use empty brackets but just as we would use 'void' to mark that -a function doesn't return a value, we can use 'void' within the brackets to -indicate that the function doesn't take parameters. +then you can use empty brackets. We can use 'void' within the brackets to +indicate that the function doesn't take parameters, however this is depricated +(but will compile for backwards compatability). --- # Functions @@ -502,7 +503,7 @@ To use a function, or "call" it, you give its name and then provide the arguments in parentheses ``` -int main (void) { +int main () { int x = 42; std::cout << "Total = " << sum(x, 100) << std::endl; return 0; @@ -511,7 +512,7 @@ int main (void) { The parameters to the function must match the declaration. -The `return 0` statement in the `main(void)` function is optional but you +The `return 0` statement in the `main()` function is optional but you have to have a `return` statement in all other functions that return a value. @@ -524,6 +525,7 @@ name** but **different arguments**. int sum(int a, int b) { return a + b; } + double sum(double a, double b) { return a + b; } diff --git a/lectures/cpp-intro/hello/hello.cpp b/lectures/cpp-intro/hello/hello.cpp index 46c8be6..71d623b 100644 --- a/lectures/cpp-intro/hello/hello.cpp +++ b/lectures/cpp-intro/hello/hello.cpp @@ -1,6 +1,6 @@ #include int main(int argc, char* argv[]) { - std::cout << "Hello, world" << std::endl; + std::cout << "Hello, world!" << std::endl; return 0; } From 10aa7f7d219046590c190d59922cb7178214863e Mon Sep 17 00:00:00 2001 From: nmannall Date: Fri, 18 Oct 2024 14:48:53 +0100 Subject: [PATCH 20/91] Update classes chapter Remove void from function arguments Minor additions to presentation notes --- lectures/classes/README.md | 56 ++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/lectures/classes/README.md b/lectures/classes/README.md index 49c3cde..ca67cd7 100644 --- a/lectures/classes/README.md +++ b/lectures/classes/README.md @@ -1,7 +1,7 @@ template: titleslide # Classes -## James Richings, EPCC -## j.richings@epcc.ed.ac.uk +## Nathan Mannall, EPCC +## n.mannall@epcc.ed.ac.uk --- @@ -17,7 +17,7 @@ struct Complex { }; ``` -Let's show this on Compiler Explorer: https://godbolt.org/z/j8Kf31T89 +Let's show this on Compiler Explorer: https://godbolt.org/z/d3MfGhPz5 This style of type is often called a "plain old data" (POD) type or a "trivial" type. @@ -38,7 +38,7 @@ Creating trivial types - give the class name then list the values to be assigned members, _in order_, inside braces: ```C++ -Complex mk_imaginary_unit(void) { +Complex mk_imaginary_unit() { return Complex{0, 1}; } ``` @@ -46,7 +46,7 @@ This is called aggregate initialisation. Alternatively: ```C++ -Complex mk_imaginary_unit(void) { +Complex mk_imaginary_unit() { Complex sqrt_m1; // Values are uninitialised sqrt_m1.re = 0; sqrt_m1.im = 1; @@ -60,7 +60,7 @@ Complex mk_imaginary_unit(void) { Using trivial types: ```C++ -void test(void) { +void test() { Complex z = mk_imaginary_unit(); assert(z.re == 0); assert(z.im == 1); @@ -96,7 +96,7 @@ Now when you create an object it will be initialised to a known state for you: ```C++ -void test(void) { +void test() { Complex z; assert(z.re == 0); assert(z.im == 0); @@ -128,7 +128,7 @@ struct Complex { double im = 0.0; }; -Complex mk_imaginary_unit(void) { +Complex mk_imaginary_unit() { return Complex{0, 1}; } ``` @@ -185,20 +185,24 @@ the compiler must not create one unless asked to... Let's define the ones we declared just now: ```C++ -Complex::Complex(double real) : re(real) { +Complex::Complex(double real) : re{real} { } -Complex::Complex(double real, double imag) : re(real), im(imag) { +Complex::Complex(double real, double imag) : re{real}, im{imag} { } ``` ??? -Point out the member initialiser syntax after the colon +Point out the member initialiser syntax after the colon. Can use parenthesis +rather than curly brackets. Assert that this is where you want to do as much initialisation as you possibly can. +The members in a member initialiser list are always initialised in the order in +which they are defined inside the class (not in the order they are defined in the +member initializer list). --- # Creating complexes @@ -227,12 +231,10 @@ C++ > associated. In C++ these attached bits of code are known as member functions - -other languages might call them methods - -For example: +other languages might call them methods. For example: ```C++ -int main(void) { +int main() { std::string name; std::cin >> name; std::cout << "Hello, " << name << ". " @@ -255,7 +257,7 @@ Typically these are *declared* in the class definition... // complex.hpp struct Complex { // Constructors as before - double magnitude(void) const; + double magnitude() const; double re = 0.0; double im = 0.0; @@ -273,7 +275,7 @@ If anyone asks, discussion of const is coming up! ```C++ // complex.cpp -double Complex::magnitude(void) const { +double Complex::magnitude() const { return std::sqrt(re*re + im*im); } ``` @@ -389,7 +391,7 @@ elsewhere in the class scope ```C++ class Greeter { std::string greetee; - void say_hello(void) const { + void say_hello() const { std::cout << "Hello, " << greetee << std::endl; } }; @@ -410,7 +412,7 @@ Compiler will say something like class Greeter { std::string greetee; public: - void say_hello(void) const { + void say_hello() const { std::cout << "Hello, " << greetee << std::endl; } }; @@ -448,7 +450,7 @@ Variables can be qualified with the `const` keyword - compiler will give errors if you try ```C++ -int main(void) { +int main() { int const i = 42; std::cout << i << std::endl; // prints 42 @@ -508,7 +510,7 @@ If they do not need to change an instance, then they should be marked ```C++ struct Complex { - double magnitude(void) const; + double magnitude() const; }; ``` @@ -517,7 +519,9 @@ struct Complex { `this` pointer, if anyone asks, but we don't like pointers -Member variables can be marked `const`, but don't do it +Member variables can be marked `const`, but don't do it: +https://www.sandordargo.com/blog/2020/11/11/when-use-const-2-member-variables + --- # East-const vs West-const @@ -557,7 +561,7 @@ template: titleslide --- # Copying -By default, if assign a variable a new value, C++ will copy it +By default, if you assign a variable a new value, C++ will copy it ```C++ double copy = original; ``` @@ -597,7 +601,7 @@ void ScaleVector(double scale, std::vector& x) { // Multiply every element of x by scale } -void test(void) { +void test() { std::vector data = ReadLargeFile(); ScaleVector(-1.0, data); } @@ -650,12 +654,12 @@ class AtomList { std::vector velocity; std::vector charge; public: - std::vector const& GetCharge(void) const { + std::vector const& GetCharge() const { return charge; } }; -void analyse_md_data(void) { +void analyse_md_data() { AtomList const atoms = ReadFromFile(); std::vector const& charges = atoms.GetCharge(); std::cout << "Average charge = " From b7cbd6ca457b51b55d8be52283f6d3e7c272578d Mon Sep 17 00:00:00 2001 From: nmannall Date: Fri, 18 Oct 2024 16:42:03 +0100 Subject: [PATCH 21/91] Update containers and loops slides Change name Additional notes on != for iterators in for loops --- lectures/loops-containers/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lectures/loops-containers/README.md b/lectures/loops-containers/README.md index f115c7a..6f3b0ac 100644 --- a/lectures/loops-containers/README.md +++ b/lectures/loops-containers/README.md @@ -1,7 +1,7 @@ template: titleslide # Containers, loops, and iterators -## James Richings -## j.richings@epcc.ed.ac.uk +## Nathan Mannall +## n.mannall@epcc.ed.ac.uk ??? @@ -428,7 +428,10 @@ Discuss pre-increment (optimiser is not perfect) Discuss `operator*` -Discuss `operator==` +Discuss `operator==`. While `operator<` is prefered for numeric comparisons +in loop conditions, it is conventional to use `operator!=` to test if an +iterator has reached the end element. This is because some iterator types are +not relationally comparable, but `operator!=` works with all iterator types --- # Implementing your own iterator From de31cdd101c345d7d05ea46022bda900dba1055d Mon Sep 17 00:00:00 2001 From: nmannall Date: Thu, 24 Oct 2024 15:17:20 +0100 Subject: [PATCH 22/91] Update resources slides and RAII examples --- .gitignore | 1 + lectures/resources/README.md | 6 +++--- lectures/resources/sample/.gitignore | 4 ++++ lectures/resources/sample/Makefile | 4 +--- lectures/resources/sample/arr1.cpp | 3 +++ lectures/resources/sample/arr2.cpp | 17 ++++++++++++----- lectures/resources/sample/arr3.cpp | 18 +++++++++++++++--- 7 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 .gitignore create mode 100644 lectures/resources/sample/.gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1530978 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o \ No newline at end of file diff --git a/lectures/resources/README.md b/lectures/resources/README.md index 8f8f795..edf4862 100644 --- a/lectures/resources/README.md +++ b/lectures/resources/README.md @@ -1,8 +1,8 @@ template: titleslide # Resource management -## James Richings, EPCC -## j.richings@epcc.ed.ac.uk +## Nathan Mannall, EPCC +## n.mannall@epcc.ed.ac.uk --- # Resources @@ -245,7 +245,7 @@ Compile and run What happens if we copy x? -Add `auto x_cp = x;` +Add `auto x_cp = x;` (same as `auto x_cp = my_array{x};`) --- # Copying diff --git a/lectures/resources/sample/.gitignore b/lectures/resources/sample/.gitignore new file mode 100644 index 0000000..b54d0cc --- /dev/null +++ b/lectures/resources/sample/.gitignore @@ -0,0 +1,4 @@ +arr1 +arr2 +arr3 +shared diff --git a/lectures/resources/sample/Makefile b/lectures/resources/sample/Makefile index 9e6cd17..3f8b388 100644 --- a/lectures/resources/sample/Makefile +++ b/lectures/resources/sample/Makefile @@ -1,7 +1,7 @@ CXXFLAGS = --std=c++17 CC = $(CXX) -exes = arr1 arr2 arr3 shared file +exes = arr1 arr2 arr3 shared all : $(exes) clean : @@ -10,5 +10,3 @@ arr1 : arr1.o arr2 : arr2.o arr3 : arr3.o shared : shared.o -file : file.o - diff --git a/lectures/resources/sample/arr1.cpp b/lectures/resources/sample/arr1.cpp index 9a0fbe2..b952b39 100644 --- a/lectures/resources/sample/arr1.cpp +++ b/lectures/resources/sample/arr1.cpp @@ -8,10 +8,13 @@ class my_array { my_array(unsigned n) : size(n), data(new double[n]) { std::cout << "Constructing: " << data << std::endl; } + + // Destructor ~my_array() { std::cout << "Destroying: " < Date: Thu, 24 Oct 2024 16:04:24 +0100 Subject: [PATCH 23/91] Update templates slides Add example to illustrate why templates need to be placed in a header file --- lectures/templates/README.md | 49 +++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/lectures/templates/README.md b/lectures/templates/README.md index 0680b47..2ad519a 100644 --- a/lectures/templates/README.md +++ b/lectures/templates/README.md @@ -1,7 +1,7 @@ template: titleslide # Templates -## James Richings -## j.richings@epcc.ed.ac.uk +## Nathan Mannall +## n.mannall@epcc.ed.ac.uk ??? @@ -151,11 +151,49 @@ public: --- # Where to put your implementation? +```C++ +// main.cpp + +#include + +template +T addOne(T x); // function template forward declaration + +int main() +{ + std::cout << addOne(1) << '\n'; + std::cout << addOne(2.3) << '\n'; + + return 0; +} +``` + +```C++ +// add.cpp + +template +T addOne(T x) // function template definition +{ + return x + 1; +} +``` + +??? +`main.cpp` will compile - it can see the forward declartion and trusts the implementaitons will be compiled. + +`add.cpp` will compile, but not it will not instatiate anything as it does +not see any uses of the `addOne` function. Therefore you will get a linker error. + +We fix this by putting all the template code in a header file. Each `.cpp` file +will see the template definition and instatiate any functions as needed. + +--- +# Where to put your implementation? + Templates are *not* executable code - they tell the compiler how to create it. -So the definition must be available in the translation unit of the user of your template - -i.e. typically in a header file. +So the definition must be available in the translation unit of the user of your template - i.e. typically in a header file. You can define the functions in place like: @@ -182,6 +220,9 @@ Point out the uglier syntax of the second form but on the other hand the class definition shown earlier is cleaner with only the member function declarations +But surely this process can cause the same function to be defined in +multiple places? (Next slide) + --- # Templates and the One Definition Rule From cba8222a07f589829bd24a725e9966eb56a28a72 Mon Sep 17 00:00:00 2001 From: nmannall Date: Fri, 25 Oct 2024 15:27:43 +0100 Subject: [PATCH 24/91] Update algorithms, lambdas and traits slides --- lectures/algorithms-lambdas-traits/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lectures/algorithms-lambdas-traits/README.md b/lectures/algorithms-lambdas-traits/README.md index 4d367cc..832c860 100644 --- a/lectures/algorithms-lambdas-traits/README.md +++ b/lectures/algorithms-lambdas-traits/README.md @@ -1,8 +1,8 @@ template: titleslide # Algorithms, lambdas, traits -## James Richings -## j.richings@epcc.ed.ac.uk +## Nathan Mannall +## n.mannall@epcc.ed.ac.uk --- @@ -201,7 +201,7 @@ std::vector SquareAndAddConst(const std::vector& x, float c) { ans.resize(x.size()); std::transform(x.begin(), x.end(), ans.begin(), - SquareAndAddConst(c)); + SquareAndAddConstF(c)); return ans; } ``` From 5123ae59dd4cd9f582690a592dece1f89427bf0c Mon Sep 17 00:00:00 2001 From: nmannall Date: Fri, 25 Oct 2024 15:48:41 +0100 Subject: [PATCH 25/91] Update exercise slides at the end of each slide deck --- lectures/algorithms-lambdas-traits/README.md | 21 ++++++++++++++++++++ lectures/cpp-intro/README.md | 6 ++++++ lectures/loops-containers/README.md | 18 ++++++++++------- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/lectures/algorithms-lambdas-traits/README.md b/lectures/algorithms-lambdas-traits/README.md index 832c860..83f471b 100644 --- a/lectures/algorithms-lambdas-traits/README.md +++ b/lectures/algorithms-lambdas-traits/README.md @@ -554,3 +554,24 @@ MPI_Datatype DataTypeTraits::Get() { If we try to communicate a data type we haven't specialised for, we will get a compile time error! +--- +# Algorithms Exercise + +In your clone of this repository, find the `algorithm` exercise and list +the files + +``` +$ cd archer2-cpp/exercises/algorithm +$ ls +Makefile ex.cpp README.md +``` + +In the file `ex.cpp` there is an incomplete program which, by +following the instructions in the comments, you can finish. + +You will likely want to refer to the documentation of the standard +library algorithms, which, for reasons, are split across two headers: + +- + +- diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index 43fa197..50da58f 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -617,6 +617,12 @@ int main(int argc, char* argv[]) { } ``` +**Exercise:** + +Change `say_hello` to accept the name it reads from the +terminal, create a new message saying "Hello, $NAME!" and +print it to standard output. + ??? What I'd like you to do is change `say_hello` to accept the name it diff --git a/lectures/loops-containers/README.md b/lectures/loops-containers/README.md index 6f3b0ac..cb2399d 100644 --- a/lectures/loops-containers/README.md +++ b/lectures/loops-containers/README.md @@ -472,14 +472,18 @@ $ ls Makefile test.cpp vector_ex.cpp vector_ex.hpp ``` -As before, `test.cpp` holds some basic unit tests. +As before, `test.cpp` holds some basic unit tests, and you can compile with `make`. -`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide the implementations +**Part 1** -The tests require that you implement, in a new header/implementation -pair of files, a function to add a string to a `std::map` as the key, -the value being the length of the string. +`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide +the implementations. -You will want to find the documentatation for `map` on https://en.cppreference.com/ -You can compile with `make` as before. +**Part 2** + +Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), +a function (`AddWord`) that adds a string to a `std::map` as the key, the value +being the length of the string. + +You will want to find the documentatation for `map` on https://en.cppreference.com/ From 86a419fedf5ffaa935992e894e7358e1af079f45 Mon Sep 17 00:00:00 2001 From: nmannall Date: Mon, 28 Oct 2024 14:52:28 +0000 Subject: [PATCH 26/91] Split containers exercise into part1 and part2 directories --- exercises/containers/part1/Makefile | 10 ++++ exercises/containers/part1/test.cpp | 56 +++++++++++++++++++ .../containers/{ => part1}/vector_ex.cpp | 0 .../containers/{ => part1}/vector_ex.hpp | 0 exercises/containers/{ => part2}/Makefile | 2 +- exercises/containers/{ => part2}/test.cpp | 0 exercises/containers/part2/vector_ex.cpp | 8 +++ exercises/containers/part2/vector_ex.hpp | 18 ++++++ 8 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 exercises/containers/part1/Makefile create mode 100644 exercises/containers/part1/test.cpp rename exercises/containers/{ => part1}/vector_ex.cpp (100%) rename exercises/containers/{ => part1}/vector_ex.hpp (100%) rename exercises/containers/{ => part2}/Makefile (71%) rename exercises/containers/{ => part2}/test.cpp (100%) create mode 100644 exercises/containers/part2/vector_ex.cpp create mode 100644 exercises/containers/part2/vector_ex.hpp diff --git a/exercises/containers/part1/Makefile b/exercises/containers/part1/Makefile new file mode 100644 index 0000000..57ac7ba --- /dev/null +++ b/exercises/containers/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 -I../../include + +test : vector_ex.o test.o + $(CXX) $^ -o $@ + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/containers/part1/test.cpp b/exercises/containers/part1/test.cpp new file mode 100644 index 0000000..471c03d --- /dev/null +++ b/exercises/containers/part1/test.cpp @@ -0,0 +1,56 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "vector_ex.hpp" +#include + +// type alias +using IV = std::vector; + +IV IntRange(int n) { + auto ans = IV(n); + for (int i = 0; i < n; ++i) + ans[i] = i; + return ans; +} + +TEST_CASE("Vector Even") { + IV const empty; + REQUIRE(empty == GetEven(empty)); + + auto const zero = IntRange(1); + // Test the testing code! + REQUIRE(zero.size() == 1); + REQUIRE(zero[0] == 0); + + REQUIRE(zero == GetEven(zero)); + + auto const zero_one = IntRange(2); + // Test the testing code! + REQUIRE(zero_one.size() == 2); + REQUIRE(zero_one[0] == 0); + REQUIRE(zero_one[1] == 1); + + REQUIRE(zero == GetEven(zero_one)); + + // edited toddler random numbers + auto const random = IV{8, 127356, 1, 29, 4376, 263478, 1537}; + auto const random_even = IV{ 8, 127356, 4376, 263478}; + REQUIRE(GetEven(random) == random_even); +} + +std::string STR(IV const& v) { + std::stringstream ss; + PrintVectorOfInt(ss, v); + return ss.str(); +} + +TEST_CASE("Vector Print") { + REQUIRE(STR(IV{}) == "[ ]"); + REQUIRE(STR(IntRange(1)) == "[ 0]"); + REQUIRE(STR(IntRange(2)) == "[ 0, 1]"); + // edited toddler random numbers + REQUIRE(STR(IV{8, 127356, 1, 29, 4376, 263478, 1537}) == "[ 8, 127356, 1, 29, 4376, 263478, 1537]"); +} diff --git a/exercises/containers/vector_ex.cpp b/exercises/containers/part1/vector_ex.cpp similarity index 100% rename from exercises/containers/vector_ex.cpp rename to exercises/containers/part1/vector_ex.cpp diff --git a/exercises/containers/vector_ex.hpp b/exercises/containers/part1/vector_ex.hpp similarity index 100% rename from exercises/containers/vector_ex.hpp rename to exercises/containers/part1/vector_ex.hpp diff --git a/exercises/containers/Makefile b/exercises/containers/part2/Makefile similarity index 71% rename from exercises/containers/Makefile rename to exercises/containers/part2/Makefile index e1cfa1b..c61fa37 100644 --- a/exercises/containers/Makefile +++ b/exercises/containers/part2/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS = --std=c++17 -I../include +CXXFLAGS = --std=c++17 -I../../include test : vector_ex.o map_ex.o test.o $(CXX) $^ -o $@ diff --git a/exercises/containers/test.cpp b/exercises/containers/part2/test.cpp similarity index 100% rename from exercises/containers/test.cpp rename to exercises/containers/part2/test.cpp diff --git a/exercises/containers/part2/vector_ex.cpp b/exercises/containers/part2/vector_ex.cpp new file mode 100644 index 0000000..00c2828 --- /dev/null +++ b/exercises/containers/part2/vector_ex.cpp @@ -0,0 +1,8 @@ +#include "vector_ex.hpp" + +// std::vector documentation: +// https://en.cppreference.com/w/cpp/container/vector + +std::vector GetEven(std::vector const& source) { +} + diff --git a/exercises/containers/part2/vector_ex.hpp b/exercises/containers/part2/vector_ex.hpp new file mode 100644 index 0000000..22372c4 --- /dev/null +++ b/exercises/containers/part2/vector_ex.hpp @@ -0,0 +1,18 @@ +#ifndef CPPEX_VECTOR_EX_HPP +#define CPPEX_VECTOR_EX_HPP + +#include +#include + +// Here we declare two functions. +// Provide implementations, in vector_ex.cpp + +// Given a vector of integers, return a new vector with only the even +// elements from our input +std::vector GetEven(std::vector const& source); + +// Given a vector of ints, print the data to the stream +// [0, 1] +void PrintVectorOfInt(std::ostream& output, std::vector const& data); + +#endif From c383a65a7cf1e8e2b0161a419b7e985198b90bc6 Mon Sep 17 00:00:00 2001 From: Rupert Nash Date: Tue, 29 Oct 2024 15:53:03 +0000 Subject: [PATCH 27/91] Update Makefile for Archer2 --- exercises/threads/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/threads/Makefile b/exercises/threads/Makefile index 60a3368..e01548c 100644 --- a/exercises/threads/Makefile +++ b/exercises/threads/Makefile @@ -3,8 +3,8 @@ # # C compiler and options for Intel # -CC= icpc -CFLAGS = -fast -std=c++11 +CC= g++ +CFLAGS = -O2 -std=c++11 LIB= -lm -lpthread # From 79e9dcb11472bfcedad68c559c946f0b834618a0 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 10:06:08 +0000 Subject: [PATCH 28/91] Correct test of copying complex numbers --- exercises/complex/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exercises/complex/test.cpp b/exercises/complex/test.cpp index 60d8077..e34b0a9 100644 --- a/exercises/complex/test.cpp +++ b/exercises/complex/test.cpp @@ -20,8 +20,10 @@ TEST_CASE("Complex numbers are constructed real/imag parts readable") { const Complex z1{1, -83}; const Complex z2 = z1; - REQUIRE(i.real() == 0.0); - REQUIRE(i.imag() == 1.0); + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); } TEST_CASE("Complex numbers can be compared") { From ac76a349e77422b850a8451ea794c2fd024c8c35 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 11:02:50 +0000 Subject: [PATCH 29/91] Check non equality of complex number imaginary part --- exercises/complex/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/complex/test.cpp b/exercises/complex/test.cpp index e34b0a9..cb05e55 100644 --- a/exercises/complex/test.cpp +++ b/exercises/complex/test.cpp @@ -34,6 +34,7 @@ TEST_CASE("Complex numbers can be compared") { REQUIRE(one == one); REQUIRE(i == i); REQUIRE(zero != one); + REQUIRE(zero != i); REQUIRE(one != i); } From d530264e898520e622a67eb6d8391c4e8e83dc83 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 11:43:43 +0000 Subject: [PATCH 30/91] Add new class types exercise --- exercises/2.1-class-types/Makefile | 15 +++++ exercises/2.1-class-types/README.md | 27 ++++++++ exercises/2.1-class-types/complex.cpp | 28 ++++++++ exercises/2.1-class-types/complex.hpp | 37 +++++++++++ exercises/2.1-class-types/test.cpp | 96 +++++++++++++++++++++++++++ 5 files changed, 203 insertions(+) create mode 100644 exercises/2.1-class-types/Makefile create mode 100644 exercises/2.1-class-types/README.md create mode 100644 exercises/2.1-class-types/complex.cpp create mode 100644 exercises/2.1-class-types/complex.hpp create mode 100644 exercises/2.1-class-types/test.cpp diff --git a/exercises/2.1-class-types/Makefile b/exercises/2.1-class-types/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/2.1-class-types/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/2.1-class-types/README.md b/exercises/2.1-class-types/README.md new file mode 100644 index 0000000..a9056a3 --- /dev/null +++ b/exercises/2.1-class-types/README.md @@ -0,0 +1,27 @@ +In your clone of this repository, find the `2.1-class-types` exercise and list the files + +``` +$ cd archer2-cpp/exercises/2.1-class-types +$ ls +Makefile README.md complex.cpp complex.hpp test.cpp +``` + +The files `complex.hpp` and `complex.cpp` contain the beginings of a complex number class. Follow the instructions in the comments to complete the missing declarations in `complex.hpp` and then add the required out of line definitions in `complex.cpp`. + +To test your implementation, `test.cpp` holds some basic unit tests created using the Catch2 unit testing library. + +You can compile by running: + +``` +$ make +g++ --std=c++14 -I../include -c -o complex.o complex.cpp +g++ complex.o test.o -o test +``` + +Then run the tests using: + +``` +$ ./test +=============================================================================== +All tests passed (36 assertions in 5 test cases) +``` diff --git a/exercises/2.1-class-types/complex.cpp b/exercises/2.1-class-types/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/2.1-class-types/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/2.1-class-types/complex.hpp b/exercises/2.1-class-types/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/2.1-class-types/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/2.1-class-types/test.cpp b/exercises/2.1-class-types/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/2.1-class-types/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file From 9350db3a6e1092bcc0af5af93e0fe80665c4d2c8 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 12:48:04 +0000 Subject: [PATCH 31/91] Reorder classes slides and add new exercise slide --- lectures/classes/README.md | 304 +++++++++++++++++++++---------------- 1 file changed, 169 insertions(+), 135 deletions(-) diff --git a/lectures/classes/README.md b/lectures/classes/README.md index ca67cd7..3a751b5 100644 --- a/lectures/classes/README.md +++ b/lectures/classes/README.md @@ -296,69 +296,6 @@ The compiler inserts an implicit argument referring to the current instance for us, known as the 'this' pointer. See https://www.learncpp.com/cpp-tutorial/the-hidden-this-pointer/ for more info ---- -# More on operator overloading - -Complex numbers have the usual arithmetic operations: `\(+-\times\div\)` - -We can provide operator overloads, like: - -```C++ -struct Complex { - // Other members... - - Complex& operator+=(Complex const & increment) { - re += increment.re; - im += increment.im; - return *this; - } -}; -Complex operator+(Complex const& a, Complex const& b) { - return Complex{a.re+b.re, a.im+b.im}; -} -``` - -Here, `operator+=` is using the implicit `this` pointer - -??? - -Recall that these are just functions (with funny names) - -We have a member function `operator+=` and a non-member (aka free -function) - -The compiler is already implicitly turning `a+b` into plus(a, b) -internally. - -If anyone asks, references and `const` coming up - ---- -# More on operator overloading - -Complex numbers have the usual arithmetic operations -(`\(+-\times\div\)`) and comparisons. - -We can now use the natural syntax to add these values - -```C++ -Complex i{0, 1}; - -Complex z; -z += i; - -assert(z.re == 0 && z.im == 1); - -auto c = z + i; -assert(c == 2*i); -``` - -??? - -We could also overload multiplication and equality comparison as shown -in the last line - -Go look up the complete list on CPP Reference! - --- # Classes and structs @@ -435,6 +372,175 @@ allows only that bit of code to access its private member variables. This is a controlled, partial relaxation of encapsulation that often makes the whole system more isolated. +--- +template: titleslide +# C++ compilation + +--- +# Declarations vs Definitions + +C++ distinguishes between *declaration* and *definition*. + +A **declaration** tells the compiler that a name exists, what kind of +entity it refers to and (if it is a function or variable) its +type. For most uses this is all the compiler needs. Declarations can +be repeated, as long as they match *exactly*. + +A **definition** tells the compiler everything it needs to create +something in its entirety. A definition is also a declaration. The +one-definition rule says that definitions must not be repeated (with +an exception). + +??? + +The exceptions being templates and `inline` functions if anyone asks + +--- +# Where to put these + +- Conventionally, one puts declarations of functions, definitions of + classes, and global constants in **header** files. + + - Common suffixes are: `.hpp`, `.h`, `.H` + +- Definitions of most functions should be in **implementation** files + + - Common suffixes are `.cpp`, `.cxx`, `.cc`, `.C` + +- Headers can be be `#include` into other files that need to use the + types and function declared there. + +??? + +Suffixes mostly meaningless to the compiler but don't surprise people! + +Prefer earlier in the list i.e. .hpp and .cpp to differentiate between C++ +and C code + +--- +# Where to put these + +E.g. Complex.hpp: + +```C++ +#ifndef COMPLEX_HPP +#define COMPLEX_HPP + +struct Complex { + Complex() = default; + Complex(double real, double imag); + double real() // etc... +private: + double re; + double im; +}; + +#endif +``` + +Complex.cpp + +```C++ +#include "complex.hpp" + +Complex::Complex(double real, double image) : re(real), im(imag) {} + +double Complex::real() { + return re +} +``` +??? + +Draw attention to the include guard idiom - include the file only once + +--- +# Exercise + +In your clone of this repository, find the `2.1-class-types` exercise and list the files + +```bash +$ cd archer2-cpp/exercises/2.1-class-types +$ ls +Makefile README.md complex.cpp complex.hpp test.cpp +``` + +The files `complex.hpp` and `complex.cpp` contain the beginings of a complex number class. Follow the instructions in the comments to complete the missing declarations in `complex.hpp` and then add the required out of line definitions in `complex.cpp`. + +To test your implementation, `test.cpp` holds some basic unit tests created using the Catch2 unit testing library. + +You can compile and run with: + +```bash +$ make && ./test +g++ --std=c++14 -I../include -c -o complex.o complex.cpp +g++ complex.o test.o -o test +=============================================================================== +All tests passed (36 assertions in 5 test cases) +``` + +--- +# More on operator overloading + +Complex numbers have the usual arithmetic operations: `\(+-\times\div\)` + +We can provide operator overloads, like: + +```C++ +struct Complex { + // Other members... + + Complex& operator+=(Complex const & increment) { + re += increment.re; + im += increment.im; + return *this; + } +}; +Complex operator+(Complex const& a, Complex const& b) { + return Complex{a.re+b.re, a.im+b.im}; +} +``` + +Here, `operator+=` is using the implicit `this` pointer + +??? + +Recall that these are just functions (with funny names) + +We have a member function `operator+=` and a non-member (aka free +function) + +The compiler is already implicitly turning `a+b` into plus(a, b) +internally. + +If anyone asks, references and `const` coming up + +--- +# More on operator overloading + +Complex numbers have the usual arithmetic operations +(`\(+-\times\div\)`) and comparisons. + +We can now use the natural syntax to add these values + +```C++ +Complex i{0, 1}; + +Complex z; +z += i; + +assert(z.re == 0 && z.im == 1); + +auto c = z + i; +assert(c == 2*i); +``` + +??? + +We could also overload multiplication and equality comparison as shown +in the last line + +Go look up the complete list on CPP Reference! + --- template: titleslide @@ -709,78 +815,6 @@ Refer back to the last slide codes and ask mutable reference then it would be a mutable ref while `auto const&` would have stayed constant) - ---- -template: titleslide -# C++ compilation - ---- -# Declarations vs Definitions - -C++ distinguishes between *declaration* and *definition*. - -A **declaration** tells the compiler that a name exists, what kind of -entity it refers to and (if it is a function or variable) its -type. For most uses this is all the compiler needs. Declarations can -be repeated, as long as they match *exactly*. - -A **definition** tells the compiler everything it needs to create -something in its entirety. A definition is also a declaration. The -one-definition rule says that definitions must not be repeated (with -an exception). - -??? - -The exceptions being templates and `inline` functions if anyone asks - ---- -# Where to put these - -- Conventionally, one puts declarations of functions, definitions of - classes, and global constants in **header** files. - - - Common suffixes are: `.hpp`, `.h`, `.H` - -- Definitions of most functions should be in **implementation** files - - - Common suffixes are `.cpp`, `.cxx`, `.cc`, `.C` - -- Headers can be be `#include` into other files that need to use the - types and function declared there. - -??? - -Suffixes mostly meaningless to the compiler but don't surprise people! - -Prefer earlier in the list i.e. .hpp and .cpp to differentiate between C++ -and C code - ---- -# Where to put these - -E.g. Complex.hpp: - -```C++ -#ifndef COMPLEX_HPP -#define COMPLEX_HPP - -struct Complex { - Complex() = default; - // etc... -private: - double re; - double im; -}; - -Complex operator+(Complex const& a, Complex const& b); -// Etc... - -#endif -``` -??? - -Draw attention to the include guard idiom - include the file only once - --- # Exercise From 5f68a91a27f7877363d20ffa15b0cc4b356118ba Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 12:51:22 +0000 Subject: [PATCH 32/91] Move complex exercise to 2.2-complex folder --- exercises/{complex => 2.2-complex}/Makefile | 0 exercises/{complex => 2.2-complex}/README.md | 0 exercises/{complex => 2.2-complex}/complex.cpp | 0 exercises/{complex => 2.2-complex}/complex.hpp | 0 exercises/{complex => 2.2-complex}/test.cpp | 0 lectures/classes/README.md | 4 ++-- 6 files changed, 2 insertions(+), 2 deletions(-) rename exercises/{complex => 2.2-complex}/Makefile (100%) rename exercises/{complex => 2.2-complex}/README.md (100%) rename exercises/{complex => 2.2-complex}/complex.cpp (100%) rename exercises/{complex => 2.2-complex}/complex.hpp (100%) rename exercises/{complex => 2.2-complex}/test.cpp (100%) diff --git a/exercises/complex/Makefile b/exercises/2.2-complex/Makefile similarity index 100% rename from exercises/complex/Makefile rename to exercises/2.2-complex/Makefile diff --git a/exercises/complex/README.md b/exercises/2.2-complex/README.md similarity index 100% rename from exercises/complex/README.md rename to exercises/2.2-complex/README.md diff --git a/exercises/complex/complex.cpp b/exercises/2.2-complex/complex.cpp similarity index 100% rename from exercises/complex/complex.cpp rename to exercises/2.2-complex/complex.cpp diff --git a/exercises/complex/complex.hpp b/exercises/2.2-complex/complex.hpp similarity index 100% rename from exercises/complex/complex.hpp rename to exercises/2.2-complex/complex.hpp diff --git a/exercises/complex/test.cpp b/exercises/2.2-complex/test.cpp similarity index 100% rename from exercises/complex/test.cpp rename to exercises/2.2-complex/test.cpp diff --git a/lectures/classes/README.md b/lectures/classes/README.md index 3a751b5..35fb15a 100644 --- a/lectures/classes/README.md +++ b/lectures/classes/README.md @@ -818,11 +818,11 @@ Refer back to the last slide codes and ask --- # Exercise -In your clone of this repository, find the `complex` exercise and list +In your clone of this repository, find the `2.2-complex` exercise and list the files ``` -$ cd archer2-cpp/exercises/complex +$ cd archer2-cpp/exercises/2.2-complex $ ls Makefile complex.cpp complex.hpp test.cpp ``` From 6f7a9d6814b700bcfee4057165f3e3e9bd23c0a2 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 12:59:37 +0000 Subject: [PATCH 33/91] Add section numbers to section 2 exercises --- exercises/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/README.md b/exercises/README.md index 2da6e5f..567db42 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -4,7 +4,8 @@ Practical exercises for C++ course See each subdirectory for further instructions -* [Complex numbers](complex/) +* [2.1 Class types](2.1-class-types/) +* [2.2 Complex numbers](2.2-complex/) * [Containers](containers/) * [Morton-order matrix class template](morton-order/) * [Using algorithms](algorithm/) From e9b4e489e8f764173b15802b7e1603aa0a7cef75 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 13:26:52 +0000 Subject: [PATCH 34/91] Update instructions for containers exercise --- .../{containers => 3-containers}/README.md | 0 exercises/3-containers/instructions.md | 24 ++++++++++++++++++ .../instructions.pdf | Bin .../part1/Makefile | 0 .../part1/test.cpp | 0 .../part1/vector_ex.cpp | 0 .../part1/vector_ex.hpp | 0 .../part2/Makefile | 0 .../part2/test.cpp | 0 .../part2/vector_ex.cpp | 0 .../part2/vector_ex.hpp | 0 exercises/README.md | 6 ++--- exercises/containers/instructions.md | 22 ---------------- lectures/loops-containers/README.md | 14 +++++----- 14 files changed, 34 insertions(+), 32 deletions(-) rename exercises/{containers => 3-containers}/README.md (100%) create mode 100644 exercises/3-containers/instructions.md rename exercises/{containers => 3-containers}/instructions.pdf (100%) rename exercises/{containers => 3-containers}/part1/Makefile (100%) rename exercises/{containers => 3-containers}/part1/test.cpp (100%) rename exercises/{containers => 3-containers}/part1/vector_ex.cpp (100%) rename exercises/{containers => 3-containers}/part1/vector_ex.hpp (100%) rename exercises/{containers => 3-containers}/part2/Makefile (100%) rename exercises/{containers => 3-containers}/part2/test.cpp (100%) rename exercises/{containers => 3-containers}/part2/vector_ex.cpp (100%) rename exercises/{containers => 3-containers}/part2/vector_ex.hpp (100%) delete mode 100644 exercises/containers/instructions.md diff --git a/exercises/containers/README.md b/exercises/3-containers/README.md similarity index 100% rename from exercises/containers/README.md rename to exercises/3-containers/README.md diff --git a/exercises/3-containers/instructions.md b/exercises/3-containers/instructions.md new file mode 100644 index 0000000..ecee831 --- /dev/null +++ b/exercises/3-containers/instructions.md @@ -0,0 +1,24 @@ +# Containers exercise + +In your clone of this repository, find the `3-containers` exercise. It contains two sub-directories `part1` and `part2`. List +the files in `part1`: + +```bash +$ cd archer2-cpp/exercises/3-containers/part1 +$ ls +Makefile test.cpp vector_ex.cpp vector_ex.hpp +``` + +As before, `test.cpp` holds some basic unit tests and you can compile with `make`. + +## Part 1 +`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide the implementations + +## Part 2 +Copy `vector_ex.cpp`/`.hpp` from part 1 into the part 2 folder. + +Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), +a function (`AddWord`) that adds a string to a `std::map` as the key, the value +being the length of the string. + +You will want to find the documentatation for `map` on https://en.cppreference.com/ diff --git a/exercises/containers/instructions.pdf b/exercises/3-containers/instructions.pdf similarity index 100% rename from exercises/containers/instructions.pdf rename to exercises/3-containers/instructions.pdf diff --git a/exercises/containers/part1/Makefile b/exercises/3-containers/part1/Makefile similarity index 100% rename from exercises/containers/part1/Makefile rename to exercises/3-containers/part1/Makefile diff --git a/exercises/containers/part1/test.cpp b/exercises/3-containers/part1/test.cpp similarity index 100% rename from exercises/containers/part1/test.cpp rename to exercises/3-containers/part1/test.cpp diff --git a/exercises/containers/part1/vector_ex.cpp b/exercises/3-containers/part1/vector_ex.cpp similarity index 100% rename from exercises/containers/part1/vector_ex.cpp rename to exercises/3-containers/part1/vector_ex.cpp diff --git a/exercises/containers/part1/vector_ex.hpp b/exercises/3-containers/part1/vector_ex.hpp similarity index 100% rename from exercises/containers/part1/vector_ex.hpp rename to exercises/3-containers/part1/vector_ex.hpp diff --git a/exercises/containers/part2/Makefile b/exercises/3-containers/part2/Makefile similarity index 100% rename from exercises/containers/part2/Makefile rename to exercises/3-containers/part2/Makefile diff --git a/exercises/containers/part2/test.cpp b/exercises/3-containers/part2/test.cpp similarity index 100% rename from exercises/containers/part2/test.cpp rename to exercises/3-containers/part2/test.cpp diff --git a/exercises/containers/part2/vector_ex.cpp b/exercises/3-containers/part2/vector_ex.cpp similarity index 100% rename from exercises/containers/part2/vector_ex.cpp rename to exercises/3-containers/part2/vector_ex.cpp diff --git a/exercises/containers/part2/vector_ex.hpp b/exercises/3-containers/part2/vector_ex.hpp similarity index 100% rename from exercises/containers/part2/vector_ex.hpp rename to exercises/3-containers/part2/vector_ex.hpp diff --git a/exercises/README.md b/exercises/README.md index 567db42..fbf4739 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -4,9 +4,9 @@ Practical exercises for C++ course See each subdirectory for further instructions -* [2.1 Class types](2.1-class-types/) -* [2.2 Complex numbers](2.2-complex/) -* [Containers](containers/) +* [2.1 - Class types](2.1-class-types/) +* [2.2 - Complex numbers](2.2-complex/) +* [3 - Containers](3-containers/) * [Morton-order matrix class template](morton-order/) * [Using algorithms](algorithm/) * [Eigen](eigen/) diff --git a/exercises/containers/instructions.md b/exercises/containers/instructions.md deleted file mode 100644 index ca4ec02..0000000 --- a/exercises/containers/instructions.md +++ /dev/null @@ -1,22 +0,0 @@ -# Containers exercise - -In your clone of this repository, find the `containers` exercise and list -the files - -``` -$ cd archer2-CPP-2021-07-20/exercises/containers -$ ls -Makefile test.cpp vector_ex.cpp vector_ex.hpp -``` - -As before, `test.cpp` holds some basic unit tests. - -`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide the implementations - -The tests require that you implement, in a new header/implementation -pair of files, a function to add a string to a `std::map` as the key, -the value being the length of the string. - -You will want to find the documentatation for `map` on https://en.cppreference.com/ - -You can compile with `make` as before. diff --git a/lectures/loops-containers/README.md b/lectures/loops-containers/README.md index cb2399d..f9477ee 100644 --- a/lectures/loops-containers/README.md +++ b/lectures/loops-containers/README.md @@ -463,16 +463,16 @@ template: titleslide --- # Containers exercise -In your clone of this repository, find the `containers` exercise and list -the files +In your clone of this repository, find the `3-containers` exercise. It contains two sub-directories `part1` and `part2`. List +the files in `part1`: -``` -$ cd archer2-cpp/exercises/containers +```bash +$ cd archer2-cpp/exercises/3-containers/part1 $ ls -Makefile test.cpp vector_ex.cpp vector_ex.hpp +Makefile test.cpp vector_ex.cpp vector_ex.hpp ``` -As before, `test.cpp` holds some basic unit tests, and you can compile with `make`. +As before, `test.cpp` holds some basic unit tests and you can compile with `make`. **Part 1** @@ -484,6 +484,6 @@ the implementations. Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), a function (`AddWord`) that adds a string to a `std::map` as the key, the value -being the length of the string. +being the length of the string. Note: Copy your completed `vector_ex.cpp`/`.hpp` files from part 1. You will want to find the documentatation for `map` on https://en.cppreference.com/ From 03ac9e3eba091f085aa26f4bd393433b61f66ce0 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 16:38:49 +0000 Subject: [PATCH 35/91] Add new templates exercise --- exercises/5-templates/README.md | 36 ++++++++ exercises/5-templates/part1/Makefile | 10 +++ exercises/5-templates/part1/sum.cpp | 18 ++++ exercises/5-templates/part2/Makefile | 15 ++++ exercises/5-templates/part2/complex.cpp | 48 +++++++++++ exercises/5-templates/part2/complex.hpp | 43 ++++++++++ exercises/5-templates/part2/test.cpp | 107 ++++++++++++++++++++++++ lectures/templates/README.md | 28 +++++++ 8 files changed, 305 insertions(+) create mode 100644 exercises/5-templates/README.md create mode 100644 exercises/5-templates/part1/Makefile create mode 100644 exercises/5-templates/part1/sum.cpp create mode 100644 exercises/5-templates/part2/Makefile create mode 100644 exercises/5-templates/part2/complex.cpp create mode 100644 exercises/5-templates/part2/complex.hpp create mode 100644 exercises/5-templates/part2/test.cpp diff --git a/exercises/5-templates/README.md b/exercises/5-templates/README.md new file mode 100644 index 0000000..bf4b181 --- /dev/null +++ b/exercises/5-templates/README.md @@ -0,0 +1,36 @@ +# Containers exercise + +In your clone of this repository, find the `5-templates` exercise. It contains two sub-directories `part1` and `part2`. + +## Part 1 + +List the files in `part1`: + +```bash +$ cd archer2-cpp/exercises/5-templates/part1 +$ ls +Makefile sum.cpp +``` + +1. Have a look at `sum.cpp`. Do you think it will compile? If so, what will the output be? +2. Compile and run the provided code with `make && ./sum`. Is the result what you expected? Can you explain what is happening at line 12? +```C++ +11 sum(3, 4); +12 sum(3.2, 5.1); +13 // sum("Hello", "World!"); +``` +3. Uncomment line 13. What happens when you try to compile? +3. Change the `sum()` function to use type templating. How does this change the output? + + +## Part 2 + +List the files in `part2`: + +```bash +$ cd archer2-cpp/exercises/5-templates/part2 +$ ls +Makefile complex.cpp complex.hpp test.cpp +``` + +`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. diff --git a/exercises/5-templates/part1/Makefile b/exercises/5-templates/part1/Makefile new file mode 100644 index 0000000..892ae1e --- /dev/null +++ b/exercises/5-templates/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++14 -I../../include + +sum : sum.o + $(CXX) $^ -o $@ + +run : sum + ./sum + +clean : + rm -rf *.o sum diff --git a/exercises/5-templates/part1/sum.cpp b/exercises/5-templates/part1/sum.cpp new file mode 100644 index 0000000..15bdcfb --- /dev/null +++ b/exercises/5-templates/part1/sum.cpp @@ -0,0 +1,18 @@ +#include + +int sum(const int& a, const int& b) { + int result = a + b; + + std::cout << a << " + " << b << " = " << result << std::endl; + return result; +} + +int main() { + // 3 + 4 = 7 + sum(3, 4); + + // 3.2 + 5.1 = 8.3 + sum(3.2, 5.1); + + // sum("Hello", "World!"); +} \ No newline at end of file diff --git a/exercises/5-templates/part2/Makefile b/exercises/5-templates/part2/Makefile new file mode 100644 index 0000000..6c9dd97 --- /dev/null +++ b/exercises/5-templates/part2/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/5-templates/part2/complex.cpp b/exercises/5-templates/part2/complex.cpp new file mode 100644 index 0000000..3156605 --- /dev/null +++ b/exercises/5-templates/part2/complex.cpp @@ -0,0 +1,48 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) {} +Complex::Complex(double real, double imag) : re(real), im(imag) {} + +double Complex::real() const { + return re; +} + +double Complex::imag() const { + return im; +} + +Complex Complex::conj() const { + return Complex{re, -im}; +} + +double Complex::norm() const { + return std::sqrt(norm2()); +} + +double Complex::norm2() const { + return re*re + im*im; +} + +bool operator==(Complex const& a, Complex const& b) { + return (a.re == b.re) && (a.im == b.im); +} +bool operator!=(Complex const& a, Complex const& b) { + return !(a == b); +} + +Complex operator+(Complex const& a, Complex const& b) { + return Complex{a.re + b.re, a.im + b.im}; +} + +Complex operator-(Complex const& a, Complex const& b) { + return Complex{a.re - b.re, a.im - b.im}; +} + +Complex operator*(Complex const& a, Complex const& b) { + return Complex{a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re}; +} + +Complex operator-(Complex const& a) { + return Complex{-a.re, -a.im}; +} diff --git a/exercises/5-templates/part2/complex.hpp b/exercises/5-templates/part2/complex.hpp new file mode 100644 index 0000000..6ad0bf3 --- /dev/null +++ b/exercises/5-templates/part2/complex.hpp @@ -0,0 +1,43 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + // Default value is zero + Complex() = default; + Complex(double real); + Complex(double real, double imag); + + // Access components + double real() const; + double imag() const; + + // Compute the complex conjugate + Complex conj() const; + + // Compute the magnitude and squared magnitude + double norm() const; + double norm2() const; + + // Declare comparisons + friend bool operator==(Complex const& a, Complex const& b); + friend bool operator!=(Complex const& a, Complex const& b); + + // Declare binary arithmetic operators + friend Complex operator+(Complex const& a, Complex const& b); + friend Complex operator-(Complex const& a, Complex const& b); + friend Complex operator*(Complex const& a, Complex const& b); + friend Complex operator/(Complex const& a, Complex const& b); + // Question: how would you declare multiplication and division by a real number? + + // Unary negation + friend Complex operator-(Complex const& a); + +private: + double re = 0.0; + double im = 0.0; +}; + + +#endif diff --git a/exercises/5-templates/part2/test.cpp b/exercises/5-templates/part2/test.cpp new file mode 100644 index 0000000..30f3ea4 --- /dev/null +++ b/exercises/5-templates/part2/test.cpp @@ -0,0 +1,107 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + const Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + const Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + const Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + const Complex z1{1, -83}; + const Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can be compared") { + const Complex zero; + const Complex one{1.0}; + const Complex i{0, 1}; + REQUIRE(zero == zero); + REQUIRE(one == one); + REQUIRE(i == i); + REQUIRE(zero != one); + REQUIRE(zero != i); + REQUIRE(one != i); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm2() == 0.0); + REQUIRE(Complex{3,4}.norm2() == 25.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + REQUIRE(z == z.conj()); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + + REQUIRE(z == -z.conj()); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +void CheckZplusZeq2Z(const Complex& z) { + REQUIRE(z + z == Complex{2*z.real(), 2*z.imag()}); +} +void CheckZminusZeq0(const Complex& z) { + REQUIRE(z - z == Complex{}); +} + +TEST_CASE("Complex number can be added and subtracted") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1,2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} + +TEST_CASE("Complex numbers can be multiplied") { + const Complex i{0, 1}; + Complex z{1}; + z = z*i; + REQUIRE(z == i); + z = z*i; + REQUIRE(z == Complex{-1}); + z = z*i; + REQUIRE(z == -i); +} + +TEST_CASE("Complex numbers can be templated") { + const Complex z1{3.4, 5.1}; + const Complex z2{3.4, 5.1}; + REQUIRE(z1 != z2); + REQUIRE(z1.real() == 3); + REQUIRE(z1.imag() == 5); + REQUIRE(z1.norm2() == 34); +} diff --git a/lectures/templates/README.md b/lectures/templates/README.md index 2ad519a..53cd8dd 100644 --- a/lectures/templates/README.md +++ b/lectures/templates/README.md @@ -339,3 +339,31 @@ copy add a reference `&` The `auto` keyword follows the same rules as template argument deduction + +--- +template: titleslide +# Exercise + +--- +# Templates Exercise + +In your clone of this repository, find the `5-templates` exercise. It contains two sub-directories `part1` and `part2`. + +**Part 1** + +1. Have a look at `sum.cpp`. Do you think it will compile? If so, what will the output be? +2. Compile and run the provided code with `make && ./sum`. Is the result what you expected? Can you explain what is happening at line 12? +```C++ +11 sum(3, 4); +12 sum(3.2, 5.1); +13 // sum("Hello", "World!"); +``` +3. Uncomment line 13. What happens when you try to compile? +4. Change the `sum()` function to use type templating. How does this change the output? + + +**Part 2** + +`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. + +As before, `test.cpp` holds some basic unit tests and you can compile with `make`. From f4519d8dbfd277ab4f0744ad9174d9fb5bb25e7f Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Mon, 10 Mar 2025 18:33:33 +0000 Subject: [PATCH 36/91] Add type templating to tests --- exercises/5-templates/part2/test.cpp | 44 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/exercises/5-templates/part2/test.cpp b/exercises/5-templates/part2/test.cpp index 30f3ea4..60f02b3 100644 --- a/exercises/5-templates/part2/test.cpp +++ b/exercises/5-templates/part2/test.cpp @@ -6,20 +6,20 @@ #include "complex.hpp" TEST_CASE("Complex numbers are constructed real/imag parts readable") { - const Complex zero; + const Complex zero; REQUIRE(zero.real() == 0.0); REQUIRE(zero.imag() == 0.0); - const Complex one{1.0}; + const Complex one{1}; REQUIRE(one.real() == 1.0); REQUIRE(one.imag() == 0.0); - const Complex i{0, 1}; + const Complex i{0, 1}; REQUIRE(i.real() == 0.0); REQUIRE(i.imag() == 1.0); - const Complex z1{1, -83}; - const Complex z2 = z1; + const Complex z1{1, -83}; + const Complex z2 = z1; REQUIRE(z1.real() == z2.real()); REQUIRE(z1.imag() == z2.imag()); REQUIRE(z2.real() == 1.0); @@ -27,9 +27,9 @@ TEST_CASE("Complex numbers are constructed real/imag parts readable") { } TEST_CASE("Complex numbers can be compared") { - const Complex zero; - const Complex one{1.0}; - const Complex i{0, 1}; + const Complex zero; + const Complex one{1}; + const Complex i{0, 1}; REQUIRE(zero == zero); REQUIRE(one == one); REQUIRE(i == i); @@ -39,18 +39,18 @@ TEST_CASE("Complex numbers can be compared") { } TEST_CASE("Complex numbers can have magnitude computed") { - REQUIRE(Complex{}.norm2() == 0.0); - REQUIRE(Complex{3,4}.norm2() == 25.0); + REQUIRE(Complex{}.norm2() == 0.0); + REQUIRE(Complex{3,4}.norm2() == 25.0); } // Pure real => z == z* void CheckConjReal(double x) { - Complex z{x}; + Complex z{x}; REQUIRE(z == z.conj()); } // Pure imaginary => z* == -z void CheckConjImag(double y) { - Complex z{0.0, y}; + Complex z{0.0, y}; REQUIRE(z == -z.conj()); } @@ -67,11 +67,11 @@ TEST_CASE("Complex numbers be conjugated") { CheckConjImag(1.876e6); } -void CheckZplusZeq2Z(const Complex& z) { - REQUIRE(z + z == Complex{2*z.real(), 2*z.imag()}); +void CheckZplusZeq2Z(const Complex& z) { + REQUIRE(z + z == Complex{2*z.real(), 2*z.imag()}); } -void CheckZminusZeq0(const Complex& z) { - REQUIRE(z - z == Complex{}); +void CheckZminusZeq0(const Complex& z) { + REQUIRE(z - z == Complex{}); } TEST_CASE("Complex number can be added and subtracted") { @@ -82,23 +82,23 @@ TEST_CASE("Complex number can be added and subtracted") { CheckZminusZeq0(1); CheckZminusZeq0(0); CheckZminusZeq0(-1); - CheckZminusZeq0(Complex{1,2}); - CheckZminusZeq0(Complex{-42, 1e-3}); + CheckZminusZeq0(Complex{1,2}); + CheckZminusZeq0(Complex{-42, 1e-3}); } TEST_CASE("Complex numbers can be multiplied") { - const Complex i{0, 1}; - Complex z{1}; + const Complex i{0, 1}; + Complex z{1}; z = z*i; REQUIRE(z == i); z = z*i; - REQUIRE(z == Complex{-1}); + REQUIRE(z == Complex{-1}); z = z*i; REQUIRE(z == -i); } TEST_CASE("Complex numbers can be templated") { - const Complex z1{3.4, 5.1}; + const Complex z1{static_cast(3.4), static_cast(5.1)}; const Complex z2{3.4, 5.1}; REQUIRE(z1 != z2); REQUIRE(z1.real() == 3); From a8f0239a62efb69e0102a37475b7f600f9bc4bda Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 21:45:40 +0000 Subject: [PATCH 37/91] updates to course structure --- lectures/Combining-classes/README.md | 189 ++++++++++++ lectures/Combining-classes/index.html | 11 + lectures/RAII-cont/README.md | 403 ++++++++++++++++++++++++++ lectures/RAII-cont/index.html | 11 + lectures/RAII-cont/mem_layout.jpg | Bin 0 -> 29477 bytes lectures/RAII-cont/sample/.gitignore | 4 + lectures/RAII-cont/sample/Makefile | 12 + lectures/RAII-cont/sample/arr1.cpp | 26 ++ lectures/RAII-cont/sample/arr2.cpp | 45 +++ lectures/RAII-cont/sample/arr3.cpp | 69 +++++ lectures/RAII-cont/sample/dyn1.cpp | 23 ++ lectures/RAII-cont/sample/dyn2.cpp | 38 +++ lectures/RAII-cont/sample/dyn3.cpp | 57 ++++ lectures/RAII-cont/sample/shared.cpp | 26 ++ lectures/RAII/README.md | 402 +++++++++++++++++++++++++ lectures/RAII/index.html | 11 + lectures/RAII/mem_layout.jpg | Bin 0 -> 29477 bytes lectures/RAII/sample/.gitignore | 4 + lectures/RAII/sample/Makefile | 12 + lectures/RAII/sample/arr1.cpp | 26 ++ lectures/RAII/sample/arr2.cpp | 45 +++ lectures/RAII/sample/arr3.cpp | 69 +++++ lectures/RAII/sample/dyn1.cpp | 23 ++ lectures/RAII/sample/dyn2.cpp | 38 +++ lectures/RAII/sample/dyn3.cpp | 57 ++++ lectures/RAII/sample/shared.cpp | 26 ++ lectures/README.md | 4 + lectures/course-intro/README.md | 177 +++++++++++ lectures/course-intro/index.html | 10 + lectures/cpp-intro/README.md | 41 ++- 30 files changed, 1856 insertions(+), 3 deletions(-) create mode 100644 lectures/Combining-classes/README.md create mode 100644 lectures/Combining-classes/index.html create mode 100644 lectures/RAII-cont/README.md create mode 100644 lectures/RAII-cont/index.html create mode 100644 lectures/RAII-cont/mem_layout.jpg create mode 100644 lectures/RAII-cont/sample/.gitignore create mode 100644 lectures/RAII-cont/sample/Makefile create mode 100644 lectures/RAII-cont/sample/arr1.cpp create mode 100644 lectures/RAII-cont/sample/arr2.cpp create mode 100644 lectures/RAII-cont/sample/arr3.cpp create mode 100644 lectures/RAII-cont/sample/dyn1.cpp create mode 100644 lectures/RAII-cont/sample/dyn2.cpp create mode 100644 lectures/RAII-cont/sample/dyn3.cpp create mode 100644 lectures/RAII-cont/sample/shared.cpp create mode 100644 lectures/RAII/README.md create mode 100644 lectures/RAII/index.html create mode 100644 lectures/RAII/mem_layout.jpg create mode 100644 lectures/RAII/sample/.gitignore create mode 100644 lectures/RAII/sample/Makefile create mode 100644 lectures/RAII/sample/arr1.cpp create mode 100644 lectures/RAII/sample/arr2.cpp create mode 100644 lectures/RAII/sample/arr3.cpp create mode 100644 lectures/RAII/sample/dyn1.cpp create mode 100644 lectures/RAII/sample/dyn2.cpp create mode 100644 lectures/RAII/sample/dyn3.cpp create mode 100644 lectures/RAII/sample/shared.cpp create mode 100644 lectures/course-intro/README.md create mode 100644 lectures/course-intro/index.html diff --git a/lectures/Combining-classes/README.md b/lectures/Combining-classes/README.md new file mode 100644 index 0000000..d432c0f --- /dev/null +++ b/lectures/Combining-classes/README.md @@ -0,0 +1,189 @@ +template: titleslide +# Combining classes +## James Richings +## j.richings@epcc.ed.ac.uk + + + +--- +# Plan + + - **Inheritance** + - Inheritance principles + - Multi-level inheritance + - Multiple inheritance + - Access specifiers + - Polymorphism + - (new) **Example** (40 mins) + - Try out different inheritance types + - Try out polymorphism + - **Design patterns** (optional) + - What are design patterns? + - What are some examples? + - Discuss a couple of each type + - Creational + - Structural + - Behavioural + - Direct at resources for other patterns but highlight limitation. + +--- +# Class manipulation so far + +We have discussed: + +- How to create new object types with classes. +- How to edit the properties of classes around: + - creation + - destruction + - copying + - movement +- How to build containers with classes with a specific purpose +- How to use templates so that classes can be used in a generic way. + +--- +# Whats left? + +Can we combine classes to make new classes? + +- Inheritance +- Multi-level Inheritance +- Multiple Inheritance +- Access specifiers +- Polymorphism +- ... also lots more but we will stop here. + +--- +# Inheritance + +In C++ it is possible to inherit methods and attributes from another class. + +There are two types of classes when we talk about inheritance. + +- Derived or child classes: A class that inherits from another. +- Base or parent classes: A class that is inherited from. + +``` +example code +``` + +--- +# Multi-level inheritance + +"Its turtles all the way down." - classes + +We can have classes that inherit from classes that are have already inherited from a base class. + +``` +Example code +``` + +--- +# Multiple inheritance + +Why limit yourself to one class when you can really confuse yourself... + +Classes can also be derived from more than one base class. + +``` +example code +``` + +--- +# Inheritance and Access specifiers + +- Public: Everyone has access +- Private: Only this class has access +- Protected: Private but also accessible by members of a child class. + +``` +example code +``` + +--- +# Polymorphism + +Allows us to use inheritance to do different things in the derived class than specified in the base class with the same methods. + +``` +example code +``` + +--- +# Exercise + +Take the example complex class we wrote a couple of days ago and ... + + +- do inheritance: + - split complex up so that the contructors and data members live in the base class and a magnitude squared function is in a derived class +- do multi-level inheritance + - Now in a third class write a absolute magnitude function and have this class inherit from the previous +- do multiple inheritance + - Take new copy of your complex class + - Write a magnitude class + - Write an angle class + - Write a class that represents complex number in polar form that inherits from complex, angle and magnitude +- Polymorphism + - Write a base class with a function called weatherforecast that prints a message. "rain" + - Write a second + + + + + +--- +# Design patterns + +Great we have ways to combine classes together now what? + +What are some of the common ways of combining classes to generate certain effects in code? + +Design patterns provide us with a library of code orginisations to draw from to acheive different effects + +--- +# Types of design pattern + +- Creational + - "These patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code." +- Structural + - "These patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient." +- Behavoural + - "These patterns are concerned with algorithms and the assignment of responsibilities between objects." + +Nice resource for this: https://refactoring.guru/design-patterns/catalog + +--- +# Creational Patterns + + +- Factory +- Abstract factory +- Builder +- Singleton +- Prototype + +--- +# Structural + +- Adapter +- Bridge +- Composite +- Decorator +- Facade +- Flyweight +- Proxy + +--- +# Behavourial + +- Chain of Responsibility +- Command +- Iterator +- Mediator +- Memento +- Observer +- State +- Strategy +- Template Method +- Visitor + diff --git a/lectures/Combining-classes/index.html b/lectures/Combining-classes/index.html new file mode 100644 index 0000000..a7be3d2 --- /dev/null +++ b/lectures/Combining-classes/index.html @@ -0,0 +1,11 @@ + + + +Combining Classes + + + + + + + \ No newline at end of file diff --git a/lectures/RAII-cont/README.md b/lectures/RAII-cont/README.md new file mode 100644 index 0000000..ad4af14 --- /dev/null +++ b/lectures/RAII-cont/README.md @@ -0,0 +1,403 @@ +template: titleslide + +# RAII continued +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk + + +--- +template: titleslide + +# RAII Rules Recap + +--- + +# The Rule of Five + +This says that if you define or delete one of the following: +- copy constructor +- copy assignment operator +- move constructor +- move assignment operator +- destructor + +then you should probably do so for all five. + +??? +This can be quite a lot of work! + +--- +# The Rule of Zero + +This says that unless your class is solely deals with ownership, then +it should define none of the five special functions. + +This is really a corollary of the general software engineering +"principle of single responsibility". + +You should split your code into a resource manager with all five +functions and a user class that has none, but uses the resource +manager as one or more data members. + +??? + +If it does deal with ownership then rule of 5 applies :( + +--- + +# Standard library & special pointers to the rescue! + +The standard library contains some help: + +`std::unique_ptr` is a pointer that uniquely owns the object(s) it +points to. + +Pointers can be moved but *not* copied - this is achieved by the copy +constructor and copy assignment operators being `delete`d: + +``` +class unique_ptr { + unique_ptr(unique_ptr const &) = delete; + unique_ptr& operator=(unique_ptr const &) = delete; +}; +``` +??? + +Ownership means that it will destroy them when it is destroyed + +Obviously there would be a lot more code in the implementation + +The syntax is basically the same as defaulting a special function + +--- +# Using std::unique_ptr + +```C++ +#include + +class Image { + Image(std::string const& file) { + // construct by reading from file... + } +}; + +std::unique_ptr ReadImage(std::string const& filename) { + return std::make_unique(filename); +} + +int main() { + auto img_ptr = ReadImage("cats.jpg"); +} +``` + +??? + +What's going on + +Include the memory header + +Some type `Image` that has a constructor to read it from a file + +Instead of constructing it in the usual way, we pass the constructor +arguments to `make_unique` and it will be allocated on the heap for us + +We return the value and because of that, the compiler knows it is +moveable so we move into the local variable img_ptr + +When this goes out of scope and is destroyed, it will destroy the +object that is pointed-to + +--- +# Using pointers + +```C++ +class Image { + int x() const { + //return x_size; + } + int y() const { + //return y_size; + } +}; +std::unique_ptr ReadImage(std::string const& filename) { + return std::make_unique(filename); +} + +int main() { + auto img_ptr = ReadImage("cats.jpg"); + Image& img_ref = *img_ptr; + + auto area = img_ref.x() * img_ref.y(); +} +``` + +??? + +We didn't do anything with that pointer. Let's imagine it has some +member functions that return the size in pixels and we want to compute +the area + +We can dereference the pointer with `operator*` + +Returns a reference to the thing-that-is-pointed-to which we can use as normal + +--- +# Using pointers + +```C++ +class Image { + int x() const { + //return x_size; + } + int y() const { + //return y_size; + } +}; +std::unique_ptr ReadImage(std::string const& filename) { + return std::make_unique(filename); +} + +int main() { + auto img_ptr = ReadImage("cats.jpg"); + + + auto area = (*img_ptr).x() * (*img_ptr).y(); +} +``` + +??? +Don't have to bind a name but this syntax looks rather ugly + +--- +# Using pointers + +```C++ +class Image { + int x() const { + //return x_size; + } + int y() const { + //return y_size; + } +}; +std::unique_ptr ReadImage(std::string const& filename) { + return std::make_unique(filename); +} + +int main() { + auto img_ptr = ReadImage("cats.jpg"); + + + auto area = img_ptr->x() * img_ref->y(); +} +``` + +??? + +Can use the pointer member access operator -> as a much more readable shorthand + +--- +# std::shared_ptr + +Sometimes you want to be able to safely share an object between many +users. + +Enter `std::shared_ptr` + +This keeps count of how many pointers are alive: increasing the count +on copy and decreasing on destruction. + +When this reaches zero the object is destroyed. + +```C++ +int main() { + auto shared = std::make_shared(); + + auto foo = LongLivedObject{shared}; + complicated_function(foo, shared); +} +``` + +![:thumb](Prefer unique_ptr unless you really need to share) + +??? + +Why not keen on shared_ptr? + +More complex, destruction no longer deterministic + +2 other annoying problems with solutions + +--- +# Niggles with shared_ptr 1 + +Sometimes you want your class to be able to get a `shared_ptr` to +itself (e.g. to create some other object that depends on it) + +```C++ +class Widget : public std::enable_shared_from_this { +public: + std::shared_ptr self() { + return shared_from_this(); + } +}; +``` + +You must ensure that a `shared_ptr` has been made before calling +shared_from_this! + +??? + +Ensure `shared_ptr` has been made first by e.g. making constructors +private and calling them from a factory function that returns a +`shared_ptr` + +--- +# Niggles with shared_ptr 2 + +Cycles: + +```C++ +class Cowboy { + using ptr = std::shared_ptr; + std::string name; + std::shared_ptr partner; +public: + Cowboy(std::string const& n) : name(n) {} + ~Cowboy() { std::cout << "Delete " << name << std::endl; } + friend void partner_up(ptr a, ptr b) { + a->partner = b; b->partner = a; + } +}; + +int main() { + auto good = std::make_shared("Alice"); + auto bad = std::make_shared("Bob"); + //ugly + partner_up(good, bad); +} +``` +??? + +Show the code in `sample/shared.cpp` - same as above but instrumented. +Compile and run and note no call of destructor! + +The way to break cycles is to use `std::weak_ptr` which doesn't count +towards the reference count. + +To use a `weak_ptr`, you must call `lock` member function which +returns a `shared_ptr` that ensures the object lives long enough to be +used. + +--- +# Raw pointers + +Now despite having been mean about pointers, there are some valid +uses - in function interfaces + +A function can perfectly well accept a raw pointer as long as it +doesn't want to change the lifetime of the object that is pointed-to + +![:thumb](Raw pointers do not own resources - use a smart pointer for that) + +![:thumb](Raw pointers represent a single object - use a span for a +contiguous range of objects) + +??? + +C++20 has `std::span` but you can use the guideline support library +if, like most of us, not there yet + +--- +# Special pointers Exercise + +- Use a shared pointer +- User a unique pointer +- Test which is which +- Do something simple + +??? + + +--- +# Other resources + +For example files - compare Python to C++ +.columns[ +.col[ +```Python +with open(filename) as f: + data = f.read() + +# file is closed on exiting the block +``` +] +.col[ +```C++ +std::string data; +{ + auto f = std::fstream{filename}; + f >> data; +} // file closed by destructor + +``` +] +] +??? + +Python with statements are opt-in + +Compare to C# using statements (types must implement the `IDisposable` +interface - ugh MS Hungarian notation) + +--- +# RAII for files + +```C++ +class File { +private: + std::unique_ptr handle = nullptr; +public: + File() = default; + File(std::string const& fn, char const* mode) : + handle{std::fopen(fn.c_str(), mode)} { + } + ~File() { + if (handle) { + std::fclose(handle.get()); + } + } + // Read/write member functions +}; +int main() { + auto f = File{"data.dat", "r"}; +} +``` + +??? + +`nullptr` - special value to indicate an invalid pointer + +private constructor + factory static member function + +d'tor closes the file if it has a value + +C++ destructors technically are also opt-in - but they really are the +single best feature of the language! + +Please use them! + +Could also have a network connection, handle to a GPU command stream +etc wrapped here. + +--- +# Exercise + +We are now going to look at bring together what we have learnt around classes, resources, RAII and special pointers in an exercise. + +The aim of this exercise is to write a new container class that has a simple vector-like interface but a memory efficent data access pattern under the hood. + +Instructions can be found here: + +archer2-cpp/exercises/morton-order/instructions.md + diff --git a/lectures/RAII-cont/index.html b/lectures/RAII-cont/index.html new file mode 100644 index 0000000..bdcc5ee --- /dev/null +++ b/lectures/RAII-cont/index.html @@ -0,0 +1,11 @@ + + + +Resource Management + + + + + + + \ No newline at end of file diff --git a/lectures/RAII-cont/mem_layout.jpg b/lectures/RAII-cont/mem_layout.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef3304462f290b850cc3c59772fe0d8478a19c7e GIT binary patch literal 29477 zcmeFZ2Ut_h)+ijhAjQx-5~>uD5+D={RR~2&=s^iZ2}QaH2r4}krFW1X5&{B-62U_6 zO7BXMUIZy3BHX;i^Pcm4_kQPm-h2P&fBye$9+Ju4vu0+mwPvlERpxa3bQW+!_>WykTsprK6`wd;kE@oC6RqQsSqZyN{Q#w%U0MORMw0-TQgj z*?W6jyKzJB=kNdgeJ}zbwgdnSN&M$$|MU)xgQK@SvDPv1Kgiz0%Lf1;@h6r=@A-KA zR(?h-Gb3DnFTeP$>_r?P@qXuT<=cOhr+=^WM|u1AI;O_z#J3^`0M5|f{<|{b@5+Dn zORNq6UGwsBc5?JTZ*tek=boLH<9YAjFhRuC`5Rt$ot#}Af2;N9_1~)e6X-vD`#Aa& z-wgmzzw2?|%lY;lpY#7|1TvSTr2&7@V%O+*9Fws2{&nxKM*q5Zw*UZ8+y?;2p8jk4%_)cUMCWRm2Ql#x`CG?2U{=^_~*nIM@XSta>Gazsi-N=wR2%0ntl zDn+VDdW{rHYDx+xy-nbBIG8c&)|T0mM(T1)ztw1;$rbe43Tbe{}Bc9x8ljGs)B zOp#2T%#h5A%!$m4ESL;MmP%GYRzXG}>mVB3s9&+wg*Iiq#P>WurD$7fQ{V9vZcGk9kC%y;T@)I!us)P~d!)c2{MP!~}X zsNYd9Qh%ob(umNg(wNd9X&%v}(^Sy3(M;3qo;`aObQW^f=&bYEhiB8zK0n)ecJAyq zT1HwiT6J0*T3_0D+EUs#v=g+ubaZsWbXV!D=zQqn>B{I@=swUL(lgOtq}QQ$pbw$X zqOYYNq~BtoW)Nby#$d}3z>vyN!|;w_;~e!l;dAQe?9K(B%R1L^ZtUDXkO?RaGz7W< zV}Tf87jT7iw2j7fk=oyndll&OfRjcJjYl39dVhZ)Hn!;E9@ zXWnLEWRYVrWASInVrgQTWhG-3X4PSJWqrb0%{t0@#KyyRjm?oQnyrFuknJlw7yDIq zM|Ko@CHn~b5yyEBO%4~11dck68BTIeF-{}Sdz`tPZJZli%v{P`_FO2gYOYCcQf@JB zW9~riLhfGfeI8yOZ5|Jv44xLA4PI8>tGr0wWZqZ2t9(p+m-(FelKEcqt(|8%f9<^M z`SkOx=ePN}`LFZ)^5^sS^M414fMB4qWhbk1y6;+>#QM zvXV-W>X16Q1is{Ysp!&_G^4bx^kZp)^cNX18GD&5nPFKvSuNQR*?QR>IdM5hxm>w% zFcVlG90_g)|BzRZ_mZ!WUsVuPxTTP-Fs8_?ctbH(u@gc8QG!EBsgNt`uCEzbbIm;cCg%L*_{y_bW2APJo21=t}lTj06DE_JCK!_gQ#I2krc*Fcm@ll^jKR#ttjz4I zxrTY7`K*PM#RH2TOHNB?%X%v+D>JJSt9@%t>lEun8?a4;%_v+1?ho&><+OFRZMw;D z)9z-?Es9&_w=lPk?F{V-?Di2lh-}2Ry}Esx{kp?dhh&FUM^(o}$7Lr~r$ncf+p4#d zZm-_Cawql9hO@eJrt=Q+Ix-LW)y2@I)aBIG+_lo}jN2`@26skx7x&h?ym$TY4tR)r zM0m`4DtV@OZhJw!O1w$D;okK=%tWQw>nrLT?mOpq*)PlQ@SfSd8h@a_yMOO}vHQ{Y zmjko{iUY|5?E{;G_=6q=%{{pGAU~KS7!lkYA`lW5viMN@A^H)`BbP^gk1st=e7qNG z9!dz~340i}5Uw4Lji8V4iWrMjj?9Z9k8+OckCu(jh(19%pt@r&#iYg@$J)ns$4SSf z$DPJI#rHpvf0Fx@@~QjN(S$1r=tN**K;lA@eo}4n`D9e`Udqjsu2i|yyfm6L-?Z6u zz4ZDF!HlOF$C-CBN3ztiDzmw>qqD!}IOGiGUd^q@5(zqUZQAhnRP z(64Z*$gHTNSh2XQgtH{3Yi*ZXfg-fXS991|tKbu+i}`o3Yt(hw+sO*NL4;zsc`Y52q=nQ8VXeQa^BiDEN5cW96*;Y}1^^ zT<^Te{M3T|!ulugPsfX4OLR-A%Y4gaD>5sMt6Hn?*R0nT*FDz1Z$xYYH?y}yw_bd{ z`nh-8VtaANYv=S!>@LS{>7LwP^Zt$f4+m}s-@l>`IS$d^6u)&GnIA2GzjsV^oc`m& z55fubWct+Y^z?KJpbj`gNl8UXafYawsHxA;&@$7}o;^#;!N|nG%)`mY%frddeO~aQ z$a#>205`X&!Uc&-(sFWgd?FB4MH!WgvT`z~Zvbb{kY6S0Y!V>gv=zYp+d`9!sPund zB>ukCpdzQBBqcjTLVRj@4nRUmPDV-rq@*MvC8eMvA^s*KC;p^joiW`}l?>6ji@yy=DwcN*O#OVD~6-z>{4~P*zy`x@nCNSi=P2;OOl)zb9WNLCm9AXecV=zp< zk_xz>w^}~8Dgiet)YZ{ZkE0gK-jpArS;4tX_?m0NhB4SGrp(bPFXfmZtl24`UCIE$ z-Ifc(-kf7A<&H>BhSpgZ35@dMy{N@F&0k&(0-@ zFF31Hz?mk+Pu{He&2S-Mw%J~g7!|D9>*jyA_`iuq<=^p0q|6=967sfmEb#wp@+ z`Czh`tM}RQ8Dqh(H~NTM4`~JF_b>$;X!R-J6&K@?{QqHsHLs_}fnO@2|Y&+g@UyvB#~lu(TPBQDZm5n{be!FD;RTyG)+?6ehl{ zFQvZERY?^Lt@b?q0}|3}7keRHIC{hiqit~!H7*fJl9>lw_pOE-F(Dv+7;k^qm^?L1 zb%S~$=56W|Qrb_#?UD4Ca{_qq7=q2G$yD2rI=9pnHvsu}iCx)bTTI9oLzBZN6sLee ze|W(DDd2$1<<3dt;_q8L0Qo6ESvkS{!ObQ9%<@;8#&JtGg2^v12BMuF0;tlnQ2$f1 zYSa{7!kTjSMsO7M%cOTT_{2!xu*PO~G;D9ABC$X@Y)YTzJ^3u_>fxRtZ<-8?NbuvT z7U^V4X~iKPReAwCKXa|71W*HzJ8pcbfv2i#e7Az~!O)syo0o^3*;46*X~1PVmJ07g zc%KVD-l`Ak8<=unigX*!@=Lbxp}qa6dP`D-Uca>!X5Qa2YBHnvJmbxV8I+vvy$-8PIaA2cvOry$N1U4Wp;o>i75d8?`qHexfIzr%?c6N}3F>ZM0O!_~J?5gzvqe-`VI_ zrVI(7o&7b%JojBywGIn+RaFN?XA7{uXMu!P@m7a8XDbX@>M*sgVJ_UJEtc)hRdoF& zr0mi*?V?t`VOn}9uVAZ|hk>i0>0Cuq+(a?#rPb8hkRw5yAkm65$vajzLBO1p@`N{XwZ;l|smJjb)65IyZKpoIAHQwOpM^o#TnQU29A4v5V{UA1wRk_uSyEr&jz%vc z3oYWbo#FD4JOjCp!J zdy!V5qX3Zr=oyrBiBDJR_y|G$rxjGIoaE>Z~0Y?f}Jr zfl>|{ulLk>3nS1l|IKuak@L{NJfF+ZH^OX;gv3{8ch|ACoC*VEFk<_U$Yc}D{nrAE*Njb6dW|+L6ZjtcS-qlQ-pIY z(_EVj)XeE$`eLpd^=zXrFh0ENtI1n7cPY_~v~vERnsY!scseV+y|F_b`(OwjreHyY)5nLr(KL@5WZWl5T8P z(BAL#_%Kx<2FKJv3kpsF-k=P4ky*WMG0friA(uzCnO%a9?ZK-QYulc8vnI<%tuMqh z{CY;jO;_2skgCs@N;0Usr!a6}cZ^Fx@hPAHE9W-xlIGC=#KuJ-ISe3^{(gicb>B+FZv^p3r3xq=&MeVx~ zy~@T}ks|yzi~IUCUPWCJ;cpErM`4``W?ZANKVrL6ta7x{T-ywS&2-6fV_kL9`@%+i zQ(RGt8chji9|!bsWiczpWKQ zu(4gRu=xq~TIbVw!)BNCPu5~$;uWZv>UKw&m)CI~O}d!QciZxFisdb!m8Q)HVzo_a zd2dQowj^3!Ow=cO?;qI|4J*5Z@_KPA)UKe?khsAW^y}%)5_4yk3brzsCC3TnvmzP|k zqmiAP^+;TjesV!O>go6t1_bY^x`>JX(Ut0sXoQ$_AGK!3pD)(eYn}D^V{+bkKjJ!U zjZ0t&srvlC5Pnfj3}**+obJza&`jrXrAD3~@4J2G&@VucT=OLhTSm{O#Gwm?-3D)s z>a|i&18`x)iO==S7atA2r;ZM9-K9q7bgR%AqxIey+&I)JORtRMICfV;2ByGz93CXD zVSC-SM`>EdRh8&G_kjh$^tSWIM}_VF4Z!jTXXXV4~u%QLn+=Z01fo!_tubjKge24 z6-+)qA?nNSSwwd%WNlF@uD2!Zsnv-zD^EHK6YT_H%IVpZUde!mKHs;cFiX*%-+d4E z@bn+Py*fOC-0&V@Ltxz0?V>%*c-zlPj;!6|nkO9cEo(%U!tm}8sRxqF+nmu7g>Qx@ z!(Qy=Fob5NqB`H-Y+tBI!y8%Enoqc*vzI zuM_c4*IW+$lA>2#^vgS|-HmLXyk9iK+GKZ+fsL#hSA9&!G1=XvlAWoxhfSYrc=!kV z=a8!Ev~_fSI@`&qYLOH|hvvl@bXMj1*9o=Gcc*Hn3|tZH;vkVAV+3zm4}*i-kDDt^JHOI%Sk6j$2{3Kx3rj=-zZ~ZK=1vzQ&f(EHXvSDZneT zSzoU$=+3(Z&mog(Oog?;MjFi4{%*u`Gv2rBvcsJBm=~-R?=EXZWA*nwH@LYOENy3E zN=#=nM~o50SFYJFE%8-|Pw+~}dA}MN>;S>Tzq!U}hySC0Iz6WI;yj0DSrSE8$c^88z&spHzT zFiYdY0ZUTPax;1HLJd^8=JF7(JF0fVXUfrZ33j>u36bX|&@H(Y#)s3riaHdP3qZSx zC=5E)J^?cQYKS+Q$ny-aS(vkFu#_s9ITexTgLCxFDmqk+{KQIzWp;SUTG$B)+JH@A z?rDEoGZ~OZkb*O;DaGOw3_4@PdO(=U(RWQHV#`gp z3{r)h^g_1TZz!7|^ks~;c`_YuW9WqVTU|hScT}afxl86^jbF$jW^>9}M6w}#fuRAq z=Qt*U881CI7q#CAHMUImHuBMteHhj!fxtk%B80Zqi&Nx1?k#!h{yD5)F%wd8WgPl? zUWAVn*)`7z+chu9bUyVr-+uaC(1j__B!MMOzbSKfS88E0D7HC4@=ddMHm%z^6kTkoxr5CUM1pDLrmkd0AYvR|Y|4;B=BkND-UlR;Jp?_sM{)GMi6{#te?ce(JEDnmZ zDzlHh;)H;~6)-JKsS*fl6ZP6XWt-zqto#xelI?0Sc064QiL%60w?3?f`B#=!i>ZY; zGE})zXe?_(?3y)UP(Oq5yxXgP`h|XJrqELz#Vz$Zh}cFV&3BWTo9%9Ky>_+LjdIR2Y26Ph1oye z_t$tg$>{@pUgd#t$-WXIg;H0GX|w$%u!>b+1niFQD(Q7>NIiO;4><*tO4uEyFzhb) zXu4e3hTkI{6l3mgMEY0&ofr{h#k^l3+&HZ!o-!QNG8Uio`R0b`v;Un@F(EIDl=t1P zJ>TNF3?6iJ-iRZ;8H%_QP)mwg(-?lawS2Sggy#AmJ^UK)=MH*=8$*?_^%${hR<0Xi zikDz~_pqDPEG)GX?i8?)<4yc`NxAcXbn#0hCV4ZslcO7`i1k+!1jkm%Yf8fRY* z^D1JgNrT~zJG?>OB40m0{G*FslX~QnPa?!+8aAV(1@EgeXJ7))ze%5CCwz!|h{`nb z6<%l<+BgL~PW+<_z^}6Hg%pL%_^_RNDes;0F94v=9iy0aaJnpr9UmdtdiZT z;~yVM$%wb<5QiD>0Ns4H`3Gi$+KcSBREIO1$8DgYbBaUkQ|lux&fc%)cc(VM?1f4m zOH8dMK04pTigYsEI6smygAr@?wyChFnBGs{X&X7Rm?gX|;`EBUP3^S9FRn%^A-Onb zM3-v2inpw1RuBC3^o(*8y{YFf5Co4?j0JmZ42{t z-(NITwD!D2QABfu7fz6vvR&&?HoT%-p65vPeC)N~uTpz65+&DNRIDZ8U%i>`Qfyb% zH(25AO2{2)8h?HJVQcfZp-5>bP;$X_9T_u@RvYaY~^wOS|%cy*6h4{NiQ!;|NzX}+c z4G?`i+ZP5FV%v*u@d}JMJC8Ts3!6Q}vajD63S9FJKJvb~35ljX1vJ;}Q#&h~9?ff{ zqdN6%B|0W-(9z>nYm3WENU_RsRO#(F=^SO!!Slmw9>N80VlGYi)mp}RCv=Y~{Uj|t z+ex8E$Q=G%XXs!uw>iqe!<{e268$zSMv86|QESCo2k&usEd#!SO<~G_i~w(c+~*2C z1<+LlYw#v-A^rPvbl*JP@$qLeD1Ajv9Tw4!N35c7TE!`yX&ZyzgH)F3P60;sSGFgY zKV4hZrIj!d;-kDM=D<;LKG7g|_TyO%I32RV1X`X}?P@*YCni=FOC zGUUUe=Sp}kF7;O7yc#&i;BpOJE~=M5Qa7IhhTR#CuI0OO}gO&ca5p)#EdL2X%busAhDY%nB`HnLfEo%5+h4wM-tHh417P^Y=%u=L}F|~ zk`=k+N-7NL!?6!K2uYI4XQMrWL^$oa9t7arF5a8A4SW!h1ibCX(E?uWR{0cXx64?m zrQrkdD}=em)L2hqvhHs>E))F?6~VN9C{{?hR*CVV*rmeeG@q6nFkPH`vbVLv;8)45 zz3(c*!lM1zkgy%*8OC5;q5;5KtL{xWodqF%tT+V$+J#(h`5Y!2$gp6wZ0tPfYhl98 zRIv8s;tZ=>rLTQXXSNane}puJ zP%9NdGIGzVFEmHQ>PuVvoZ^1}j)wWKqhWrkq5pR)!2jfGBWsTrn@6{UpsVEH7wjnsixs<5W%J$Df3XNHHP= z>PW$79K19?9HiCStr=y$fXui+VnbjO65EmBE`s&7t>CTEFWDiIvy7BaUKS zjZkv~+rxzejt&TY8TdPcK7Op#qM3^Zq3K8Bft3lOB)hjh(?%-iZu7A2N7L=&cQkQc zT4Qu~DL{n-?3-tSEKzN~wFSxu+pd}p)6|iwuw%Wz_|@uUYV!|t!g(^wdeV@5u{UP8 zTwHMJ<$i0i0?S&nl`ok5Ht$gMp+P?ENqA0+k{~1weNZHIJO%~=>!LQ)<4zv2C}YZ1{TJDfd49QhT1;M{xG#rV<-R;6ZMCVWs| zr80Vi6q~D`#_R|#j=CGY;>qDVQ5I9hazFEvsqNjd^M4TPzi%2Lb;)H3FIr6gBp2lG zSvs$t6mbZ3f%`Ua*BP`~bl~rQ#*9{@dk$5d9Aar=if1)rX!2OgfX~gf>HG-dM*X!1 zadfci$x$-p z)F-DSzoqrIc&P^n%nMqedO&cA@{KgN%+!(g#l2Co`6MtmoYW5=EntXuEK2W8$m3m` z5)*E3>JQBE2~0h@UwdHPn3VB|zACn=s8!M9{PnKOf;tdI(a}QNWlhrv)H;~+scYOx z8D6r^GP%AhS4=O z14g#X^1Z|rv8kr@2El4A&!(zHRuKvqT1|R>UM)}G(Wf|qWkr4E`5*n&u9F{ z)PHC(Z1Maph@_>mk$(Zsu+M&_-uG*mic9`UA=OvMqY3>U`R_J~Ark*L@afsIv?Sh=qEcC|(>YILI^1hOrNPehbY68dj_zj| z3GbJ`g_3t|@uw3*$!`)$ufS~ES`pzyw|0%n4sE!*VO)>;Q&*>&upnhsMpDoWDyDbw zAURi~`fJRCc`tEUe7b;gKrYquKeL^mjuTS#6aPobvvNk@GJH!|XlIVZ4~uP65R2Ow$R z_hvsiXFSrx^dj!)I}E?Pv@ks}UX@>x1x6sfi81a*iXu`nQt~bLHE}~bJRl0I6zP^O z?UA=_@0XB+i^vTxP|2|AWJD#!f|8$7kyhEzd4076WkHj#YB>~y5m8RslpEP-FS6_9(`()J8hV#d=3#U8 zd{qpvQ4kz8bG<@CKdP>}VyFf{mY&_7yvlvgu-WLAK29wd?!>Q(yGtIY5moE8)^rL; z-Ey$?Ihj#}ME5!yBy{*33$c<<4vQ>~BA6yex33RhkGXvj`|+FmlIF7E=1{1PG)nYK zdVvYOcD5O8X|DVZ<1y2iHIN%aNRTU8Xc!~fSwnwI7yoMwP}XxYa_9F9i4Lbrt%#DSMKi*UN@p@aFJ-0?Y zcRYmdP4<9JX6Y-r`@wWExRvp>m@*S%cZH1Lci-f8{qs2bmsP!~jc%WpH>!R*%0=8< z;}SF-SbQ9yNYtm{PcAHOWh>b6f{bL9Ythe7T-4rIt-**J znkdxMd66H}z4(exvZ9B%HQ?OwP`ZxnF`bmsYB}m)y6F71aT;tHlkw-5Il&(f<=8{c zEliulNpokO$!E0X*4Alpy^bDtjdYmf{voMv2-CvHp8^_g%*^WP+v@p7 zT{08Oi!{!DYVYJ&%74{W&__rM&K*|`niK?UR8PQuBo@dm3cHM#GMm(BT^l(eHH%}F zQNPU;_QR_)NpwY_S`4XGQ!`msdQ>*_Ne`z*57*V}9X@PLeh_4A5`)h+*Smv0>63qL zygAj^8}Slg->YNNjam$LOIQaxTcV#j+FeaSHuMy*hMB2r&7hYtgejkoZz4yPy_L+V z+S;miOp8R{7mwyM#xSOiwm8V0Al_QU9JiV}^>}}Ds{9@sbTb+>|9P$!*&t2wRG_z- zQPZ9UM#zvG6)Qd{5UV|ya+zFr$(0TkEw@vNd%L@*(|4Hh!lAx-Y}rUpfQ3S+?#&5EFV`x-T-UTdR`O=UykYW21|c z$vn7>Rh)Evq3BjS67DzSj>&`!bP~SvzBOH0Q>=Iyz0-QY*zA4j!nX5!ys5am&H z-uFqDjF|&9yxj@573QDlOO=c zXKr*9;JfOqCCUOzuP8@(=90Dh2c%izbcr#88!e6lTgk5FmiLfa7(7Sy$6mx{(;mi8 zWxXE}V89=JtTrf=j|ip-a59!~hP>jZn>L`?3BPV7bthHB3kh#ZIcbiJU8^Tt6ezaq zl_C|)avHA^UI#kla57_!+@r?aoL)+=;Jn;Md&F#4-}TRP-+s80%Ue9^Vq71g_VUt% zPhc^ZI4gWI0fW&J(4vtN-Y(IQY|*od?a;G_&sxi) zS-E&PGuzd)Awb(Ldidzwa36e+!*`(h#$^_<@;Jl8XDu5E>5)8x#-5H&ELlbg#@sC_ zX-hB-FWc!JA2=GFkskOelr=z%uSHCn;Q=Mno~dEP9lNa~TFVu52rasse&v4Fs_7|U zzL!?@ZT0e@x;;y|`h@u*d>rBE)KAb2LpAfl1^x}N(8 z%zA>wCTheBH^8EQ`=m4E6!7}%PUeHp{^G;IM$3+w_3go$PcKDqMJli{jihJO^c;wj z64RJ&mKk?q;sW3-as`lXT=mV;x9svzGP`=8Xwd`#KPW3g`9R_hCJMD~+Xp3@ARq~h zYgk`P7#lU%`!4ICm_=fhW=?MU!k|QnJ^?Obr#irk^>xcF*9KWaQb1NKsInTgU68C$ zy1#oCI9eL!S4V$0A}uY(D3nLYj5i8XWpv=piPP}wtI)#LjypE4i)M7hub8a9d1vZ0 z>TZcSnAp(Ph>FVcYp~>~&@imx3A8vgG!qj0*v7-koporyFV1KnK44^3#mIcGf#@2} zvI5_A)CPCCg};gUKiFD?qiva48&TDVf)JJ%u$V5HfSTiF(A6ihOpje&Y-bbK0Vzfo zGwrrMRvX-AH$pShj=q(VVWDm~8Z(`JRVodUTiKb8VPzw07CR6{&s>j+iYeyA_M@82 z_&+~u4n{?9_wgw;8RJm91>Teztu>}d(_~7mu9y>UQF?sJ-l)Zgmfbeo2SZH9CxJVTS+b}XBBmjYIx?uAhmJrQfNDCrM zZ}n%OMXW|iufIt5_MDZr=!^z#&lH;-!0Z!XQob72lk4ujj1~;ELu)U(V~d5J3a=)v zONt(71PCg+RIHZ5va!vouzpRLxfo`;H?4W^zMhdn)T|CxY}aFu_I^CeIxZ?sLtl(w z;@Q}B5Ym}WtkGz6A!?xuyY}VjC@pn7%W6IDp!jD>lk49Gbrje=uEFlVT*_G(Eb^cxe0N0!ny?n;PtB>&|)YiF9+5Zxs+7U2LvP zTq#_B_wBM5yIQDTI<`HzRGQBs;MD_=>HrrH%m(Y%00XjFAAmq^Dt6^8kOg~I>!`e|3 zDh4V}bKWjkETMa*B>PE}5q;QaraG{%4HqMZ@G5HhC7U&LqEiWysmuN5;@d$fyoI`{ zg?AFs+crox&e%o~%1yn=N`j@?cNVFGjD0;|q>DwcZs?8D?cuJA3^8x!3KX;=#JMH} z5_+64B_k%J|0I0+GNYyTqcS|T^Qsab6wfENP&?jp$Z142Z3z+M%%QVH&Tc-tS6WIp(Jye56m)* zl8dUzXJp`x#k`$QG$QGIXXMsuQv9_s4V@UwBFs7m(vMI9M(j96nysD_o>#Ay7sKYa zYCtClMwxM(OW6qbB7TzGL5*P*MUh3jDFdZO_?OIeFesp@xzHhODF1Jv3uwZW0 zG$}Q>UOybel`v_9!5m;9-RRL7n7J7-Kd^vrves&jbGk8%CXSQa$z)dE%Fn(MyLt58Kq`?Rv}PwGMHx7o`bG@=gO2eE--L^og>p+Iuew@xX2 z_r6Q$ZS>#^bbSC!a51lf$qpIu$uAj+X~M*NVZe1T*hn#pJ33OwFd?mEsJKXof7@&D zeFh~HSZvka2w3JjS*u~-E%ATPDDv-MwSP zi@lWv_L(Cxc=2#yyCFw`R}36^M7f;;XE3g1;vI%*!UJ*~mNYa?wJm4Nj26mGE<8cp zn%rx>zZl0Vr%*oK+!ig)$z)Sevqi`xu*iKFt1c*+vGpW6iRY)gb6VWno^HqS>LMgq zJ6uLkUz^S4x1vxZFH_fxZAJ?~-VIFV(6q#faDz7eP~J)g;H|@VG04ommiYLMKsIUE zI*`r82sMM#G~fNqJqqdoXIp2O)VqyW^8yRI^fVujZw6G^9_lX53;?a_yk>l`nZp_e z_dv*1uP@e@%Err{+mFo6OYm{*xS3eidk)MzK`eDtAM@#!I z*!lG*tfabEY(`%%sR!CbI}Ue918vHB^A0BT^w7noB~H?;cl1r>dOHH0@V^jKxUG|4vz%xyjH*&5JGEqLy;d#sMfHvju!TVNiU&t1ZYOT@yLur*2m8(d8vM z%=%*S%O@q;GG%(``G665;{d-TNq!do+G#${_z~@9r!eynX=`kjS&rXphqP~mx)k=! z-I{TfmJ96&Nc(zaj0nq4q8$s?cY@9;=0U{<5avoq-6DFqBQrRb<}-UYF#4|^CvH7z zQ6+FOim77hH?+lEv|Qk*O(k4#m(&r_F!I~=Usz>-I~U@A7PCr8p_^n--cxH^$}g@= zQmI|b-OJjBRO%*X8;aR>mxZjAaE~&#B=u0m%^jI`kWfEAzdd;4xc-!Toox?hn0Q$EqZasa6q4x=${BF|;{oU!f8| zZ0S<mvEa_Vytf;$v7T@1tk;!i*&YjjMTr#3z;Ipi( z^7`*jx&Q#EcD`f0_x}jdj{K(~+P!}V9YxFzr%Kx@%g4BvNfHkc3HsY1BF;iPn!IYzIK6PXojfqfvACSfQ3^|4(~C;*xAKrXctWs1 z(N(N*iQ;|Hqk2UGcb}CF4j!*zI*H1#^@W&C!%R&7W_U!SM@;|zUmZ6?jpuD@S$=gse_uG| zY>deS+mq(hAqCdWN8_)Az6&1&jh+JVugJ`yy(;!m(po|-!zg_v!M4l3?;V#GY6XO5 z)$HfNW=e%AHeZQ(UpNf{HZ21a@tNEn*=E8tx?LfXl9-Ac@@cybG%r_t4`Zr^VkRfk zU&2_7LBh#qWv#vD$bF>Wovy(Pihj^2%{Uq_S;vp`g1t>8MqA#Q*a`&%3RMej7+aS8 zGK#DeNPtl{j59Z&95{o9EzgK`G!4iRWLjNY%&W|>#9dzFW>en@dwccXaY`g=$@z=1 z$82-#L06}m#UzjocniGwa!09r_2&B`4Er&*IAd7}65*dYw*Vwq9vbkK7aM_2}%_1~5jcS3~ zQk2DiI}xF!3b9>bI=6wr)uh;r2}f>PgH~Ltj3QstmsrO`}|Y2Z@f! z?_R8y9Fag{eN3Tq^8XiFHJEIQTd!eX9alJ`V^V0?(^EA3X+-n$^&6aJ4aH~_c0$e6 z8#Z-N+h)3>+2Q+5?}60g9Y-0Rnq7|^UNf&OU=PKH7Ph=R#33tz@$0gNzWhc!ACvFQ zDd5I%ywvdJGEa*p{jnAgm7AvkpgZSG8g(grZ=>mTnR>hbheDm!8R*37F?&uX+%-=3itO;f}1+);QH$Q#wMHi%wo zo!qR9d@u#VloXGroFg8t`}eso|AN5|Kpw(y?>*w3ruw0y){T4Wy&rXnXHv1=`kSGx z?A_eEANP;mjqp&?eZHBo2ZPSXIb(19*LB^5MW~}OoOYd189tglqTP4o)$m=_ z88=ka`t8Lnn)Qp3EJ-FdemGSYCy}L*i&Y@0ALvDW1EgE^OC&b?CD>B9-YPsQCzJg^ zEh~ENz3`~7a1OQO`938C(?{S&fIu2T03lDbfvf{rd!A35Jx~6!peMk@ys(nA`?S;_ z>4O!uG83zM{}BjQvj$6y&h(E*`z~o-$uQ@rzF(I9Vj?-3(o1EnPc7^K!ty>*!nP-K z^o0d7m)T1U&X9xfvb}v+)Uti6)x0(3X^`k@v5ER9vwu|9{qJmeKmQ;FNkJE}1$jfT zENhlb!ufvf982_#+zt?dQ8Q-3+iEn1&ZIeB6PmwUZUF5)1u&-v)3T!u6RR1*dE_v6 z0-B!yneJXe98|nxh*)w+#5Dbwe_JS(Rdm_x2Y=3d+4S(f@p0yNlVaJUE$~W-(|1XD%8Ws0=NPIw`jH*h4U* z$|f^TmS}WZRO4D>XNcf7TuGZ}SlU%|*X2fDnvQ?Dch*}0BmUya%)23rbvfoaA$83e zX?s?t?ZT+BK0R4rQ-4l4C&;-bRk)-^7i`29?&Xcyb(3DXf7h_(5Q)8-h8Y#Uuupvt zS`I0TXcC(I6acXUfq@vph&f`$h@nvC!^3(?Ub=LL`zRkC7aGCaV0-nz>}4011a#CV zb6l;rtEnkWvpJzVfVnXZSY}}6V(f~PA;%lsAK-@;NcWyta}}crHs}~fvk4Z0>8?of zi)cN88!go4xVDo1?VCMuG98`|fQp8T!f}$ro0~D-N=<2UvD%@l=?HhH`-yTN&OQoa zs{$#9Mm(P?gJEK-%nmdre>Jq0c1WEzGbr(M0Em z&6&HL$l=+FWZZple}N$)G^$(?B30}NSP0jk;ol!6~TRj>hGndVxFTaPI#-hYRLp9 zgw?@_qiIuW3vjWC0SKEL_19T*bWAlsn+TMj=|_UC9j(PKw&6E1oza0pu>tO8?=M(n zW<@y`yz~4fYKUmq;oOq>TDad9iydG@v{K?ei*i`SW6pO7Eyu^~$Te zMra&-A93_)VO_C~n&Y)icuV4uOI1(wyC*5cnM^!sUs!Hk5u)BOzbpW6x7`b_uS&5l z+u`{i?VWj06K5aCt@ReLfpUr{;cB=vK!5~Mkbp=a7)ZE+sNt&NkcL17R4OVY3c(;D z5U^?x$U)GAHy{xxNCYH+a)cuY3d*4Zma2&0(G+#2edp~l&f8A^aA)?}nVsEdc7D&Z z&$Ih|zR%~m4#h+lG?Pf=;2Yk?tggANuo?ptw>l1^tng^4wrKyN5;TAlJ+c%t>!~d5KEWnh7qftt{EhyfPg2)!rLI47<#z9@ z4o5bjNm5wIYSwODci5h`yx@RQ0;EiIAU3Nf@kp&oF?h^yQ;XzAZl;>H#utpsWWcnG zFas{)a33KU7^E(Ug0!troAKt_OsV%J2u;{|hR}%;2Ewg<4 z7~9v^zYd zdqD!TvIgJk?xP+uMOhtu2haQBzRiA;NgZVhV;cQpaaP z29f8|F6{%ik62nvr7l1Kc533(7r>eUfX)L9rk@|MTTs3p8C(rgF4=CfG$>4=Yjo(0 zDsfv|1hx4Ths~eJ5k1{ICHU51u@pW^(L5SjEZqZo< zA{@@H(}V`WMyRsL;l}r(yoV3`k;?qPtW56;=w14ozn~8UfcLhAMW?AR-%A~ohW9%Ud)$exc7!-lZdx}I zTLpIBvWyglIzm=~A#C3Mntf#|VN;`ykIDDVgSHQjYb5xUCFv3GXDRF-#Z^sabWvQv zX|z-00vjas(`EliK%lK*N_%iHr4?-S_|r99AKLNyh%5@#%DLJHviVS%$0=5|q&40@ zxqQt#!mpv6@LRWtR))@(-TyD=;jgO9uWAf(?=1HnGlFflXmok+P5z5eWVy8Uo{D^P z`r)${o{_yA@(=&g#_qSrfhu7p!Kp89aU%iq8I`tnsyCVm5#`kJ%h06h*DCOxr zV{2Y|~rr4PwxLHs^C zPR_Oei3a^DHp?=`u!y-A{xv{ZZ$l^=3$-?*scz@zta<}(Hz)}M+W^Qc&VxFZJ=w;&*}AS_;8tZu#|6cv*`$zjz0oC~jR9?Nc>%@|6V06dsH>lBJDTmlbv#m~*Mj zjOdIR%c6o64@^**{I=ZC0+J}+zn#1sfRWpeMZnmRef`yn0eNiSa&x{8z6h+2Fc@l{ z$SL{aVaMh6Jcz#rPtggrO=-Yy}}BhTw?_9#KPx;fn&ExKr-V2H*GD3*Y*Xj?63Qx5 zn8S0=Y$;tE!b`GD4t2+oD0qDvuxWRsp|FL5jof3*DL$`gQRQ^WP^aMLgsNMCEq_xz z%e|Rd?p+}A(UYF)6>4+)^<;xz<$K8}3reoY(H;#lhSSkgL^iy5d8LsHvmty1-E$OX zy2=N^5eAlFPb;%J1vGIH+I znFw3AME%!ZCc~}h`-S)Fzhj(JHf9P>E6&s86eqX{2xP*>Ct?;O{Hylt69eb}!IzhR zav0}F_W}aU4GV|0pNlP}RqY@%WydQ4ZxmTmYhyk5^%(A + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + + // Copy constructor + my_array(my_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + delete[] data; + size = other.size; + data = new double[size]; + std::cout << "Copy assigning: " << data << std::endl; + std::copy(other.data, other.data + size, data); + return *this; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + + // Copy constructor + my_array(my_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + delete[] data; + size = other.size; + data = new double[size]; + std::cout << "Copy assigning: " << data << std::endl; + std::copy(other.data, other.data + size, data); + return *this; + } + + // Move constructor + my_array(my_array&& other) noexcept : size(other.size), data(other.data) { + std::cout << "Move construct: " << data << std::endl; + other.size = 0; + other.data = nullptr; + } + + // Move assignment operator + my_array& operator=(my_array&& other) noexcept { + std::swap(size, other.size); + std::swap(data, other.data); + std::cout << "Move assign: " << data << std::endl; + return *this; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class dyn_array { + unsigned size = 0; + double* data = nullptr; +public: + dyn_array() = default; + dyn_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + ~dyn_array() { + std::cout << "Destroying: " < + +class dyn_array { + unsigned size = 0; + double* data = nullptr; +public: + dyn_array() = default; + dyn_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + dyn_array(dyn_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + dyn_array& operator=(dyn_array const& other) { + std::cout << "Destroying: " < + +class dyn_array { + unsigned size = 0; + double* data = nullptr; +public: + dyn_array() = default; + dyn_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + dyn_array(dyn_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + dyn_array& operator=(dyn_array const& other) { + std::cout << "Destroying: " < +#include +#include + +class Cowboy { + using ptr = std::shared_ptr; + std::string name; + std::weak_ptr partner; +public: + + Cowboy(std::string const& n) : name(n) { + std::cout << name << std::endl; + } + ~Cowboy() { std::cout << "Delete " << name << std::endl; } + friend void partner_up(ptr a, ptr b) { + std::cout << "BFFs: " << a->name << " & " << b->name << std::endl; + a->partner = b; b->partner = a; + } +}; + +int main() { + auto good = std::make_shared("Alice"); + auto bad = std::make_shared("Bob"); + //ugly + partner_up(good, bad); +} diff --git a/lectures/RAII/README.md b/lectures/RAII/README.md new file mode 100644 index 0000000..190f7c6 --- /dev/null +++ b/lectures/RAII/README.md @@ -0,0 +1,402 @@ +template: titleslide + +# RAII +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk + +--- +template: titleslide +# Reminder Classes and Constructors + +--- +# Classes + +- User defined types. + +- can be defined with either the `class` or `struct` keyword. + +```C++ +struct Complex { + double re; + double im; +}; +``` + +- Creating trivial types - give the class name then list the values to be assigned to the +members, _in order_, inside braces: + +```C++ +Complex mk_imaginary_unit() { + return Complex{0, 1}; +} +``` +This is called aggregate initialisation. + +Alternatively: +```C++ +Complex mk_imaginary_unit() { + Complex sqrt_m1; // Values are uninitialised + sqrt_m1.re = 0; + sqrt_m1.im = 1; + return sqrt_m1; +} +``` + +--- +# Constructors + +Often you want to control the creation of instances of your classes. + +You do this with _constructors_ - these are special member "functions" +with the same name as the type. + +```C++ +struct Complex { + Complex() = default; + Complex(double re); + Complex(double re, double im); + double re = 0.0; + double im = 0.0; +}; +``` + +Note we declare three: +- one that initialises with a purely real value +- one that initialises with a real and imaginary value +- a *default constructor* which needs no arguments (that we tell the + compiler to generate for us as before with `= default` ) + +??? + +Control in more detail than just starting from a default value or +having to provide *all* the of member values. + +Constructors are not strictly functions in C++ but very nearly (next slide) + +Why do you have to "explictly default the default constructor"? + +Because the language rules say if the user provides any constructors, +the compiler must not create one unless asked to... + +--- +# Constructors + +- Constructors are not directy callable +- Constructors do not return a value +- Constructors can do initialisation of member variables before the body begins execution + + +Let's define the ones we declared just now: + +```C++ +Complex::Complex(double real) : re{real} { +} + +Complex::Complex(double real, double imag) : re{real}, im{imag} { +} +``` + +--- +# Destructors + +You can also control what happens when your objects reach the end of +their lifetime. + +When this happens is deterministic: +- when a local variable goes out of scope +- when an object that contains them is destroyed +- when the programmer `delete`s them + +For a class `Name` they are declared like: + +```C++ +struct Name { + ~Name(); +}; +``` + +It's important to note that you should **never call this directly** - +the compiler will call it for you when your objects are deallocated. + +??? + +Note the tilde syntax is the logical negation of the class. Cf +annihilation operators for any physicists. + + +--- +# Resource allocation is instantiation + +A very important pattern in C++ is **RAII**: resource allocation is +instantiation. + +Also known as constructor acquires, destructor releases (CADRe). + +This odd name is trying to communicate that any resource you have +should be tied to the lifetime of an object. + +So the when the compiler destroys your object it will release the +resource (e.g. memory). + +??? + +Saying that in some philosophical sense allocating a resource is the +creation of something, which implies its destruction later. + +--- +# RAII example + +A very simple copy of `std::vector`: + +```C++ +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + explicit my_array(unsigned n) : size(n), data(new double[size]) {} + ~my_array() { + delete[] data; + } + double& operator[](unsigned i) { + return data[i]; + } +}; +``` + +??? + +This class allocates some memory to store `n` doubles when constructed + +When it reaches the end of its life the destructor returns the memory +to the OS + +It allows users to access elements (with no bounds checking) + +--- +# What happens when we compile and run? + +Add a few annotations to print in the contructor/destructor + +??? + +Open sample/arr1.cpp +Compile and run + +What happens if we copy x? + +Add `auto x_cp = x;` (same as `auto x_cp = my_array{x};`) + +--- +# Copying + +When you value assign an object in C++ this will only be valid if +there is a *copy constructor* or *copy assignment operator* + +-- + +Copy constructor - when you create a new object as the destination: + +```C++ +my_array x{10}; // Direct initialisation +my_array y{x}; // Direct initialisation +my_array z = x; // Copy initialization +``` + +-- +Copy assignment - when you assign a new value to an existing object + +```C++ +my_array x{10}; +x = my_array{2000}; +``` + +??? + +What's the diff? + +In the last case, you have to deal with releasing any resources held +by the target object + +--- +# Implicit copy + +The compiler will automatically generate these operations for us if +all the data members of you class are copyable. + +So what went wrong with the example shown? + +-- +A pointer is just a number and so it can be copied implicitly - hence the double delete + +If we want to copy our array then we need to either: +- copy the data (aka deep copy) +- share the data and somehow keep track of when the last reference to it is destroyed (aka shallow copy) + +??? + +Deep copies are more expensive at run time but somewhat safer + +Shallow copies can be faster but harder to implement correctly and can have thread safety issues + +Do we want to copy? + +--- +# User-defined copy + +Of course, you can control how your objects are copied + + +```C++ +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + explicit my_array(unsigned n) : size(n) data(new double[size]) {} + my_array(my_array const& other) : size(other.size), data(new double[size]) { + // Copy data + } + my_array& operator=(my_array const& other) { + delete[] data; + size = other.size; + data = new double[size]; + // Copy data + return *this; + } + ~my_array() { + delete[] data; + } +}; +``` +??? + +Open arr2.cpp + +Note the signature + +--- +# Returning a value looks a lot like copying + +When a function returns a value, you might think that will copy it to the target: + +```C++ +std::vector ReadData() { + std::vector answer; + // Read it from somewhere + return answer; +} + +int main() { + auto data = ReadData(); +} +``` + +??? + +Thinking about std::vector examples we've seen and that you might have implemented + +Have previously said that you should use bare auto when you want a +copy - by that what we really mean is you want to *own* the object and +control its lifetime. + +Copying a vector of billions of elements is going to get expensive and +would be counter to C++'s zero overhead abstractions principle + +--- +# Move instead + +Since C++11, the language has supported the concept of *moving* from +objects which the compiler knows (or the programmer asserts) will not +be used any more. + +Examples are: +- temporaries (i.e. the result of a function call/constructor expression) +- automatic variables that are going out of scope +- the result of calling `std::move` on an object + +The destination object "steals" the contained resources from the +source object and sets the source to a valid but undefined state - +typically the only operations you can perform on a moved-from object +are destruction and assignment. + +--- +# Move implementation + +Going back to our simple array: +```C++ +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + // c'tors, copy assignment, d'tor + my_array(my_array&& other) noexcept : size(other.size), data(other.data) { + other.size = 0; + other.data = nullptr; + } + my_array& operator=(my_array&& other) noexcept { + std::swap(size, other.size); + std::swap(data, other.data); + } +}; +``` + +??? + +Comment on `noexcept` - this is for STL compatibility. The containers +will copy if your move operations are not noexcept. These ones cannot +throw exceptions so this is safe. + + +Look at arr3.cpp + +--- +# The Rule of Five + +This says that if you define or delete one of the following: +- copy constructor +- copy assignment operator +- move constructor +- move assignment operator +- destructor + +then you should probably do so for all five. + +??? +This can be quite a lot of work! + +--- +# The Rule of Zero + +This says that unless your class is solely deals with ownership, then +it should define none of the five special functions. + +This is really a corollary of the general software engineering +"principle of single responsibility". + +You should split your code into a resource manager with all five +functions and a user class that has none, but uses the resource +manager as one or more data members. + +??? + +If it does deal with ownership then rule of 5 applies :( + +--- + +# Exercise + +Time to try this out for yourself: + +- Try writing your own simple my_vector class: + + - Make sure you define a constructor and descructor for the my_vector class. + - Write a simple test program that uses the my_vector. + - Add in the ability to copy the your new my_vector class. + - Update you test program so it copies an instance of your new my_vector. + - Add the ability to move an instance of your my_vector class. + - Update the example program to include a move of the my_vector your have implemented. + - Can you add a member function to calculate the magnitude of my_vector? + +N.B. Add print statements to each function call in my_vector so you can see when each of the class members are called. + diff --git a/lectures/RAII/index.html b/lectures/RAII/index.html new file mode 100644 index 0000000..bdcc5ee --- /dev/null +++ b/lectures/RAII/index.html @@ -0,0 +1,11 @@ + + + +Resource Management + + + + + + + \ No newline at end of file diff --git a/lectures/RAII/mem_layout.jpg b/lectures/RAII/mem_layout.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef3304462f290b850cc3c59772fe0d8478a19c7e GIT binary patch literal 29477 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 + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + + // Copy constructor + my_array(my_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + delete[] data; + size = other.size; + data = new double[size]; + std::cout << "Copy assigning: " << data << std::endl; + std::copy(other.data, other.data + size, data); + return *this; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + + // Copy constructor + my_array(my_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + delete[] data; + size = other.size; + data = new double[size]; + std::cout << "Copy assigning: " << data << std::endl; + std::copy(other.data, other.data + size, data); + return *this; + } + + // Move constructor + my_array(my_array&& other) noexcept : size(other.size), data(other.data) { + std::cout << "Move construct: " << data << std::endl; + other.size = 0; + other.data = nullptr; + } + + // Move assignment operator + my_array& operator=(my_array&& other) noexcept { + std::swap(size, other.size); + std::swap(data, other.data); + std::cout << "Move assign: " << data << std::endl; + return *this; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class dyn_array { + unsigned size = 0; + double* data = nullptr; +public: + dyn_array() = default; + dyn_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + ~dyn_array() { + std::cout << "Destroying: " < + +class dyn_array { + unsigned size = 0; + double* data = nullptr; +public: + dyn_array() = default; + dyn_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + dyn_array(dyn_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + dyn_array& operator=(dyn_array const& other) { + std::cout << "Destroying: " < + +class dyn_array { + unsigned size = 0; + double* data = nullptr; +public: + dyn_array() = default; + dyn_array(unsigned n) : size(n), data(new double[n]) { + std::cout << "Constructing: " << data << std::endl; + } + dyn_array(dyn_array const& other) : size(other.size), data(new double[size]) { + std::cout << "Copy constructing: " << data << std::endl; + std::copy(other.data, other.data + size, data); + } + dyn_array& operator=(dyn_array const& other) { + std::cout << "Destroying: " < +#include +#include + +class Cowboy { + using ptr = std::shared_ptr; + std::string name; + std::weak_ptr partner; +public: + + Cowboy(std::string const& n) : name(n) { + std::cout << name << std::endl; + } + ~Cowboy() { std::cout << "Delete " << name << std::endl; } + friend void partner_up(ptr a, ptr b) { + std::cout << "BFFs: " << a->name << " & " << b->name << std::endl; + a->partner = b; b->partner = a; + } +}; + +int main() { + auto good = std::make_shared("Alice"); + auto bad = std::make_shared("Bob"); + //ugly + partner_up(good, bad); +} diff --git a/lectures/README.md b/lectures/README.md index 8e80894..94fc752 100644 --- a/lectures/README.md +++ b/lectures/README.md @@ -1,10 +1,14 @@ # Lectures +* [Course introduction](course-intro) * [A brief introduction to C++](cpp-intro) * [Class types](classes) * [Loops, containers, and iterators](loops-containers) * [Managing resources](resources) +* [RAII](RAII) * [Templates for generic programming](templates) +* [RAII continued](RAII-cont) +* [Combining Classes](Inheritance) * [Algorithms, lambdas and traits](algorithms-lambdas-traits) * [Linear algebra with Eigen](eigen) * [Threads with C++](threads-1) and [Further topics with threads](threads-2) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md new file mode 100644 index 0000000..effefcf --- /dev/null +++ b/lectures/course-intro/README.md @@ -0,0 +1,177 @@ + + +# Course Introduction +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk + +--- + +template: titleslide +# Time table + +--- +# Day 1 + +- 9:30 - 9:45 : Introduction to this course + +- 9:45 – 10:45 : Introduction + - Introduction to C++ + - Exercise: + - Archer2 log in & Hello World + - Notes on C++ + - Variables + - Types + - Selection + - Functions + - Exercise + - Simple chat exchange extension to Hello World + +- 10:45 – 11:00 : Coffee break + +- 11:00 – 12:30 : Class types + - Classes + - Creating instances + - Member functions + - Exercise + - Write simple version of the complex class + - Constants + - References + - C++ compilation + - Exercise + - Debug an implementation of complex + +- 12:30 – 1:30 : Lunch + +- 11:00 – 12:30 : Class types + - Classes + - Creating instances + - Member functions + - Exercise + - Write simple version of the complex class + - Constants + - References + - C++ compilation + - Exercise + - Debug an implementation of complex + +- 12:30 – 1:30 : Lunch + +- 1:30 – 2:30 : loops, containers, and iterators + - Containers + - Loops + - Iterators + - Exercise + - Containers exercise + +- 2:30 - 3:00 : Managing resources + - Resources + - Exercise: 20 mins + - Pointers basics practical + +- 3:00 – 3:15 : Coffee + +- 3:15 - 4:00 : Time to work on exercises from the day & ask questions + +--- +# Day 2 + +- 9:30 - 9:45 : Recap previous day + +- 9:45 – 10:45 : Managing resources Continued (RAII) + - RAII + - Copying + - Moving + - Rules + - Exercise + - Implement your own container class for an array with the rule of 5 + +- 10:45 – 11:00 : Coffee + +- 11:00 – 12:00 : Templates for generic programming + - Templates + - Templates in practice + - Exercise + - Add templating to example code. + +- 12:00 - 1:00 : Lunch + +- 1:00 - 3:00 : RAII continued + - Recap previous RAII lecture: + - Special pointers + - Exercise (30 mins) + - Simple examples using special pointers + - Other resources + - Other resources + - RAII for files + - Exercise (1 hr) + - Morton ordering + +- 3:00 - 3:15 : Coffee + +- 3:15 - 4:00 : Time to work on exercises from the day & ask questions + +--- +# Day 3 + +- 9:30-9:45: Recap the previous day + +- 9:45 - 10:45 : Combining classes + - Inheritance + - Example + - Try out different inheritance types + - Try out polymorphism + - Design patterns (optional) taught material + +- 10:45 - 11:00 : coffee + +- 11:00 – 12:00 : Algorithms, lambdas, and traits + - Recap + - Iterators + - Standard algorithms library + - Lambda functions + - Performance + - Traits + - Exercise + - Algorithms exercise + +- 12:00 - 1:00 : Lunch + +- 1:00 – 2:00 : Linear algebra with Eigen + - Eigen + - Matrices + - Arrays + - Solvers + - Exercise + - Diffusion equation sparse + - Diffusion equation three ways + +- 2:00 - 2:15 : Coffee + +- 2:15 – 3:00 : Threads with C++ + - Threads + - Synchronisation + - Exercise + - Try out implementing different locks and guards + +- Epilogue: + - More ARCHER2 courses + - Upcoming courses + - Access to ARCHER2 + - Follow up post course + - Any questions + +- 3:05 - 4:00 : Time to work on exercises from the day & ask questions + +--- + +### Access to ARCHER2 + +- SAFE: + +- SAFE DoCS: + +- Project: + +- Machine account: + + diff --git a/lectures/course-intro/index.html b/lectures/course-intro/index.html new file mode 100644 index 0000000..e7a0665 --- /dev/null +++ b/lectures/course-intro/index.html @@ -0,0 +1,10 @@ + + + +Course Introduction + + + + + + diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index 50da58f..4c927ea 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -12,7 +12,7 @@ template: titleslide --- # Assumptions -- You are a decent programmer in another language +- You have some experience in another language - You know how to use the shell @@ -434,6 +434,37 @@ Partially fixed in C++20 and further in C++23 Find a library e.g. Boost, if you need to do serious text handling because Unicode is super complicated +--- +template: titleslide +# Conditionals + +--- +# If + +- If example + +- possible conditions + +--- +# Combining conditions + +--- +# if and else + +- If and else example + +- else if example + +--- +# Short hand if + +- example of a short hand if + +--- +# Switch + +- example of a switch + --- template: titleslide # Functions @@ -482,7 +513,7 @@ int sum(int a, int b) { } ``` -Since C++11, we can alse use a _trailing return type_ (aka "east end functions"): +Since C++11, we can also use a _trailing return type_ (aka "east end functions"): ```C++ auto sum(int a, int b) -> int { return a + b; @@ -611,7 +642,7 @@ int main(int argc, char* argv[]) { std::cin >> name; // Have the program greet the user by name - say_hello(name); + say_hello(); return 0; } @@ -623,6 +654,10 @@ Change `say_hello` to accept the name it reads from the terminal, create a new message saying "Hello, $NAME!" and print it to standard output. +If you complete this try adding conditional responses based on the input name. + +Finally can you write a simple question and answer workflow where the program uses the answer from the user to ask subsequent questions? + ??? What I'd like you to do is change `say_hello` to accept the name it From 1023279d14a7ee0122ade82d9388694106de6134 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 22:10:19 +0000 Subject: [PATCH 38/91] update timeable --- lectures/course-intro/README.md | 104 +++----------------------------- 1 file changed, 7 insertions(+), 97 deletions(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index effefcf..e92f9b5 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -1,4 +1,4 @@ - +template: titleslide # Course Introduction ## James Richings, EPCC @@ -14,59 +14,17 @@ template: titleslide - 9:30 - 9:45 : Introduction to this course -- 9:45 – 10:45 : Introduction - - Introduction to C++ - - Exercise: - - Archer2 log in & Hello World - - Notes on C++ - - Variables - - Types - - Selection - - Functions - - Exercise - - Simple chat exchange extension to Hello World +- 9:45 – 10:45 : Introduction to C++ - 10:45 – 11:00 : Coffee break - 11:00 – 12:30 : Class types - - Classes - - Creating instances - - Member functions - - Exercise - - Write simple version of the complex class - - Constants - - References - - C++ compilation - - Exercise - - Debug an implementation of complex - -- 12:30 – 1:30 : Lunch - -- 11:00 – 12:30 : Class types - - Classes - - Creating instances - - Member functions - - Exercise - - Write simple version of the complex class - - Constants - - References - - C++ compilation - - Exercise - - Debug an implementation of complex - 12:30 – 1:30 : Lunch - 1:30 – 2:30 : loops, containers, and iterators - - Containers - - Loops - - Iterators - - Exercise - - Containers exercise - 2:30 - 3:00 : Managing resources - - Resources - - Exercise: 20 mins - - Pointers basics practical - 3:00 – 3:15 : Coffee @@ -78,33 +36,14 @@ template: titleslide - 9:30 - 9:45 : Recap previous day - 9:45 – 10:45 : Managing resources Continued (RAII) - - RAII - - Copying - - Moving - - Rules - - Exercise - - Implement your own container class for an array with the rule of 5 - 10:45 – 11:00 : Coffee - 11:00 – 12:00 : Templates for generic programming - - Templates - - Templates in practice - - Exercise - - Add templating to example code. - 12:00 - 1:00 : Lunch - 1:00 - 3:00 : RAII continued - - Recap previous RAII lecture: - - Special pointers - - Exercise (30 mins) - - Simple examples using special pointers - - Other resources - - Other resources - - RAII for files - - Exercise (1 hr) - - Morton ordering - 3:00 - 3:15 : Coffee @@ -116,49 +55,18 @@ template: titleslide - 9:30-9:45: Recap the previous day - 9:45 - 10:45 : Combining classes - - Inheritance - - Example - - Try out different inheritance types - - Try out polymorphism - - Design patterns (optional) taught material - 10:45 - 11:00 : coffee - 11:00 – 12:00 : Algorithms, lambdas, and traits - - Recap - - Iterators - - Standard algorithms library - - Lambda functions - - Performance - - Traits - - Exercise - - Algorithms exercise - 12:00 - 1:00 : Lunch - 1:00 – 2:00 : Linear algebra with Eigen - - Eigen - - Matrices - - Arrays - - Solvers - - Exercise - - Diffusion equation sparse - - Diffusion equation three ways - 2:00 - 2:15 : Coffee - 2:15 – 3:00 : Threads with C++ - - Threads - - Synchronisation - - Exercise - - Try out implementing different locks and guards - -- Epilogue: - - More ARCHER2 courses - - Upcoming courses - - Access to ARCHER2 - - Follow up post course - - Any questions - 3:05 - 4:00 : Time to work on exercises from the day & ask questions @@ -166,12 +74,14 @@ template: titleslide ### Access to ARCHER2 -- SAFE: +- SAFE: https://safe.epcc.ed.ac.uk/ -- SAFE DoCS: +- SAFE Docs: https://epcced.github.io/safe-docs/ -- Project: +- Project: ta198 - Machine account: + - Login to safe + - Login accounts -> Request a login account From d69ba22ce059f4034c2e49aaeb81cc2ef0aa5dfd Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 22:28:57 +0000 Subject: [PATCH 39/91] conditionals --- lectures/cpp-intro/README.md | 88 ++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index 4c927ea..49b6f89 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -441,30 +441,102 @@ template: titleslide --- # If -- If example +Use if to specify weather a block of code should be executed based on a condition. -- possible conditions +``` +if (condition) { + //block of code +} +``` + +Conditions include: + +- Equal to `a == b` +- Not equal to `a != b` +- Less than `a < b` +- Greater than `a > b` --- # Combining conditions +You can combine conditions together to make more complex logic: + +- and + +``` +if (condition A && condition B) { + //block of code +} +``` + +- or + +``` +if (condition A || condition B) { + //block of code +} +``` + --- # if and else -- If and else example +The `else` statement allows you to specify what happens if the condition is false: -- else if example +``` +if (condition) { + // block of code executes when condition true +} +else { + // block of code executes when condition false +} +``` --- -# Short hand if +# If we want more than else + +More complex control flow can be generated with else if: -- example of a short hand if +``` +if (condition 1) { + // block of code executes when condition 1 is true +} +else if (condition 2) { + // block of code executes when condition 2 is true +} +else if (condition 3) { + // block of code executes when condition 3 is true +} +else { + // block of code executes when conditions are false +} +``` --- -# Switch +# Shorthand if + +In some cases you might want to be more compact + +``` +variable = (condition) ? expression if True : expression if False; +``` -- example of a switch +--- +# Switch statement +``` +switch(expression) + case a: + // code block if expression = value a + break; + case b: + // code block if expression = value b + break; + . + . + . + default: + // default code block if no cases matched +``` --- template: titleslide # Functions From 26fbfb09cd72f99bcf7d9ce25720d79f1b0d4e85 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 22:34:40 +0000 Subject: [PATCH 40/91] formatting --- lectures/course-intro/README.md | 6 ++---- lectures/cpp-intro/README.md | 16 ++++++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index e92f9b5..3843f5a 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -5,9 +5,8 @@ template: titleslide ## j.richings@epcc.ed.ac.uk --- - template: titleslide -# Time table +# Timetable --- # Day 1 @@ -71,8 +70,7 @@ template: titleslide - 3:05 - 4:00 : Time to work on exercises from the day & ask questions --- - -### Access to ARCHER2 +# Access to ARCHER2 - SAFE: https://safe.epcc.ed.ac.uk/ diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index 49b6f89..d4a36f5 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -415,7 +415,7 @@ You have to `#include ` to use it which includes the "header file" that contains all the information the compiler needs to let you use it. -``` +```C++ #include #include @@ -443,7 +443,7 @@ template: titleslide Use if to specify weather a block of code should be executed based on a condition. -``` +```C++ if (condition) { //block of code } @@ -463,7 +463,7 @@ You can combine conditions together to make more complex logic: - and -``` +```C++ if (condition A && condition B) { //block of code } @@ -471,7 +471,7 @@ if (condition A && condition B) { - or -``` +```C++ if (condition A || condition B) { //block of code } @@ -482,7 +482,7 @@ if (condition A || condition B) { The `else` statement allows you to specify what happens if the condition is false: -``` +```C++ if (condition) { // block of code executes when condition true } @@ -496,7 +496,7 @@ else { More complex control flow can be generated with else if: -``` +```C++ if (condition 1) { // block of code executes when condition 1 is true } @@ -516,14 +516,14 @@ else { In some cases you might want to be more compact -``` +```C++ variable = (condition) ? expression if True : expression if False; ``` --- # Switch statement -``` +```C++ switch(expression) case a: // code block if expression = value a From 1e459e214ef3bd112ce48c7dfada35f19bbbc884 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 22:57:21 +0000 Subject: [PATCH 41/91] update formatting of exercise --- lectures/cpp-intro/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index d4a36f5..6d5b577 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -726,10 +726,6 @@ Change `say_hello` to accept the name it reads from the terminal, create a new message saying "Hello, $NAME!" and print it to standard output. -If you complete this try adding conditional responses based on the input name. - -Finally can you write a simple question and answer workflow where the program uses the answer from the user to ask subsequent questions? - ??? What I'd like you to do is change `say_hello` to accept the name it @@ -737,3 +733,7 @@ read from the terminal, create a new message saying "Hello, $NAME!" and print it to standard output. I've shown here how to read a string from standard input. + +If completing quickly try adding conditional responses based on the input name. + +Finally can you write a simple question and answer workflow where the program uses the answer from the user to ask subsequent questions? From 763f893d67d3da9dfefcdaeee72ea07a35115228 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 23:12:58 +0000 Subject: [PATCH 42/91] update operator introduction --- lectures/classes/README.md | 40 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/lectures/classes/README.md b/lectures/classes/README.md index 35fb15a..9574d90 100644 --- a/lectures/classes/README.md +++ b/lectures/classes/README.md @@ -479,7 +479,43 @@ All tests passed (36 assertions in 5 test cases) ``` --- -# More on operator overloading +template: titleslide +# Operators + +--- +# Operators are functions + +C++ operators, for the non-fundamental types, are just functions with odd +names, e.g.: +```C++ +std::string operator+(const std::string& a, const std::string& b); +``` + +You can then use the natural syntax when manipulating these in other +code: + +```C++ +std::string user_name = "alice"; +auto data_file = user_name + ".csv"; +``` + +??? + +Here, 'data_file' is given the type 'std::string' by the compiler using +"type inference" + +In general we'd recommend using auto quite a lot "Almost always auto" + +Why? + +Can't have an uninitialized variable + + +Add types - on RHS as constructors - when you need to ensure the type +of something (is known to the reader). + +--- +# Operator overloading Complex numbers have the usual arithmetic operations: `\(+-\times\div\)` @@ -515,7 +551,7 @@ internally. If anyone asks, references and `const` coming up --- -# More on operator overloading +# Operator overloading Complex numbers have the usual arithmetic operations (`\(+-\times\div\)`) and comparisons. From e63ba1c046f13fcb474cbdbdb0b0dfb1c6b98ef2 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 23:13:22 +0000 Subject: [PATCH 43/91] formatting --- lectures/course-intro/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index 3843f5a..dd11992 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -82,4 +82,6 @@ template: titleslide - Login to safe - Login accounts -> Request a login account +--- + From ed8420a41532f1f0f73710e7beff0ea1295f7453 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 23:13:34 +0000 Subject: [PATCH 44/91] formatting --- lectures/cpp-intro/README.md | 37 +----------------------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/lectures/cpp-intro/README.md b/lectures/cpp-intro/README.md index 6d5b577..59964eb 100644 --- a/lectures/cpp-intro/README.md +++ b/lectures/cpp-intro/README.md @@ -605,7 +605,7 @@ In this case, by us telling it later To use a function, or "call" it, you give its name and then provide the arguments in parentheses -``` +```C++ int main () { int x = 42; std::cout << "Total = " << sum(x, 100) << std::endl; @@ -661,41 +661,6 @@ std::cout << sum(d2, i1) << std::endl; std::cout << sum(name, file) << std::endl; ``` ---- -# Operators are functions - -C++ operators, for the non-fundamental types, are just functions with odd -names, e.g.: -```C++ -std::string operator+(const std::string& a, const std::string& b); -``` - -You can then use the natural syntax when manipulating these in other -code: - -```C++ -std::string user_name = "alice"; -auto data_file = user_name + ".csv"; -``` - -We'll come back to this! - -??? - -Here, 'data_file' is given the type 'std::string' by the compiler using -"type inference" - -In general we'd recommend using auto quite a lot "Almost always auto" - -Why? - -Can't have an uninitialized variable - - -Add types - on RHS as constructors - when you need to ensure the type -of something (is known to the reader). - - --- # Let's write some code From f4e1bf6456864c09a072fec8918e7298df996779 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 23:57:52 +0000 Subject: [PATCH 45/91] title update --- exercises/5-templates/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/5-templates/README.md b/exercises/5-templates/README.md index bf4b181..14ce830 100644 --- a/exercises/5-templates/README.md +++ b/exercises/5-templates/README.md @@ -1,4 +1,4 @@ -# Containers exercise +# Templates exercise In your clone of this repository, find the `5-templates` exercise. It contains two sub-directories `part1` and `part2`. From 6efdfc87da17175454fbf69663683eae6fa1f2a9 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 23:58:09 +0000 Subject: [PATCH 46/91] formatting --- lectures/course-intro/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index dd11992..c7e12aa 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -69,6 +69,7 @@ template: titleslide - 3:05 - 4:00 : Time to work on exercises from the day & ask questions + --- # Access to ARCHER2 @@ -82,6 +83,3 @@ template: titleslide - Login to safe - Login accounts -> Request a login account ---- - - From 6af5115acb7bbbdbb795c0f62edb5df20f05509b Mon Sep 17 00:00:00 2001 From: JPRichings Date: Mon, 10 Mar 2025 23:58:24 +0000 Subject: [PATCH 47/91] pointer games --- lectures/resources/README.md | 630 +---------------------------------- 1 file changed, 9 insertions(+), 621 deletions(-) diff --git a/lectures/resources/README.md b/lectures/resources/README.md index edf4862..1fbeb8e 100644 --- a/lectures/resources/README.md +++ b/lectures/resources/README.md @@ -157,632 +157,20 @@ and Valgrind But C++ has a design pattern that can tame this - first another feature of the language --- -# Destructors +# Pointers exercise -You can also control what happens when your objects reach the end of -their lifetime. +Write a new C++ code to test your understanding of pointers. -When this happens is deterministic: -- when a local variable goes out of scope -- when an object that contains them is destroyed -- when the programmer `delete`s them +Start with an int x and give it a value. -For a class `Name` they are declared like: +Create a pointer to x and show how to dereference the pointer and increment the value of x. -```C++ -struct Name { - ~Name(); -}; -``` - -It's important to note that you should **never call this directly** - -the compiler will call it for you when your objects are deallocated. - -??? - -Note the tilde syntax is the logical negation of the class. Cf -annihilation operators for any physicists. - - ---- -# Resource allocation is instantiation - -A very important pattern in C++ is **RAII**: resource allocation is -instantiation. - -Also known as constructor acquires, destructor releases (CADRe). - -This odd name is trying to communicate that any resource you have -should be tied to the lifetime of an object. - -So the when the compiler destroys your object it will release the -resource (e.g. memory). - -??? - -Saying that in some philosophical sense allocating a resource is the -creation of something, which implies its destruction later. - ---- -# RAII example - -A very simple copy of `std::vector`: - -```C++ -class my_array { - unsigned size = 0; - double* data = nullptr; -public: - my_array() = default; - explicit my_array(unsigned n) : size(n), data(new double[size]) {} - ~my_array() { - delete[] data; - } - double& operator[](unsigned i) { - return data[i]; - } -}; -``` - -??? - -This class allocates some memory to store `n` doubles when constructed - -When it reaches the end of its life the destructor returns the memory -to the OS - -It allows users to access elements (with no bounds checking) - ---- -# What happens when we compile and run? - -Add a few annotations to print in the contructor/destructor - -??? - -Open sample/arr1.cpp -Compile and run - -What happens if we copy x? - -Add `auto x_cp = x;` (same as `auto x_cp = my_array{x};`) - ---- -# Copying - -When you value assign an object in C++ this will only be valid if -there is a *copy constructor* or *copy assignment operator* - --- - -Copy constructor - when you create a new object as the destination: - -```C++ -my_array x{10}; // Direct initialisation -my_array y{x}; // Direct initialisation -my_array z = x; // Copy initialization -``` - --- -Copy assignment - when you assign a new value to an existing object - -```C++ -my_array x{10}; -x = my_array{2000}; -``` - -??? - -What's the diff? - -In the last case, you have to deal with releasing any resources held -by the target object - ---- -# Implicit copy - -The compiler will automatically generate these operations for us if -all the data members of you class are copyable. - -So what went wrong with the example shown? - --- -A pointer is just a number and so it can be copied implicitly - hence the double delete - -If we want to copy our array then we need to either: -- copy the data (aka deep copy) -- share the data and somehow keep track of when the last reference to it is destroyed (aka shallow copy) - -??? - -Deep copies are more expensive at run time but somewhat safer - -Shallow copies can be faster but harder to implement correctly and can have thread safety issues - -Do we want to copy? - ---- -# User-defined copy - -Of course, you can control how your objects are copied - - -```C++ -class my_array { - unsigned size = 0; - double* data = nullptr; -public: - my_array() = default; - explicit my_array(unsigned n) : size(n) data(new double[size]) {} - my_array(my_array const& other) : size(other.size), data(new double[size]) { - // Copy data - } - my_array& operator=(my_array const& other) { - delete[] data; - size = other.size; - data = new double[size]; - // Copy data - return *this; - } - ~my_array() { - delete[] data; - } -}; -``` -??? - -Open arr2.cpp - -Note the signature - ---- -# Returning a value looks a lot like copying - -When a function returns a value, you might think that will copy it to the target: - -```C++ -std::vector ReadData() { - std::vector answer; - // Read it from somewhere - return answer; -} - -int main() { - auto data = ReadData(); -} -``` - -??? - -Thinking about std::vector examples we've seen and that you might have implemented - -Have previously said that you should use bare auto when you want a -copy - by that what we really mean is you want to *own* the object and -control its lifetime. - -Copying a vector of billions of elements is going to get expensive and -would be counter to C++'s zero overhead abstractions principle - ---- -# Move instead - -Since C++11, the language has supported the concept of *moving* from -objects which the compiler knows (or the programmer asserts) will not -be used any more. - -Examples are: -- temporaries (i.e. the result of a function call/constructor expression) -- automatic variables that are going out of scope -- the result of calling `std::move` on an object - -The destination object "steals" the contained resources from the -source object and sets the source to a valid but undefined state - -typically the only operations you can perform on a moved-from object -are destruction and assignment. - ---- -# Move implementation - -Going back to our simple array: -```C++ -class my_array { - unsigned size = 0; - double* data = nullptr; -public: - // c'tors, copy assignment, d'tor - my_array(my_array&& other) noexcept : size(other.size), data(other.data) { - other.size = 0; - other.data = nullptr; - } - my_array& operator=(my_array&& other) noexcept { - std::swap(size, other.size); - std::swap(data, other.data); - } -}; -``` - -??? - -Comment on `noexcept` - this is for STL compatibility. The containers -will copy if your move operations are not noexcept. These ones cannot -throw exceptions so this is safe. - - -Look at arr3.cpp - ---- -# The Rule of Five - -This says that if you define or delete one of the following: -- copy constructor -- copy assignment operator -- move constructor -- move assignment operator -- destructor - -then you should probably do so for all five. - -??? -This can be quite a lot of work! - ---- -# The Rule of Zero - -This says that unless your class is solely deals with ownership, then -it should define none of the five special functions. - -This is really a corollary of the general software engineering -"principle of single responsibility". - -You should split your code into a resource manager with all five -functions and a user class that has none, but uses the resource -manager as one or more data members. - -??? - -If it does deal with ownership then rule of 5 applies :( - ---- -# Standard library to the rescue! - -The standard library contains some help: - -`std::unique_ptr` is a pointer that uniquely owns the object(s) it -points to. - -Pointers can be moved but *not* copied - this is achieved by the copy -constructor and copy assignment operators being `delete`d: - -``` -class unique_ptr { - unique_ptr(unique_ptr const &) = delete; - unique_ptr& operator=(unique_ptr const &) = delete; -}; -``` -??? - -Ownership means that it will destroy them when it is destroyed - -Obviously there would be a lot more code in the implementation - -The syntax is basically the same as defaulting a special function - ---- -# Using std::unique_ptr - -```C++ -#include - -class Image { - Image(std::string const& file) { - // construct by reading from file... - } -}; - -std::unique_ptr ReadImage(std::string const& filename) { - return std::make_unique(filename); -} - -int main() { - auto img_ptr = ReadImage("cats.jpg"); -} -``` - -??? - -What's going on - -Include the memory header - -Some type `Image` that has a constructor to read it from a file - -Instead of constructing it in the usual way, we pass the constructor -arguments to `make_unique` and it will be allocated on the heap for us - -We return the value and because of that, the compiler knows it is -moveable so we move into the local variable img_ptr - -When this goes out of scope and is destroyed, it will destroy the -object that is pointed-to - ---- -# Using pointers - -```C++ -class Image { - int x() const { - //return x_size; - } - int y() const { - //return y_size; - } -}; -std::unique_ptr ReadImage(std::string const& filename) { - return std::make_unique(filename); -} - -int main() { - auto img_ptr = ReadImage("cats.jpg"); - Image& img_ref = *img_ptr; - - auto area = img_ref.x() * img_ref.y(); -} -``` - -??? - -We didn't do anything with that pointer. Let's imagine it has some -member functions that return the size in pixels and we want to compute -the area - -We can dereference the pointer with `operator*` - -Returns a reference to the thing-that-is-pointed-to which we can use as normal - ---- -# Using pointers - -```C++ -class Image { - int x() const { - //return x_size; - } - int y() const { - //return y_size; - } -}; -std::unique_ptr ReadImage(std::string const& filename) { - return std::make_unique(filename); -} - -int main() { - auto img_ptr = ReadImage("cats.jpg"); - - - auto area = (*img_ptr).x() * (*img_ptr).y(); -} -``` - -??? -Don't have to bind a name but this syntax looks rather ugly - ---- -# Using pointers - -```C++ -class Image { - int x() const { - //return x_size; - } - int y() const { - //return y_size; - } -}; -std::unique_ptr ReadImage(std::string const& filename) { - return std::make_unique(filename); -} - -int main() { - auto img_ptr = ReadImage("cats.jpg"); - - - auto area = img_ptr->x() * img_ref->y(); -} -``` - -??? - -Can use the pointer member access operator -> as a much more readable shorthand - ---- -# std::shared_ptr - -Sometimes you want to be able to safely share an object between many -users. - -Enter `std::shared_ptr` - -This keeps count of how many pointers are alive: increasing the count -on copy and decreasing on destruction. - -When this reaches zero the object is destroyed. - -```C++ -int main() { - auto shared = std::make_shared(); - - auto foo = LongLivedObject{shared}; - complicated_function(foo, shared); -} -``` - -![:thumb](Prefer unique_ptr unless you really need to share) - -??? - -Why not keen on shared_ptr? - -More complex, destruction no longer deterministic - -2 other annoying problems with solutions - ---- -# Niggles with shared_ptr 1 - -Sometimes you want your class to be able to get a `shared_ptr` to -itself (e.g. to create some other object that depends on it) - -```C++ -class Widget : public std::enable_shared_from_this { -public: - std::shared_ptr self() { - return shared_from_this(); - } -}; -``` - -You must ensure that a `shared_ptr` has been made before calling -shared_from_this! - -??? - -Ensure `shared_ptr` has been made first by e.g. making constructors -private and calling them from a factory function that returns a -`shared_ptr` - ---- -# Niggles with shared_ptr 2 - -Cycles: - -```C++ -class Cowboy { - using ptr = std::shared_ptr; - std::string name; - std::shared_ptr partner; -public: - Cowboy(std::string const& n) : name(n) {} - ~Cowboy() { std::cout << "Delete " << name << std::endl; } - friend void partner_up(ptr a, ptr b) { - a->partner = b; b->partner = a; - } -}; - -int main() { - auto good = std::make_shared("Alice"); - auto bad = std::make_shared("Bob"); - //ugly - partner_up(good, bad); -} -``` -??? - -Show the code in `sample/shared.cpp` - same as above but instrumented. -Compile and run and note no call of destructor! - -The way to break cycles is to use `std::weak_ptr` which doesn't count -towards the reference count. - -To use a `weak_ptr`, you must call `lock` member function which -returns a `shared_ptr` that ensures the object lives long enough to be -used. - ---- -# Raw pointers - -Now despite having been mean about pointers, there are some valid -uses - in function interfaces - -A function can perfectly well accept a raw pointer as long as it -doesn't want to change the lifetime of the object that is pointed-to - -![:thumb](Raw pointers do not own resources - use a smart pointer for that) - -![:thumb](Raw pointers represent a single object - use a span for a -contiguous range of objects) - -??? - -C++20 has `std::span` but you can use the guideline support library -if, like most of us, not there yet - ---- -# Other resources - -For example files - compare Python to C++ -.columns[ -.col[ -```Python -with open(filename) as f: - data = f.read() - -# file is closed on exiting the block -``` -] -.col[ -```C++ -std::string data; -{ - auto f = std::fstream{filename}; - f >> data; -} // file closed by destructor - -``` -] -] -??? - -Python with statements are opt-in - -Compare to C# using statements (types must implement the `IDisposable` -interface - ugh MS Hungarian notation) - ---- -# RAII for files - -```C++ -class File { -private: - std::unique_ptr handle = nullptr; -public: - File() = default; - File(std::string const& fn, char const* mode) : - handle{std::fopen(fn.c_str(), mode)} { - } - ~File() { - if (handle) { - std::fclose(handle.get()); - } - } - // Read/write member functions -}; -int main() { - auto f = File{"data.dat", "r"}; -} -``` - -??? - -`nullptr` - special value to indicate an invalid pointer - -private constructor + factory static member function - -d'tor closes the file if it has a value - -C++ destructors technically are also opt-in - but they really are the -single best feature of the language! - -Please use them! - -Could also have a network connection, handle to a GPU command stream -etc wrapped here. - ---- -# Exercise +Make sure your print the location of the pointer and x in memory so you convince yourself no data movement has occurred. -Try out some of this with exercises/morton-order +Second create an double array y of 4 elements and give each element a value. -Instructions can be found here: +Create a pointer to the 0th and 1st members of the array. -archer2-cpp/exercises/morton-order/instructions.md +Incrementing the pointer to the 0th element of the array and show the pointer now pointers to the 1st element of the array. +Make sure to print the location of the pointer and array elements in memory to convince yourself no data movement has occurred. From d56c1bc935f0680b2306f5cd53a1658c47d4d1f2 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Tue, 11 Mar 2025 00:22:38 +0000 Subject: [PATCH 48/91] formatting --- lectures/RAII/README.md | 10 ---------- lectures/resources/README.md | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/lectures/RAII/README.md b/lectures/RAII/README.md index 190f7c6..9bc90ad 100644 --- a/lectures/RAII/README.md +++ b/lectures/RAII/README.md @@ -32,16 +32,6 @@ Complex mk_imaginary_unit() { ``` This is called aggregate initialisation. -Alternatively: -```C++ -Complex mk_imaginary_unit() { - Complex sqrt_m1; // Values are uninitialised - sqrt_m1.re = 0; - sqrt_m1.im = 1; - return sqrt_m1; -} -``` - --- # Constructors diff --git a/lectures/resources/README.md b/lectures/resources/README.md index 1fbeb8e..a6ed27f 100644 --- a/lectures/resources/README.md +++ b/lectures/resources/README.md @@ -169,8 +169,8 @@ Make sure your print the location of the pointer and x in memory so you convince Second create an double array y of 4 elements and give each element a value. -Create a pointer to the 0th and 1st members of the array. +Create two pointers one for each of the 0th and 1st members of the array. -Incrementing the pointer to the 0th element of the array and show the pointer now pointers to the 1st element of the array. +Incrementing the pointer to the 0th element of the array and show the pointer now points to the 1st element of the array. Make sure to print the location of the pointer and array elements in memory to convince yourself no data movement has occurred. From 562291ccf8019f288c966b4bef0438ed42a86ac4 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Tue, 11 Mar 2025 08:55:25 +0000 Subject: [PATCH 49/91] formatting --- lectures/course-intro/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index c7e12aa..fd669cb 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -70,7 +70,7 @@ template: titleslide - 3:05 - 4:00 : Time to work on exercises from the day & ask questions ---- +--- # Access to ARCHER2 - SAFE: https://safe.epcc.ed.ac.uk/ @@ -82,4 +82,9 @@ template: titleslide - Machine account: - Login to safe - Login accounts -> Request a login account + - Set user name + - Set ssh key + - Set multi-factor token + - ssh user@login.archer2.ac.uk + - Set password (used for recovery only) From 406c8a5c7d503656b523deba8bbb9e83cdda7b26 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Tue, 11 Mar 2025 09:18:59 +0000 Subject: [PATCH 50/91] Exercise README updates --- exercises/2.1-class-types/README.md | 2 ++ exercises/2.2-complex/README.md | 2 ++ exercises/3-containers/README.md | 27 +++++++++++++++++++++--- exercises/3-containers/instructions.md | 24 --------------------- exercises/3-containers/instructions.pdf | Bin 75709 -> 0 bytes exercises/5-templates/README.md | 2 +- exercises/README.md | 7 +++--- 7 files changed, 33 insertions(+), 31 deletions(-) delete mode 100644 exercises/3-containers/instructions.md delete mode 100644 exercises/3-containers/instructions.pdf diff --git a/exercises/2.1-class-types/README.md b/exercises/2.1-class-types/README.md index a9056a3..9760ac7 100644 --- a/exercises/2.1-class-types/README.md +++ b/exercises/2.1-class-types/README.md @@ -1,3 +1,5 @@ +# Class types exercise + In your clone of this repository, find the `2.1-class-types` exercise and list the files ``` diff --git a/exercises/2.2-complex/README.md b/exercises/2.2-complex/README.md index 6aa5885..9fe8a98 100644 --- a/exercises/2.2-complex/README.md +++ b/exercises/2.2-complex/README.md @@ -1,3 +1,5 @@ +# Complex class exercise + In your clone of this repository, find the `complex` exercise and list the files ``` diff --git a/exercises/3-containers/README.md b/exercises/3-containers/README.md index f8a502d..ecee831 100644 --- a/exercises/3-containers/README.md +++ b/exercises/3-containers/README.md @@ -1,3 +1,24 @@ -This document is available in multiple formats: -* [PDF](instructions.pdf) -* [Markdown](instructions.md) +# Containers exercise + +In your clone of this repository, find the `3-containers` exercise. It contains two sub-directories `part1` and `part2`. List +the files in `part1`: + +```bash +$ cd archer2-cpp/exercises/3-containers/part1 +$ ls +Makefile test.cpp vector_ex.cpp vector_ex.hpp +``` + +As before, `test.cpp` holds some basic unit tests and you can compile with `make`. + +## Part 1 +`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide the implementations + +## Part 2 +Copy `vector_ex.cpp`/`.hpp` from part 1 into the part 2 folder. + +Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), +a function (`AddWord`) that adds a string to a `std::map` as the key, the value +being the length of the string. + +You will want to find the documentatation for `map` on https://en.cppreference.com/ diff --git a/exercises/3-containers/instructions.md b/exercises/3-containers/instructions.md deleted file mode 100644 index ecee831..0000000 --- a/exercises/3-containers/instructions.md +++ /dev/null @@ -1,24 +0,0 @@ -# Containers exercise - -In your clone of this repository, find the `3-containers` exercise. It contains two sub-directories `part1` and `part2`. List -the files in `part1`: - -```bash -$ cd archer2-cpp/exercises/3-containers/part1 -$ ls -Makefile test.cpp vector_ex.cpp vector_ex.hpp -``` - -As before, `test.cpp` holds some basic unit tests and you can compile with `make`. - -## Part 1 -`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide the implementations - -## Part 2 -Copy `vector_ex.cpp`/`.hpp` from part 1 into the part 2 folder. - -Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), -a function (`AddWord`) that adds a string to a `std::map` as the key, the value -being the length of the string. - -You will want to find the documentatation for `map` on https://en.cppreference.com/ diff --git a/exercises/3-containers/instructions.pdf b/exercises/3-containers/instructions.pdf deleted file mode 100644 index 751e9e8c896071be732f08ecc849f1e15c1ab776..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75709 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�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

k(G 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 Date: Tue, 11 Mar 2025 09:27:42 +0000 Subject: [PATCH 51/91] Correct template exercise README --- exercises/5-templates/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/5-templates/README.md b/exercises/5-templates/README.md index 20f816d..a4744e8 100644 --- a/exercises/5-templates/README.md +++ b/exercises/5-templates/README.md @@ -20,7 +20,7 @@ Makefile sum.cpp 13 // sum("Hello", "World!"); ``` 3. Uncomment line 13. What happens when you try to compile? -3. Change the `sum()` function to use type templating. How does this change the output? +3. Change the `sum()` function to use type templating. How does this change the output? Hint: C++ will not automatically convert from a char array to std::string so you will need to be explicit. ## Part 2 @@ -33,4 +33,4 @@ $ ls Makefile complex.cpp complex.hpp test.cpp ``` -`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. Hint: C++ will not automatically convert from a char array to std::string so you will need to be explicit. +`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. From f68bef02597e96c03b500b3f25dda3e2aff9c948 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 12 Mar 2025 00:23:32 +0000 Subject: [PATCH 52/91] my_array example added --- exercises/4-my_array/part1/Makefile | 10 ++++ exercises/4-my_array/part1/my_array.cpp | 27 +++++++++++ exercises/4-my_array/part2/Makefile | 10 ++++ exercises/4-my_array/part2/my_array.cpp | 42 +++++++++++++++++ exercises/4-my_array/part3/Makefile | 10 ++++ exercises/4-my_array/part3/my_array.cpp | 63 +++++++++++++++++++++++++ 6 files changed, 162 insertions(+) create mode 100644 exercises/4-my_array/part1/Makefile create mode 100644 exercises/4-my_array/part1/my_array.cpp create mode 100644 exercises/4-my_array/part2/Makefile create mode 100644 exercises/4-my_array/part2/my_array.cpp create mode 100644 exercises/4-my_array/part3/Makefile create mode 100644 exercises/4-my_array/part3/my_array.cpp diff --git a/exercises/4-my_array/part1/Makefile b/exercises/4-my_array/part1/Makefile new file mode 100644 index 0000000..46c8093 --- /dev/null +++ b/exercises/4-my_array/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +my_array : my_array.o + $(CXX) $^ -o $@ + +run : my_array + ./my_array + +clean : + rm -rf *.o my_array \ No newline at end of file diff --git a/exercises/4-my_array/part1/my_array.cpp b/exercises/4-my_array/part1/my_array.cpp new file mode 100644 index 0000000..42c172e --- /dev/null +++ b/exercises/4-my_array/part1/my_array.cpp @@ -0,0 +1,27 @@ +#include + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) { + std::cout << "Constructing: " << data << std::endl; + // TODO: Implement constructor + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) { + std::cout << "Constructing: " << data << std::endl; + // TODO: Implement constructor + } + + // Copy constructor + my_array(my_array const& other) { + std::cout << "Copy constructing: " << data << std::endl; + // TODO: Implement copy constructor + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + // TODO: Implement copy assignment operator + std::cout << "Copy assigning: " << data << std::endl; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) { + std::cout << "Constructing: " << data << std::endl; + // TODO: Implement constructor + } + + // Copy constructor + my_array(my_array const& other) { + std::cout << "Copy constructing: " << data << std::endl; + // TODO: Implement copy constructor + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + // TODO: Implement copy assignment operator + std::cout << "Copy assigning: " << data << std::endl; + } + + // Move constructor + my_array(my_array&& other) noexcept { + std::cout << "Move construct: " << data << std::endl; + // TODO: Implement move constructor + } + + // Move assignment operator + my_array& operator=(my_array&& other) noexcept { + std::cout << "Move assign: " << data << std::endl; + // TODO: Implement move assignment operator + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < Date: Wed, 12 Mar 2025 00:24:10 +0000 Subject: [PATCH 53/91] pointers exercise added --- exercises/6.1-pointers/Makefile | 10 +++++ exercises/6.1-pointers/pointers.cpp | 61 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 exercises/6.1-pointers/Makefile create mode 100644 exercises/6.1-pointers/pointers.cpp diff --git a/exercises/6.1-pointers/Makefile b/exercises/6.1-pointers/Makefile new file mode 100644 index 0000000..a29826c --- /dev/null +++ b/exercises/6.1-pointers/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +pointers : pointers.o + $(CXX) $^ -o $@ + +run : pointers + ./pointers + +clean : + rm -rf *.o pointers \ No newline at end of file diff --git a/exercises/6.1-pointers/pointers.cpp b/exercises/6.1-pointers/pointers.cpp new file mode 100644 index 0000000..6cb8d0a --- /dev/null +++ b/exercises/6.1-pointers/pointers.cpp @@ -0,0 +1,61 @@ +#include +#include + + +// Print as you go +// Print the value of x +// Print the address of x +// Print the pointer to x +// Print the dereferenced pointer to x +// Print the address of the pointer to x + +int main() { + +// TODO: Create an int x and set it to a value + + +// TODO: Create a point to x called x_ptr + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing + + +std::cout << "----------" << std::endl; + +// TODO: Dereference the pointer to x to increment x's value by one + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing + + +std::cout << "----------" << std::endl; + +// TODO: Create a double array of size 4 and give it some values + + +// TODO: Create a pointers to the 0th and 1st elements in y +// Call them y0_ptr and y1_ptr + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing but for y + + +std::cout << "----------" << std::endl; + +// TODO: Increment the y0_ptr + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing but for y + +std::cout << "----------" << std::endl; + +return 0; +} \ No newline at end of file From 3a0e1cb3222255a924a8d92d5e03ba7e6d7737af Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 12 Mar 2025 00:24:34 +0000 Subject: [PATCH 54/91] special pointers exercise added --- exercises/6.2-special-pointers/part1/Makefile | 10 +++++ .../6.2-special-pointers/part1/unique.cpp | 31 +++++++++++++++ exercises/6.2-special-pointers/part2/Makefile | 10 +++++ .../6.2-special-pointers/part2/shared.cpp | 38 +++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 exercises/6.2-special-pointers/part1/Makefile create mode 100644 exercises/6.2-special-pointers/part1/unique.cpp create mode 100644 exercises/6.2-special-pointers/part2/Makefile create mode 100644 exercises/6.2-special-pointers/part2/shared.cpp diff --git a/exercises/6.2-special-pointers/part1/Makefile b/exercises/6.2-special-pointers/part1/Makefile new file mode 100644 index 0000000..f4df9e5 --- /dev/null +++ b/exercises/6.2-special-pointers/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +pointers : unique.o + $(CXX) $^ -o $@ + +run : unique + ./unique + +clean : + rm -rf *.o unique \ No newline at end of file diff --git a/exercises/6.2-special-pointers/part1/unique.cpp b/exercises/6.2-special-pointers/part1/unique.cpp new file mode 100644 index 0000000..8b9b8e6 --- /dev/null +++ b/exercises/6.2-special-pointers/part1/unique.cpp @@ -0,0 +1,31 @@ +#include +#include + +struct A { + void printA() { std::cout << "A struct...." << std::endl; } +}; + +int main() { + +// https://en.cppreference.com/w/cpp/memory/unique_ptr + +// TODO: Define a unique pointer of A called p1 + + +p1->printA(); + +// displays address of the containing pointer +std::cout << p1.get() << std::endl; + +// TODO: Make a new unique pointer of A p2 +// Set it equal to p1 +// What happens when you compile this? + +// TODO: Use move to move p1 into p2 instead, what happens? + +p2->printA(); +std::cout << p1.get() << std::endl; +std::cout << p2.get() << std::endl; + + return 0; +} \ No newline at end of file diff --git a/exercises/6.2-special-pointers/part2/Makefile b/exercises/6.2-special-pointers/part2/Makefile new file mode 100644 index 0000000..9a1ca9f --- /dev/null +++ b/exercises/6.2-special-pointers/part2/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +pointers : special.o + $(CXX) $^ -o $@ + +run : special + ./special + +clean : + rm -rf *.o special \ No newline at end of file diff --git a/exercises/6.2-special-pointers/part2/shared.cpp b/exercises/6.2-special-pointers/part2/shared.cpp new file mode 100644 index 0000000..5844b3e --- /dev/null +++ b/exercises/6.2-special-pointers/part2/shared.cpp @@ -0,0 +1,38 @@ +#include +#include + +struct A { + void printA() { std::cout << "A struct...." << std::endl; } +}; + +int main() { + +// https://en.cppreference.com/w/cpp/memory/shared_ptr + +// TODO: Create a shared pointer of A called p1 + + +// Printing the address of the managed object +std::cout << p1.get() << std::endl; +p1->printA(); + +// Creating a new shared pointer p2 that shares ownership of p1 + +p2->printA(); + +// Printing addresses of p1 and p2 +std::cout << p1.get() << std::endl; +std::cout << p2.get() << std::endl; + +// TODO: Print the use count of p1 and p2 + +// TODO: Look up how to relinquishes ownership of p1 + +std::cout << p1.get() << std::endl; + +// TODO: Print the use count of p1 + +std::cout << p2.get() << std::endl; + +return 0; +} \ No newline at end of file From 6a77d50ed2dcc353ea4c9b4a5fcac793e52ceb4e Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 12 Mar 2025 00:26:30 +0000 Subject: [PATCH 55/91] update RAII lectures --- lectures/RAII-cont/README.md | 135 +++++++++++++++++++++++++++++++++-- lectures/RAII/README.md | 118 +++++++++++++++++++++++++++--- 2 files changed, 237 insertions(+), 16 deletions(-) diff --git a/lectures/RAII-cont/README.md b/lectures/RAII-cont/README.md index ad4af14..e672d1c 100644 --- a/lectures/RAII-cont/README.md +++ b/lectures/RAII-cont/README.md @@ -43,6 +43,122 @@ manager as one or more data members. If it does deal with ownership then rule of 5 applies :( + +--- +template: titleslide + +# Standard library to the rescue! + +--- + +template: titleslide + +# But first Pointers... + +--- +# Pointers in C++ + +In C++ you can get a pointer to an object using `&` (the *address of* operator) + +You can read or write the value-that-is-pointed-to with `*` (the *dereference* operator) + +```C++ +int main() { + int i = 99; + int* i_ptr = &i; + + *i_ptr += 1; + std::cout << i << std::endl; +} +``` + +--- +# Pointers vs references + +Pointers are a lot like references, but they are not guaranteed to be +initialised to a valid value! + +It is valid to assign an arbitrary value to a pointer, but actually +using it is **undefined behaviour** - typically a crash but could be +silent corruption of data. + +```C++ +int main() { + int* i_ptr = 0xdeadbeef; + + *i_ptr += 1; + std::cout << i << std::endl; +} +``` +Use pointers with great caution! + +--- +# Pointers vs references + +Pointers are a lot like references, but they are not guaranteed to be +initialised to a valid value! + +It is valid to assign an arbitrary value to a pointer, but actually +using it is **undefined behaviour** - typically a crash but could be +silent corruption of data. + +```C++ +int* make_bad_pointer() { + int i = 42; + return &i; +} +``` + +Returning a pointer to a local variable is a recipe for problems + +??? + +Because once that function returns the lifetime of the target object +has ended and accessing it is UB + +--- +# Pointers to dynamically allocated memory + +In C++ you can manually create instances of objects dynamically with +`new` and try to remember to destroy them with `delete` + +But doing so is a recipe for problems! + +??? + +Using new and delete throughout your code is generally going to cause +you problems, even with extensive use of tools like AddressSanitizer (ASan) +and Valgrind + +But C++ has a design pattern that can tame this - first another feature of the language + +--- +# Pointers exercise + +Write a new C++ code to test your understanding of pointers. + +Start with an int x and give it a value. + +Create a pointer to x and show how to dereference the pointer and increment the value of x. + +Make sure your print the location of the pointer and x in memory so you convince yourself no data movement has occurred. + +Second create an double array y of 4 elements and give each element a value. + +Create two pointers one for each of the 0th and 1st members of the array. + +Incrementing the pointer to the 0th element of the array and show the pointer now points to the 1st element of the array. + +Make sure to print the location of the pointer and array elements in memory to convince yourself no data movement has occurred. + +** Exercise ** + +Code skeleton in exercises 6.1-pointers +--- +template: titleslide + +# Finally Standard library to the rescue! + --- # Standard library & special pointers to the rescue! @@ -55,7 +171,7 @@ points to. Pointers can be moved but *not* copied - this is achieved by the copy constructor and copy assignment operators being `delete`d: -``` +```C++ class unique_ptr { unique_ptr(unique_ptr const &) = delete; unique_ptr& operator=(unique_ptr const &) = delete; @@ -311,10 +427,19 @@ if, like most of us, not there yet --- # Special pointers Exercise -- Use a shared pointer -- User a unique pointer -- Test which is which -- Do something simple +These exercises try to get you to test how each of the special pointers work. + +** Part 1 ** + +Try using a unique pointers in a simple example. + +Code skeleton in `6.2-special-pointers/part1` + +** Part 2 ** + +Try using shared pointers in a simple example. + +Code skeleton in `6.2-special-pointers/part2` ??? diff --git a/lectures/RAII/README.md b/lectures/RAII/README.md index 9bc90ad..7ad4338 100644 --- a/lectures/RAII/README.md +++ b/lectures/RAII/README.md @@ -1,9 +1,98 @@ template: titleslide -# RAII +# Managing resources ## James Richings, EPCC ## j.richings@epcc.ed.ac.uk +--- +# Resources + +In a program you often need to manage things like: + +- memory + +- open files + +- GPUs + +- network sockets + +??? + +Let's talk a bit about memory first + +--- +# Memory: overview + +.columns[ +.col[ +Modern OSes given each process a flat address space + +Each byte can be accessed by its address, which is just a number. + +For most purposes this can be considered in two parts: +- stack +- heap +] +.col[ +.center[ +![:scale_img 50%](mem_layout.jpg) +] +] +] + +--- +# Memory: stack + +In C++ (and C and many other compiled languages) local variables are +stored in the **stack**. + +As your program runs, the local variables that are defined, get space +allocated by the compiler relative to the current stack frame. + +Each time you call a function, you start a new stack frame by +incrementing the stack pointer. + +Variables are implemented as offsets from this, so allocating a local +variable has no run-time cost. + +When you return from a function, the stack pointer is updated and +deallocation also has no cost + +These allocations are called *static* because they have to be prepared +at compile time + +--- +# Memory: heap + +The language and OS also make available some memory for dynamic +allocation: the *heap* + +You need to request some memory to store an object and then give it +back when you are finished. + +```C++ +int main() +{ + // This memory for 3 integers + // is allocated on heap. + int *ptr = new int[3]{1,2,3}; + + // To keep things tidy we need to manually call delete + delete[] ptr; +} +``` + + + +??? + +Fortran has allocatable arrays and somewhat restricted pointers + +C programmers will be familiar with malloc and free, which is also +present in C++, but should never be used (I can't recall ever having +done so) + --- template: titleslide # Reminder Classes and Constructors @@ -374,19 +463,26 @@ If it does deal with ownership then rule of 5 applies :( --- -# Exercise +# my_array Exercise + +Time to try this out for yourself your own class. + +**Part 1** + +- Implement the constructor +- Implement the destructor + +**Part 2** + +- Implement the copy constructor +- Implement the copy assignment operator + +**Part 3** -Time to try this out for yourself: +- Implement the move constructor +- Implement the movement move assignment operator -- Try writing your own simple my_vector class: - - Make sure you define a constructor and descructor for the my_vector class. - - Write a simple test program that uses the my_vector. - - Add in the ability to copy the your new my_vector class. - - Update you test program so it copies an instance of your new my_vector. - - Add the ability to move an instance of your my_vector class. - - Update the example program to include a move of the my_vector your have implemented. - - Can you add a member function to calculate the magnitude of my_vector? N.B. Add print statements to each function call in my_vector so you can see when each of the class members are called. From 2cc9bd241ef1b864d6e5d575feed450137b97185 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 12 Mar 2025 00:28:26 +0000 Subject: [PATCH 56/91] udate course timetable --- lectures/course-intro/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index fd669cb..b37221b 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -34,15 +34,15 @@ template: titleslide - 9:30 - 9:45 : Recap previous day -- 9:45 – 10:45 : Managing resources Continued (RAII) +- 9:45 – 10:45 : Managing resources & RAII - 10:45 – 11:00 : Coffee -- 11:00 – 12:00 : Templates for generic programming +- 11:00 – 12:30 : Templates for generic programming -- 12:00 - 1:00 : Lunch +- 12:00 - 1:30 : Lunch -- 1:00 - 3:00 : RAII continued +- 1:30 - 3:00 : RAII continued - 3:00 - 3:15 : Coffee From fb38239c8816f3322e4deff2df50fbac82619a83 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 12 Mar 2025 00:30:43 +0000 Subject: [PATCH 57/91] update course timetable --- lectures/course-intro/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index b37221b..9b98718 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -40,7 +40,7 @@ template: titleslide - 11:00 – 12:30 : Templates for generic programming -- 12:00 - 1:30 : Lunch +- 12:30 - 1:30 : Lunch - 1:30 - 3:00 : RAII continued From 08d00b7c0fdcfc09bdf6d59c63d3e11bfb32f3b0 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Wed, 12 Mar 2025 09:44:50 +0000 Subject: [PATCH 58/91] Add some hints to complex exercise --- exercises/5-templates/part2/complex.cpp | 3 +++ exercises/5-templates/part2/complex.hpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/exercises/5-templates/part2/complex.cpp b/exercises/5-templates/part2/complex.cpp index 3156605..5648072 100644 --- a/exercises/5-templates/part2/complex.cpp +++ b/exercises/5-templates/part2/complex.cpp @@ -1,6 +1,9 @@ #include "complex.hpp" #include +// Hint: Templates should be defined in the .hpp file +// Will any of these functions remain here? + Complex::Complex(double real) : re(real) {} Complex::Complex(double real, double imag) : re(real), im(imag) {} diff --git a/exercises/5-templates/part2/complex.hpp b/exercises/5-templates/part2/complex.hpp index 6ad0bf3..1b6e2a8 100644 --- a/exercises/5-templates/part2/complex.hpp +++ b/exercises/5-templates/part2/complex.hpp @@ -2,6 +2,7 @@ #define CPPEX_COMPLEX_COMPLEX_HPP // Simple complex number class +/* Add template typename definition here */ class Complex { public: // Default value is zero @@ -25,6 +26,10 @@ class Complex { friend bool operator!=(Complex const& a, Complex const& b); // Declare binary arithmetic operators + /* What if I want to add a Complex to a Complex? + What would be returned in this case? What should the return type + be to make this flexible? + */ friend Complex operator+(Complex const& a, Complex const& b); friend Complex operator-(Complex const& a, Complex const& b); friend Complex operator*(Complex const& a, Complex const& b); From 79e22b92ee4d799ab555b1c55902ddb04ec56b74 Mon Sep 17 00:00:00 2001 From: Nathan Mannall Date: Wed, 12 Mar 2025 09:48:13 +0000 Subject: [PATCH 59/91] Update exercises README --- exercises/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/exercises/README.md b/exercises/README.md index 32bc35e..b701614 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -7,7 +7,10 @@ See each subdirectory for further instructions * [2.1 Class types](2.1-class-types/) * [2.2 Complex numbers](2.2-complex/) * [3 Containers](3-containers/) +* [4 My array](4-my_array) * [5 Templates](5-templates) +* [6.1 Pointers](6.1-pointer) +* [6.2 Special pointers](6.2-special-pointers) * [Morton-order matrix class template](morton-order/) * [Using algorithms](algorithm/) * [Eigen](eigen/) From d8d97f90a1b04cc143cbd3eea571862bb6b40f62 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 12 Mar 2025 21:21:07 +0000 Subject: [PATCH 60/91] correct makefile --- exercises/6.2-special-pointers/part2/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/6.2-special-pointers/part2/Makefile b/exercises/6.2-special-pointers/part2/Makefile index 9a1ca9f..5aa8edc 100644 --- a/exercises/6.2-special-pointers/part2/Makefile +++ b/exercises/6.2-special-pointers/part2/Makefile @@ -1,10 +1,10 @@ CXXFLAGS = --std=c++17 -pointers : special.o +pointers : shared.o $(CXX) $^ -o $@ -run : special - ./special +run : shared + ./shared clean : - rm -rf *.o special \ No newline at end of file + rm -rf *.o shared \ No newline at end of file From b9ae42c587b712f014a051d6946440346d07fe25 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Thu, 13 Mar 2025 01:15:14 +0000 Subject: [PATCH 61/91] update course timetable final day --- lectures/course-intro/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lectures/course-intro/README.md b/lectures/course-intro/README.md index 9b98718..29ec8cf 100644 --- a/lectures/course-intro/README.md +++ b/lectures/course-intro/README.md @@ -53,19 +53,19 @@ template: titleslide - 9:30-9:45: Recap the previous day -- 9:45 - 10:45 : Combining classes +- 9:45 - 11:00 : Combining classes -- 10:45 - 11:00 : coffee +- 11:00 - 11:15 : coffee -- 11:00 – 12:00 : Algorithms, lambdas, and traits +- 11:15 – 12:30 : Algorithms, lambdas, and traits -- 12:00 - 1:00 : Lunch +- 12:30 - 1:30 : Lunch -- 1:00 – 2:00 : Linear algebra with Eigen +- 1:30 – 2:15 : Linear algebra with Eigen -- 2:00 - 2:15 : Coffee +- 2:15 - 2:30 : Coffee -- 2:15 – 3:00 : Threads with C++ +- 2:30 – 3:00 : Threads with C++ - 3:05 - 4:00 : Time to work on exercises from the day & ask questions From bd49cc77dd16d0c68872c8916a1086846abccc79 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Thu, 13 Mar 2025 01:15:43 +0000 Subject: [PATCH 62/91] combining classes lectures --- lectures/Combining-classes/README.md | 699 ++++++++++++++++++++++++--- 1 file changed, 639 insertions(+), 60 deletions(-) diff --git a/lectures/Combining-classes/README.md b/lectures/Combining-classes/README.md index d432c0f..4ef1dee 100644 --- a/lectures/Combining-classes/README.md +++ b/lectures/Combining-classes/README.md @@ -3,29 +3,6 @@ template: titleslide ## James Richings ## j.richings@epcc.ed.ac.uk - - ---- -# Plan - - - **Inheritance** - - Inheritance principles - - Multi-level inheritance - - Multiple inheritance - - Access specifiers - - Polymorphism - - (new) **Example** (40 mins) - - Try out different inheritance types - - Try out polymorphism - - **Design patterns** (optional) - - What are design patterns? - - What are some examples? - - Discuss a couple of each type - - Creational - - Structural - - Behavioural - - Direct at resources for other patterns but highlight limitation. - --- # Class manipulation so far @@ -37,18 +14,18 @@ We have discussed: - destruction - copying - movement -- How to build containers with classes with a specific purpose +- How to build containers with classes with a specific purpose. - How to use templates so that classes can be used in a generic way. --- # Whats left? -Can we combine classes to make new classes? +**Can we combine classes to make new classes?** - Inheritance - Multi-level Inheritance - Multiple Inheritance -- Access specifiers +- More Access specifiers - Polymorphism - ... also lots more but we will stop here. @@ -59,54 +36,653 @@ In C++ it is possible to inherit methods and attributes from another class. There are two types of classes when we talk about inheritance. -- Derived or child classes: A class that inherits from another. -- Base or parent classes: A class that is inherited from. +- Derived or child classes: class that inherits from another. +- Base or parent classes: class that is inherited from. + +```C++ +class Parent +{ + public: + Parent() = default; + int a; +}; + +class Child : Parent +{ + public: + Child() = default; + int b; +}; +``` +Class `Child` has data members `a` and `b`. + +--- +# Access specifiers: members + +The previous example everything is public but what is we want to control access + +```C++ +class Parent +{ +public: + Parent() = default; +protected: + int a = 1; +}; + +class Child : Parent +{ +public: + Child() = default; + + void Print() + { + std::cout << "a: " << a << std::endl; + std::cout << "b: " << b << std::endl; + } +private: + int b = 2; +}; +``` + +--- +# Access specifiers: members + + - **`public`**: + - members are accessible from outside the class. + + - **`private`**: + - members cannot be accessed (or viewed) from outside the class. + + - **`protected`**: + - members cannot be accessed from outside the class, however, they can be accessed in inherited classes. + +```C++ +int main() +{ + Child x; + x.Print(); +} ``` -example code +```C++ +./access +a: 1 +b: 2 ``` --- -# Multi-level inheritance +# Access specifiers: Inheritance + +We can also specify the level of access we want when we inherit the parent class in the child class. + +```C++ +class Parent +{ + Parent() = default; + + public: + int a1; + protected: + int a2; + private: + int a3; +}; + +class Child : public Parent +{ + Child() = default; + private: + int b; +}; +``` +- **`public inheritance`**: + - makes public members of the base class public in the derived class, and the protected members of the base class remain protected in the derived class. -"Its turtles all the way down." - classes +--- +# Access specifiers: Inheritance + +We can also specify the level of access we want when we inherit the parent class in the child class. + +```C++ +class Parent +{ + Parent() = default; + + public: + int a1; + protected: + int a2; + private: + int a3; +}; + +class Child : protected Parent +{ + Child() = default; + private: + int b; +}; +``` +- **`protected inheritance`**: + - makes the public and protected members of the base class protected in the derived class. + +--- +# Access specifiers: Inheritance + + We can also specify the level of access we want when we inherit the parent class in the child class. + +```C++ +class Parent +{ + Parent() = default; + + public: + int a1; + protected: + int a2; + private: + int a3; +}; + +class Child : private Parent +{ + Child() = default; + private: + int b; +}; +``` +- **`private inheritance`**: + - makes the public and protected members of the base class private in the derived class. +--- +# Multi-level inheritance +```C++ +class Grandparent +{ +public: + Grandparent() = default; +protected: + int a = 1; +}; +class Parent : public Grandparent +{ +public: + Parent() = default; +protected: + int b = 1; +}; +class Child : public Parent +{ +public: + Child() = default; + void Print() + { + std::cout << "a: " << a << std::endl; + std::cout << "b: " << b << std::endl; + std::cout << "c: " << c << std::endl; + } +private: + int c = 3; +}; +``` + +??? +The "Its turtles all the way down" of classes We can have classes that inherit from classes that are have already inherited from a base class. +--- +# Multi-level inheritance + +Multi-level inheritance allows us to inherit in chain. +**The "Its turtles all the way down" of classes** + +Simple test: +```C++ +int main() +{ + Child x; + x.Print(); +} ``` -Example code + +Result: +```C++ +./multilevel +a: 1 +b: 2 +c: 3 ``` --- # Multiple inheritance -Why limit yourself to one class when you can really confuse yourself... +```C++ +class ParentA +{ +public: + ParentA() = default; +protected: + int a = 4; +}; +class ParentB +{ +public: + ParentB() = default; +protected: + int b = 5; +}; +class Child : public ParentA, public ParentB +{ +public: + Child() = default; + void Print() { + + std::cout << "a: " << a << std::endl; + std::cout << "b: " << b << std::endl; + std::cout << "c: " << c << std::endl; + } +private: + int c = 6; +}; +``` + +??? Classes can also be derived from more than one base class. +Why limit yourself to one class when you can really confuse yourself... + +--- +# Multiple inheritance + +Why limit yourself to one class when you can really confuse yourself... +Simple test: +```C++ +int main() +{ + Child x; + x.Print(); +} ``` -example code +Result: +```C++ +./multiclass +a: 4 +b: 5 +c: 6 +``` + +--- +# The diamond problem + +.center[![:scale_img 80%](diamond-problem-in-cpp.webp)] + +https://www.geeksforgeeks.org/diamond-problem-in-cpp/ + +??? +Two classes inherit the same base class inherited by a third class + +--- +# The diamond problem + +```C++ +class Base { +public: + void fun() { std::cout << "Base" << std::endl; } +}; + +class ParentA : public Base { +public: +}; + +class ParentB : public Base { +public: +}; + +class Child : public ParentA, public ParentB { +}; + +int main() +{ + Child* obj = new Child(); + obj->fun(); + + return 0; +} ``` --- -# Inheritance and Access specifiers +# The diamond problem + +Error: + +```C++ +g++ diamond.cpp -o diamond +diamond.cpp: In function ‘int main()’: +diamond.cpp:26:10: error: request for member ‘fun’ is ambiguous + 26 | obj->fun(); // Abiguity arises, as Child now has two copies of fun() + | ^~~ +diamond.cpp:6:10: note: candidates are: ‘void Base::fun()’ + 6 | void fun() { std::cout << "Base" << std::endl; } + | ^~~ +diamond.cpp:6:10: note: ‘void Base::fun()’ +``` -- Public: Everyone has access -- Private: Only this class has access -- Protected: Private but also accessible by members of a child class. +Issue: +```C++ +int main() +{ + Child* obj = new Child(); + obj->fun(); // Abiguity arises, as Child now has two copies of fun() + return 0; +} ``` -example code + +--- +# The diamond problem: virtual + +```C++ +// Base class +class Base { +public: + void fun() { std::cout << "Base" << std::endl; } +}; + +class ParentA : virtual public Base { +public: +}; + +class ParentB : virtual public Base { +public: +}; + +class Child : public ParentA, public ParentB { +}; + +int main() +{ + Child* obj = new Child(); + obj->fun(); // No ambiguity due to virtual inheritance + return 0; +} ``` +--- +# Inheritance and constructors + +```C++ +class ParentA +{ + public: + ParentA() + { + std::cout << "Constructor of the base class ParentA" << std::endl; + } +}; + +class ParentB +{ + public: + ParentB() + { + std::cout << "Constructor of the base class ParentB" << std::endl; + } +}; + +class Child: public ParentA, public ParentB +{ + public: + Child(): ParentA(), ParentB() + { + std::cout << "Constructor of the derived class Child" << std::endl; + } +}; +``` + +--- +# Inheritance and constructors +Simple test: + +```C++ +int main() +{ + Child obj; + return 0; +} +``` +Result: + +```C++ +./multiConst +Constructor of the base class ParentA +Constructor of the base class ParentB +Constructor of the derived class Child +``` + +N.B. Order of of construction matches order of inheritance + +--- +# Inheritance and destructors + +```C++ +class ParentA +{ + public: + ParentA() = default; + ~ParentA() + { + std::cout << "Destructor of the base class ParentA" << std::endl; + } +}; +class ParentB +{ + public: + ParentB() = default; + ~ParentB() + { + std::cout << "Destructor of the base class ParentB" << std::endl; + } +}; +class Child: public ParentA, public ParentB +{ + public: + Child() = default; + ~Child() + { + std::cout << "Destructor of the derived class Child" << std::endl; + } +}; +``` + +--- +# Inheritance and destructors + +Simple test: + +```C++ +int main() +{ + Child obj; + return 0; +} +``` +Result: + +```C++ +./multiDestruct +Destructor of the derived class Child +Destructor of the base class ParentB +Destructor of the base class ParentA +``` + +N.B. Order of of destruction reverse order of inheritance + --- # Polymorphism +```C++ +class Parent +{ +public: + Parent() = default; + void Print() + { + std::cout << "print from parent" << std::endl; + std::cout << "a: " << a << std::endl; + } + +protected: + int a = 1; +}; + +class Child : Parent +{ +public: + Child() = default; + void Print() + { + std::cout << "print from child" << std::endl; + std::cout << "a: " << a << std::endl; + std::cout << "b: " << b << std::endl; + } +private: + int b = 2; +}; +``` + +??? Allows us to use inheritance to do different things in the derived class than specified in the base class with the same methods. +--- +# Polymorphism + +Simple test: + +```C++ +int main() { + +Child x; + +x.Print(); + +Parent y; + +y.Print(); + +} +``` +Result: + +```C++ +print from child +a: 1 +b: 2 +print from parent +a: 1 +``` +--- +# Virtual functions + +```C++ +class base { +public: + virtual void print() { std::cout << "print base class" << std::end; } + + void show() { std::cout << "show base class" << std:endl; } +}; + +class derived : public base { +public: + void print() { std::cout << "print derived class" << std::endl; } + + void show() { std::cout << "show derived class" << std::endl; } +}; + +int main() +{ + base* bptr; + derived d; + bptr = &d; + + // Virtual function, binded at runtime + bptr->print(); + // Non-virtual function, binded at compile time + bptr->show(); + + return 0; +} ``` -example code + +--- +# Virtual functions + +Test: +```C++ +int main() +{ + base* bptr; + derived d; + bptr = &d; + + // Virtual function, binded at runtime + bptr->print(); + // Non-virtual function, binded at compile time + bptr->show(); + + return 0; +} ``` +Result: +```C++ +./virtual +print derived class +show base class +``` + +--- +# Override + +```C++ +class Base +{ + public: + // user wants to override this in the derived class + virtual void func() + { + std::cout << "I am in base" << std::endl; + } +}; + +class derived : public Base +{ + public: + // did a silly mistake by putting an argument "int a" + void func(int a) override + { + std::cout << "I am in derived class" << std::endl; + } +}; + +int main() +{ + Base b; + derived d; + std::cout << "Compiled successfully" << std::endl; + return 0; +} +``` + +--- +# Types of polymorphism + +.center[![:scale_img 90%](Polymorphism-in-CPP.png)] + +https://www.geeksforgeeks.org/cpp-polymorphism/ +--- +template:titleslide +# Exercise --- # Exercise @@ -114,44 +690,47 @@ example code Take the example complex class we wrote a couple of days ago and ... -- do inheritance: - - split complex up so that the contructors and data members live in the base class and a magnitude squared function is in a derived class -- do multi-level inheritance +- **`Try inheritance`**: + - Split complex up so that the contructors and data members live in the base class and a magnitude squared function is in a derived class +- **` Try multi-level inheritance`**: - Now in a third class write a absolute magnitude function and have this class inherit from the previous -- do multiple inheritance - - Take new copy of your complex class +- **`Try multiple inheritance`**: + - Take new copy of your complex class with only data members - Write a magnitude class - - Write an angle class - - Write a class that represents complex number in polar form that inherits from complex, angle and magnitude + - Write an real getter class + - Write imaginary getter class + - Write a class that combines inherits all three to make a complex complex objecgt - Polymorphism - Write a base class with a function called weatherforecast that prints a message. "rain" - Write a second - - - --- +template: titleslide +# Great we can combine classes... ...now what? +--- +template: titleslide # Design patterns -Great we have ways to combine classes together now what? - -What are some of the common ways of combining classes to generate certain effects in code? - -Design patterns provide us with a library of code orginisations to draw from to acheive different effects - --- -# Types of design pattern +# Design pattern -- Creational +- **`Creational`**: - "These patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code." -- Structural +- **`Structural`**: - "These patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient." -- Behavoural +- **`Behavoural`**: - "These patterns are concerned with algorithms and the assignment of responsibilities between objects." Nice resource for this: https://refactoring.guru/design-patterns/catalog +Books: + +- [Gang of 4 design patterns](https://www.google.co.uk/books/edition/Design_Patterns/6oHuKQe3TjQC?hl=en&gbpv=0) + +- [Patterns for Parallel Programming](https://www.google.co.uk/books/edition/Patterns_for_Parallel_Programming/LNcFvN5Z4RMC?hl=en&gbpv=0) + +- Slightly dusty [ARCHER course material](http://www.archer.ac.uk/training/course-material/2018/11/parallel-patterns-oxford/slides/L01-Introduction.pdf) --- # Creational Patterns From 01ab3532755e06d6617028b2d31782eda38140be Mon Sep 17 00:00:00 2001 From: JPRichings Date: Thu, 13 Mar 2025 01:16:19 +0000 Subject: [PATCH 63/91] combining classes images --- .../Combining-classes/Polymorphism-in-CPP.png | Bin 0 -> 35277 bytes .../diamond-problem-in-cpp.webp | Bin 0 -> 10924 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lectures/Combining-classes/Polymorphism-in-CPP.png create mode 100644 lectures/Combining-classes/diamond-problem-in-cpp.webp diff --git a/lectures/Combining-classes/Polymorphism-in-CPP.png b/lectures/Combining-classes/Polymorphism-in-CPP.png new file mode 100644 index 0000000000000000000000000000000000000000..9779a3f1d471ba782b88ff0d41d2e02ad9ec036c GIT binary patch literal 35277 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 literal 0 HcmV?d00001 diff --git a/lectures/Combining-classes/diamond-problem-in-cpp.webp b/lectures/Combining-classes/diamond-problem-in-cpp.webp new file mode 100644 index 0000000000000000000000000000000000000000..752c31a16e4fda7680e0891e84004af56e17240c GIT binary patch literal 10924 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 Date: Thu, 13 Mar 2025 01:39:58 +0000 Subject: [PATCH 64/91] update combining classes exercises --- exercises/7-inheritance/part1/Makefile | 15 ++++ exercises/7-inheritance/part1/complex.cpp | 28 +++++++ exercises/7-inheritance/part1/complex.hpp | 37 +++++++++ exercises/7-inheritance/part1/test.cpp | 96 +++++++++++++++++++++++ exercises/7-inheritance/part2/Makefile | 15 ++++ exercises/7-inheritance/part2/complex.cpp | 28 +++++++ exercises/7-inheritance/part2/complex.hpp | 37 +++++++++ exercises/7-inheritance/part2/test.cpp | 96 +++++++++++++++++++++++ exercises/7-inheritance/part3/Makefile | 15 ++++ exercises/7-inheritance/part3/complex.cpp | 28 +++++++ exercises/7-inheritance/part3/complex.hpp | 37 +++++++++ exercises/7-inheritance/part3/test.cpp | 96 +++++++++++++++++++++++ exercises/7-inheritance/part4/main.cpp | 9 +++ exercises/7-inheritance/part4/poly.cpp | 17 ++++ exercises/7-inheritance/part4/poly.hpp | 23 ++++++ lectures/Combining-classes/README.md | 14 ++-- 16 files changed, 584 insertions(+), 7 deletions(-) create mode 100644 exercises/7-inheritance/part1/Makefile create mode 100644 exercises/7-inheritance/part1/complex.cpp create mode 100644 exercises/7-inheritance/part1/complex.hpp create mode 100644 exercises/7-inheritance/part1/test.cpp create mode 100644 exercises/7-inheritance/part2/Makefile create mode 100644 exercises/7-inheritance/part2/complex.cpp create mode 100644 exercises/7-inheritance/part2/complex.hpp create mode 100644 exercises/7-inheritance/part2/test.cpp create mode 100644 exercises/7-inheritance/part3/Makefile create mode 100644 exercises/7-inheritance/part3/complex.cpp create mode 100644 exercises/7-inheritance/part3/complex.hpp create mode 100644 exercises/7-inheritance/part3/test.cpp create mode 100644 exercises/7-inheritance/part4/main.cpp create mode 100644 exercises/7-inheritance/part4/poly.cpp create mode 100644 exercises/7-inheritance/part4/poly.hpp diff --git a/exercises/7-inheritance/part1/Makefile b/exercises/7-inheritance/part1/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/7-inheritance/part1/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/7-inheritance/part1/complex.cpp b/exercises/7-inheritance/part1/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/7-inheritance/part1/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/7-inheritance/part1/complex.hpp b/exercises/7-inheritance/part1/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/7-inheritance/part1/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/7-inheritance/part1/test.cpp b/exercises/7-inheritance/part1/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/7-inheritance/part1/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file diff --git a/exercises/7-inheritance/part2/Makefile b/exercises/7-inheritance/part2/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/7-inheritance/part2/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/7-inheritance/part2/complex.cpp b/exercises/7-inheritance/part2/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/7-inheritance/part2/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/7-inheritance/part2/complex.hpp b/exercises/7-inheritance/part2/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/7-inheritance/part2/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/7-inheritance/part2/test.cpp b/exercises/7-inheritance/part2/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/7-inheritance/part2/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file diff --git a/exercises/7-inheritance/part3/Makefile b/exercises/7-inheritance/part3/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/7-inheritance/part3/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/7-inheritance/part3/complex.cpp b/exercises/7-inheritance/part3/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/7-inheritance/part3/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/7-inheritance/part3/complex.hpp b/exercises/7-inheritance/part3/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/7-inheritance/part3/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/7-inheritance/part3/test.cpp b/exercises/7-inheritance/part3/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/7-inheritance/part3/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file diff --git a/exercises/7-inheritance/part4/main.cpp b/exercises/7-inheritance/part4/main.cpp new file mode 100644 index 0000000..49d491d --- /dev/null +++ b/exercises/7-inheritance/part4/main.cpp @@ -0,0 +1,9 @@ +#include "poly.cpp" + + +int main() +{ +child x; + +x.predict(); +} diff --git a/exercises/7-inheritance/part4/poly.cpp b/exercises/7-inheritance/part4/poly.cpp new file mode 100644 index 0000000..44a2a15 --- /dev/null +++ b/exercises/7-inheritance/part4/poly.cpp @@ -0,0 +1,17 @@ +#include +#include "poly.hpp" + + +void base::forcast(){ + std::cout << "The forcast is Rain" << std::endl; +} + +void child::forcast(){ + std::cout << "The forcast is Sunny" << std::endl; +} + +void child::predict(){ + base::forcast(); +}; + + diff --git a/exercises/7-inheritance/part4/poly.hpp b/exercises/7-inheritance/part4/poly.hpp new file mode 100644 index 0000000..33ab206 --- /dev/null +++ b/exercises/7-inheritance/part4/poly.hpp @@ -0,0 +1,23 @@ + +class base +{ + +public: + +void forcast(); + +}; + + +class child : base +{ + +private: + +void forcast(); + +public: + +void predict(); + +}; diff --git a/lectures/Combining-classes/README.md b/lectures/Combining-classes/README.md index 4ef1dee..f9cf34e 100644 --- a/lectures/Combining-classes/README.md +++ b/lectures/Combining-classes/README.md @@ -696,13 +696,13 @@ Take the example complex class we wrote a couple of days ago and ... - Now in a third class write a absolute magnitude function and have this class inherit from the previous - **`Try multiple inheritance`**: - Take new copy of your complex class with only data members - - Write a magnitude class - - Write an real getter class - - Write imaginary getter class - - Write a class that combines inherits all three to make a complex complex objecgt -- Polymorphism - - Write a base class with a function called weatherforecast that prints a message. "rain" - - Write a second + - Write an real and imaginary getter class + - Write a magnitude class that inherits the other two classes to make a complete complex class. +- **`Polymorphism`** + - Compile part 4 with `g++ main.cpp -o main` + - Run `main` + - Look at the code and can you explain how the inheritance works? + - Can you correct the forcast? --- From 0d7dba9144e33cacee12bcef9c9f02a0fb41ff74 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Thu, 13 Mar 2025 01:44:15 +0000 Subject: [PATCH 65/91] update content --- lectures/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lectures/README.md b/lectures/README.md index 94fc752..c29c0a4 100644 --- a/lectures/README.md +++ b/lectures/README.md @@ -8,7 +8,7 @@ * [RAII](RAII) * [Templates for generic programming](templates) * [RAII continued](RAII-cont) -* [Combining Classes](Inheritance) +* [Combining Classes](Combining-classes) * [Algorithms, lambdas and traits](algorithms-lambdas-traits) * [Linear algebra with Eigen](eigen) * [Threads with C++](threads-1) and [Further topics with threads](threads-2) From f5a30195fbcdc5f3aaf37043075da78501438ae5 Mon Sep 17 00:00:00 2001 From: nmannall Date: Thu, 17 Apr 2025 10:31:01 +0100 Subject: [PATCH 66/91] Add section numbers to lecture and exercise folders --- exercises/{threads => 10-threads}/Makefile | 0 .../{morton-order => 10-threads}/README.md | 0 exercises/{threads => 10-threads}/area.cpp | 0 .../{threads => 10-threads}/instructions.md | 0 .../{threads => 10-threads}/instructions.pdf | Bin .../{4-my_array => 4-my-array}/part1/Makefile | 0 .../part1/my_array.cpp | 0 .../{4-my_array => 4-my-array}/part2/Makefile | 0 .../part2/my_array.cpp | 0 .../{4-my_array => 4-my-array}/part3/Makefile | 0 .../part3/my_array.cpp | 0 .../Makefile | 0 .../{threads => 6.3-morton-order}/README.md | 0 .../bits.hpp | 0 .../config.mk | 0 .../instructions.md | 0 .../instructions.pdf | Bin .../mortonorder.png | Bin .../range.hpp | 0 .../step1/Makefile | 0 .../step1/matrix.hpp | 0 .../step1/test_matrix_base.cpp | 0 .../step2/Makefile | 0 .../step2/matrix.hpp | 0 .../step2/test_matrix_base.cpp | 0 .../step2/test_matrix_iter.cpp | 0 .../test.hpp | 0 .../test_bits.cpp | 0 exercises/{algorithm => 8-algorithm}/Makefile | 0 .../{algorithm => 8-algorithm}/README.md | 0 exercises/{algorithm => 8-algorithm}/ex.cpp | 0 exercises/{eigen => 9-eigen}/Makefile | 0 exercises/{eigen => 9-eigen}/README.md | 0 exercises/{eigen => 9-eigen}/explicit.cpp | 0 exercises/{eigen => 9-eigen}/implicit.cpp | 0 exercises/{eigen => 9-eigen}/modules.sh | 0 exercises/{eigen => 9-eigen}/movie.py | 0 exercises/{eigen => 9-eigen}/sparse.cpp | 0 exercises/README.md | 17 +++++++------ .../README.md | 0 .../index.html | 0 lectures/{cpp-intro => 1-cpp-intro}/README.md | 0 .../{cpp-intro => 1-cpp-intro}/auto/auto.cpp | 0 .../{cpp-intro => 1-cpp-intro}/frank_mon.jpg | Bin .../hello/hello.cpp | 0 .../{cpp-intro => 1-cpp-intro}/index.html | 0 .../{cpp-intro => 1-cpp-intro}/octodog.jpg | Bin lectures/{cpp-intro => 1-cpp-intro}/sak.jpg | Bin .../{cpp-intro => 1-cpp-intro}/sum/sum.cpp | 0 .../{threads-1 => 10.1-threads}/README.md | 0 .../{threads-1 => 10.1-threads}/index.html | 0 .../README.md | 0 .../index.html | 0 .../omp-v-thread.png | Bin lectures/{classes => 2-classes}/README.md | 0 lectures/{classes => 2-classes}/index.html | 0 .../README.md | 0 .../domain_decomp.png | Bin .../index.html | 0 lectures/{resources => 4-resources}/README.md | 0 .../{RAII-cont => 4-resources}/index.html | 0 .../{RAII-cont => 4-resources}/mem_layout.jpg | Bin .../sample/.gitignore | 0 .../sample/Makefile | 0 .../sample/arr1.cpp | 0 .../sample/arr2.cpp | 0 .../sample/arr3.cpp | 0 .../sample/dyn1.cpp | 0 .../sample/dyn2.cpp | 0 .../sample/dyn3.cpp | 0 .../sample/shared.cpp | 0 lectures/{templates => 5-templates}/README.md | 0 .../{templates => 5-templates}/index.html | 0 lectures/{RAII => 6.1-RAII}/README.md | 0 lectures/{RAII => 6.1-RAII}/index.html | 0 lectures/{RAII => 6.1-RAII}/mem_layout.jpg | Bin lectures/{RAII => 6.1-RAII}/sample/.gitignore | 0 lectures/{RAII => 6.1-RAII}/sample/Makefile | 0 lectures/{RAII => 6.1-RAII}/sample/arr1.cpp | 0 lectures/{RAII => 6.1-RAII}/sample/arr2.cpp | 0 lectures/{RAII => 6.1-RAII}/sample/arr3.cpp | 0 lectures/{RAII => 6.1-RAII}/sample/dyn1.cpp | 0 lectures/{RAII => 6.1-RAII}/sample/dyn2.cpp | 0 lectures/{RAII => 6.1-RAII}/sample/dyn3.cpp | 0 lectures/{RAII => 6.1-RAII}/sample/shared.cpp | 0 .../{RAII-cont => 6.2-RAII-cont}/README.md | 0 .../{resources => 6.2-RAII-cont}/index.html | 0 .../mem_layout.jpg | Bin .../sample/.gitignore | 0 .../sample/Makefile | 0 .../sample/arr1.cpp | 0 .../sample/arr2.cpp | 0 .../sample/arr3.cpp | 0 .../sample/dyn1.cpp | 0 .../sample/dyn2.cpp | 0 .../sample/dyn3.cpp | 0 .../sample/shared.cpp | 0 .../Polymorphism-in-CPP.png | Bin .../README.md | 0 .../diamond-problem-in-cpp.webp | Bin .../index.html | 0 .../README.md | 0 .../index.html | 0 .../looptests/opt0.svg | 0 .../looptests/opt2.svg | 0 lectures/{eigen => 9-eigen}/README.md | 0 lectures/{eigen => 9-eigen}/index.html | 0 lectures/README.md | 24 +++++++++--------- 108 files changed, 21 insertions(+), 20 deletions(-) rename exercises/{threads => 10-threads}/Makefile (100%) rename exercises/{morton-order => 10-threads}/README.md (100%) rename exercises/{threads => 10-threads}/area.cpp (100%) rename exercises/{threads => 10-threads}/instructions.md (100%) rename exercises/{threads => 10-threads}/instructions.pdf (100%) rename exercises/{4-my_array => 4-my-array}/part1/Makefile (100%) rename exercises/{4-my_array => 4-my-array}/part1/my_array.cpp (100%) rename exercises/{4-my_array => 4-my-array}/part2/Makefile (100%) rename exercises/{4-my_array => 4-my-array}/part2/my_array.cpp (100%) rename exercises/{4-my_array => 4-my-array}/part3/Makefile (100%) rename exercises/{4-my_array => 4-my-array}/part3/my_array.cpp (100%) rename exercises/{morton-order => 6.3-morton-order}/Makefile (100%) rename exercises/{threads => 6.3-morton-order}/README.md (100%) rename exercises/{morton-order => 6.3-morton-order}/bits.hpp (100%) rename exercises/{morton-order => 6.3-morton-order}/config.mk (100%) rename exercises/{morton-order => 6.3-morton-order}/instructions.md (100%) rename exercises/{morton-order => 6.3-morton-order}/instructions.pdf (100%) rename exercises/{morton-order => 6.3-morton-order}/mortonorder.png (100%) rename exercises/{morton-order => 6.3-morton-order}/range.hpp (100%) rename exercises/{morton-order => 6.3-morton-order}/step1/Makefile (100%) rename exercises/{morton-order => 6.3-morton-order}/step1/matrix.hpp (100%) rename exercises/{morton-order => 6.3-morton-order}/step1/test_matrix_base.cpp (100%) rename exercises/{morton-order => 6.3-morton-order}/step2/Makefile (100%) rename exercises/{morton-order => 6.3-morton-order}/step2/matrix.hpp (100%) rename exercises/{morton-order => 6.3-morton-order}/step2/test_matrix_base.cpp (100%) rename exercises/{morton-order => 6.3-morton-order}/step2/test_matrix_iter.cpp (100%) rename exercises/{morton-order => 6.3-morton-order}/test.hpp (100%) rename exercises/{morton-order => 6.3-morton-order}/test_bits.cpp (100%) rename exercises/{algorithm => 8-algorithm}/Makefile (100%) rename exercises/{algorithm => 8-algorithm}/README.md (100%) rename exercises/{algorithm => 8-algorithm}/ex.cpp (100%) rename exercises/{eigen => 9-eigen}/Makefile (100%) rename exercises/{eigen => 9-eigen}/README.md (100%) rename exercises/{eigen => 9-eigen}/explicit.cpp (100%) rename exercises/{eigen => 9-eigen}/implicit.cpp (100%) rename exercises/{eigen => 9-eigen}/modules.sh (100%) rename exercises/{eigen => 9-eigen}/movie.py (100%) rename exercises/{eigen => 9-eigen}/sparse.cpp (100%) rename lectures/{course-intro => 0-course-intro}/README.md (100%) rename lectures/{course-intro => 0-course-intro}/index.html (100%) rename lectures/{cpp-intro => 1-cpp-intro}/README.md (100%) rename lectures/{cpp-intro => 1-cpp-intro}/auto/auto.cpp (100%) rename lectures/{cpp-intro => 1-cpp-intro}/frank_mon.jpg (100%) rename lectures/{cpp-intro => 1-cpp-intro}/hello/hello.cpp (100%) rename lectures/{cpp-intro => 1-cpp-intro}/index.html (100%) rename lectures/{cpp-intro => 1-cpp-intro}/octodog.jpg (100%) rename lectures/{cpp-intro => 1-cpp-intro}/sak.jpg (100%) rename lectures/{cpp-intro => 1-cpp-intro}/sum/sum.cpp (100%) rename lectures/{threads-1 => 10.1-threads}/README.md (100%) rename lectures/{threads-1 => 10.1-threads}/index.html (100%) rename lectures/{threads-2 => 10.2-threads-cont}/README.md (100%) rename lectures/{threads-2 => 10.2-threads-cont}/index.html (100%) rename lectures/{threads-2 => 10.2-threads-cont}/omp-v-thread.png (100%) rename lectures/{classes => 2-classes}/README.md (100%) rename lectures/{classes => 2-classes}/index.html (100%) rename lectures/{loops-containers => 3-loops-containers}/README.md (100%) rename lectures/{loops-containers => 3-loops-containers}/domain_decomp.png (100%) rename lectures/{loops-containers => 3-loops-containers}/index.html (100%) rename lectures/{resources => 4-resources}/README.md (100%) rename lectures/{RAII-cont => 4-resources}/index.html (100%) rename lectures/{RAII-cont => 4-resources}/mem_layout.jpg (100%) rename lectures/{RAII-cont => 4-resources}/sample/.gitignore (100%) rename lectures/{RAII-cont => 4-resources}/sample/Makefile (100%) rename lectures/{RAII-cont => 4-resources}/sample/arr1.cpp (100%) rename lectures/{RAII-cont => 4-resources}/sample/arr2.cpp (100%) rename lectures/{RAII-cont => 4-resources}/sample/arr3.cpp (100%) rename lectures/{RAII-cont => 4-resources}/sample/dyn1.cpp (100%) rename lectures/{RAII-cont => 4-resources}/sample/dyn2.cpp (100%) rename lectures/{RAII-cont => 4-resources}/sample/dyn3.cpp (100%) rename lectures/{RAII-cont => 4-resources}/sample/shared.cpp (100%) rename lectures/{templates => 5-templates}/README.md (100%) rename lectures/{templates => 5-templates}/index.html (100%) rename lectures/{RAII => 6.1-RAII}/README.md (100%) rename lectures/{RAII => 6.1-RAII}/index.html (100%) rename lectures/{RAII => 6.1-RAII}/mem_layout.jpg (100%) rename lectures/{RAII => 6.1-RAII}/sample/.gitignore (100%) rename lectures/{RAII => 6.1-RAII}/sample/Makefile (100%) rename lectures/{RAII => 6.1-RAII}/sample/arr1.cpp (100%) rename lectures/{RAII => 6.1-RAII}/sample/arr2.cpp (100%) rename lectures/{RAII => 6.1-RAII}/sample/arr3.cpp (100%) rename lectures/{RAII => 6.1-RAII}/sample/dyn1.cpp (100%) rename lectures/{RAII => 6.1-RAII}/sample/dyn2.cpp (100%) rename lectures/{RAII => 6.1-RAII}/sample/dyn3.cpp (100%) rename lectures/{RAII => 6.1-RAII}/sample/shared.cpp (100%) rename lectures/{RAII-cont => 6.2-RAII-cont}/README.md (100%) rename lectures/{resources => 6.2-RAII-cont}/index.html (100%) rename lectures/{resources => 6.2-RAII-cont}/mem_layout.jpg (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/.gitignore (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/Makefile (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/arr1.cpp (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/arr2.cpp (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/arr3.cpp (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/dyn1.cpp (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/dyn2.cpp (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/dyn3.cpp (100%) rename lectures/{resources => 6.2-RAII-cont}/sample/shared.cpp (100%) rename lectures/{Combining-classes => 7-combining-classes}/Polymorphism-in-CPP.png (100%) rename lectures/{Combining-classes => 7-combining-classes}/README.md (100%) rename lectures/{Combining-classes => 7-combining-classes}/diamond-problem-in-cpp.webp (100%) rename lectures/{Combining-classes => 7-combining-classes}/index.html (100%) rename lectures/{algorithms-lambdas-traits => 8-algorithms-lambdas-traits}/README.md (100%) rename lectures/{algorithms-lambdas-traits => 8-algorithms-lambdas-traits}/index.html (100%) rename lectures/{algorithms-lambdas-traits => 8-algorithms-lambdas-traits}/looptests/opt0.svg (100%) rename lectures/{algorithms-lambdas-traits => 8-algorithms-lambdas-traits}/looptests/opt2.svg (100%) rename lectures/{eigen => 9-eigen}/README.md (100%) rename lectures/{eigen => 9-eigen}/index.html (100%) diff --git a/exercises/threads/Makefile b/exercises/10-threads/Makefile similarity index 100% rename from exercises/threads/Makefile rename to exercises/10-threads/Makefile diff --git a/exercises/morton-order/README.md b/exercises/10-threads/README.md similarity index 100% rename from exercises/morton-order/README.md rename to exercises/10-threads/README.md diff --git a/exercises/threads/area.cpp b/exercises/10-threads/area.cpp similarity index 100% rename from exercises/threads/area.cpp rename to exercises/10-threads/area.cpp diff --git a/exercises/threads/instructions.md b/exercises/10-threads/instructions.md similarity index 100% rename from exercises/threads/instructions.md rename to exercises/10-threads/instructions.md diff --git a/exercises/threads/instructions.pdf b/exercises/10-threads/instructions.pdf similarity index 100% rename from exercises/threads/instructions.pdf rename to exercises/10-threads/instructions.pdf diff --git a/exercises/4-my_array/part1/Makefile b/exercises/4-my-array/part1/Makefile similarity index 100% rename from exercises/4-my_array/part1/Makefile rename to exercises/4-my-array/part1/Makefile diff --git a/exercises/4-my_array/part1/my_array.cpp b/exercises/4-my-array/part1/my_array.cpp similarity index 100% rename from exercises/4-my_array/part1/my_array.cpp rename to exercises/4-my-array/part1/my_array.cpp diff --git a/exercises/4-my_array/part2/Makefile b/exercises/4-my-array/part2/Makefile similarity index 100% rename from exercises/4-my_array/part2/Makefile rename to exercises/4-my-array/part2/Makefile diff --git a/exercises/4-my_array/part2/my_array.cpp b/exercises/4-my-array/part2/my_array.cpp similarity index 100% rename from exercises/4-my_array/part2/my_array.cpp rename to exercises/4-my-array/part2/my_array.cpp diff --git a/exercises/4-my_array/part3/Makefile b/exercises/4-my-array/part3/Makefile similarity index 100% rename from exercises/4-my_array/part3/Makefile rename to exercises/4-my-array/part3/Makefile diff --git a/exercises/4-my_array/part3/my_array.cpp b/exercises/4-my-array/part3/my_array.cpp similarity index 100% rename from exercises/4-my_array/part3/my_array.cpp rename to exercises/4-my-array/part3/my_array.cpp diff --git a/exercises/morton-order/Makefile b/exercises/6.3-morton-order/Makefile similarity index 100% rename from exercises/morton-order/Makefile rename to exercises/6.3-morton-order/Makefile diff --git a/exercises/threads/README.md b/exercises/6.3-morton-order/README.md similarity index 100% rename from exercises/threads/README.md rename to exercises/6.3-morton-order/README.md diff --git a/exercises/morton-order/bits.hpp b/exercises/6.3-morton-order/bits.hpp similarity index 100% rename from exercises/morton-order/bits.hpp rename to exercises/6.3-morton-order/bits.hpp diff --git a/exercises/morton-order/config.mk b/exercises/6.3-morton-order/config.mk similarity index 100% rename from exercises/morton-order/config.mk rename to exercises/6.3-morton-order/config.mk diff --git a/exercises/morton-order/instructions.md b/exercises/6.3-morton-order/instructions.md similarity index 100% rename from exercises/morton-order/instructions.md rename to exercises/6.3-morton-order/instructions.md diff --git a/exercises/morton-order/instructions.pdf b/exercises/6.3-morton-order/instructions.pdf similarity index 100% rename from exercises/morton-order/instructions.pdf rename to exercises/6.3-morton-order/instructions.pdf diff --git a/exercises/morton-order/mortonorder.png b/exercises/6.3-morton-order/mortonorder.png similarity index 100% rename from exercises/morton-order/mortonorder.png rename to exercises/6.3-morton-order/mortonorder.png diff --git a/exercises/morton-order/range.hpp b/exercises/6.3-morton-order/range.hpp similarity index 100% rename from exercises/morton-order/range.hpp rename to exercises/6.3-morton-order/range.hpp diff --git a/exercises/morton-order/step1/Makefile b/exercises/6.3-morton-order/step1/Makefile similarity index 100% rename from exercises/morton-order/step1/Makefile rename to exercises/6.3-morton-order/step1/Makefile diff --git a/exercises/morton-order/step1/matrix.hpp b/exercises/6.3-morton-order/step1/matrix.hpp similarity index 100% rename from exercises/morton-order/step1/matrix.hpp rename to exercises/6.3-morton-order/step1/matrix.hpp diff --git a/exercises/morton-order/step1/test_matrix_base.cpp b/exercises/6.3-morton-order/step1/test_matrix_base.cpp similarity index 100% rename from exercises/morton-order/step1/test_matrix_base.cpp rename to exercises/6.3-morton-order/step1/test_matrix_base.cpp diff --git a/exercises/morton-order/step2/Makefile b/exercises/6.3-morton-order/step2/Makefile similarity index 100% rename from exercises/morton-order/step2/Makefile rename to exercises/6.3-morton-order/step2/Makefile diff --git a/exercises/morton-order/step2/matrix.hpp b/exercises/6.3-morton-order/step2/matrix.hpp similarity index 100% rename from exercises/morton-order/step2/matrix.hpp rename to exercises/6.3-morton-order/step2/matrix.hpp diff --git a/exercises/morton-order/step2/test_matrix_base.cpp b/exercises/6.3-morton-order/step2/test_matrix_base.cpp similarity index 100% rename from exercises/morton-order/step2/test_matrix_base.cpp rename to exercises/6.3-morton-order/step2/test_matrix_base.cpp diff --git a/exercises/morton-order/step2/test_matrix_iter.cpp b/exercises/6.3-morton-order/step2/test_matrix_iter.cpp similarity index 100% rename from exercises/morton-order/step2/test_matrix_iter.cpp rename to exercises/6.3-morton-order/step2/test_matrix_iter.cpp diff --git a/exercises/morton-order/test.hpp b/exercises/6.3-morton-order/test.hpp similarity index 100% rename from exercises/morton-order/test.hpp rename to exercises/6.3-morton-order/test.hpp diff --git a/exercises/morton-order/test_bits.cpp b/exercises/6.3-morton-order/test_bits.cpp similarity index 100% rename from exercises/morton-order/test_bits.cpp rename to exercises/6.3-morton-order/test_bits.cpp diff --git a/exercises/algorithm/Makefile b/exercises/8-algorithm/Makefile similarity index 100% rename from exercises/algorithm/Makefile rename to exercises/8-algorithm/Makefile diff --git a/exercises/algorithm/README.md b/exercises/8-algorithm/README.md similarity index 100% rename from exercises/algorithm/README.md rename to exercises/8-algorithm/README.md diff --git a/exercises/algorithm/ex.cpp b/exercises/8-algorithm/ex.cpp similarity index 100% rename from exercises/algorithm/ex.cpp rename to exercises/8-algorithm/ex.cpp diff --git a/exercises/eigen/Makefile b/exercises/9-eigen/Makefile similarity index 100% rename from exercises/eigen/Makefile rename to exercises/9-eigen/Makefile diff --git a/exercises/eigen/README.md b/exercises/9-eigen/README.md similarity index 100% rename from exercises/eigen/README.md rename to exercises/9-eigen/README.md diff --git a/exercises/eigen/explicit.cpp b/exercises/9-eigen/explicit.cpp similarity index 100% rename from exercises/eigen/explicit.cpp rename to exercises/9-eigen/explicit.cpp diff --git a/exercises/eigen/implicit.cpp b/exercises/9-eigen/implicit.cpp similarity index 100% rename from exercises/eigen/implicit.cpp rename to exercises/9-eigen/implicit.cpp diff --git a/exercises/eigen/modules.sh b/exercises/9-eigen/modules.sh similarity index 100% rename from exercises/eigen/modules.sh rename to exercises/9-eigen/modules.sh diff --git a/exercises/eigen/movie.py b/exercises/9-eigen/movie.py similarity index 100% rename from exercises/eigen/movie.py rename to exercises/9-eigen/movie.py diff --git a/exercises/eigen/sparse.cpp b/exercises/9-eigen/sparse.cpp similarity index 100% rename from exercises/eigen/sparse.cpp rename to exercises/9-eigen/sparse.cpp diff --git a/exercises/README.md b/exercises/README.md index b701614..95ab517 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -7,11 +7,12 @@ See each subdirectory for further instructions * [2.1 Class types](2.1-class-types/) * [2.2 Complex numbers](2.2-complex/) * [3 Containers](3-containers/) -* [4 My array](4-my_array) -* [5 Templates](5-templates) -* [6.1 Pointers](6.1-pointer) -* [6.2 Special pointers](6.2-special-pointers) -* [Morton-order matrix class template](morton-order/) -* [Using algorithms](algorithm/) -* [Eigen](eigen/) -* [Simple use of threads](threads/) +* [4 My array](4-my-array/) +* [5 Templates](5-templates/) +* [6.1 Pointers](6.1-pointers/) +* [6.2 Special pointers](6.2-special-pointers/) +* [6.3 Morton-order matrix class template](6.3-morton-order/) +* [7 Inheritance](7-inheritance/) +* [8 Using algorithms](8-algorithm/) +* [9 Eigen](9-eigen/) +* [10 Simple use of threads](10-threads/) diff --git a/lectures/course-intro/README.md b/lectures/0-course-intro/README.md similarity index 100% rename from lectures/course-intro/README.md rename to lectures/0-course-intro/README.md diff --git a/lectures/course-intro/index.html b/lectures/0-course-intro/index.html similarity index 100% rename from lectures/course-intro/index.html rename to lectures/0-course-intro/index.html diff --git a/lectures/cpp-intro/README.md b/lectures/1-cpp-intro/README.md similarity index 100% rename from lectures/cpp-intro/README.md rename to lectures/1-cpp-intro/README.md diff --git a/lectures/cpp-intro/auto/auto.cpp b/lectures/1-cpp-intro/auto/auto.cpp similarity index 100% rename from lectures/cpp-intro/auto/auto.cpp rename to lectures/1-cpp-intro/auto/auto.cpp diff --git a/lectures/cpp-intro/frank_mon.jpg b/lectures/1-cpp-intro/frank_mon.jpg similarity index 100% rename from lectures/cpp-intro/frank_mon.jpg rename to lectures/1-cpp-intro/frank_mon.jpg diff --git a/lectures/cpp-intro/hello/hello.cpp b/lectures/1-cpp-intro/hello/hello.cpp similarity index 100% rename from lectures/cpp-intro/hello/hello.cpp rename to lectures/1-cpp-intro/hello/hello.cpp diff --git a/lectures/cpp-intro/index.html b/lectures/1-cpp-intro/index.html similarity index 100% rename from lectures/cpp-intro/index.html rename to lectures/1-cpp-intro/index.html diff --git a/lectures/cpp-intro/octodog.jpg b/lectures/1-cpp-intro/octodog.jpg similarity index 100% rename from lectures/cpp-intro/octodog.jpg rename to lectures/1-cpp-intro/octodog.jpg diff --git a/lectures/cpp-intro/sak.jpg b/lectures/1-cpp-intro/sak.jpg similarity index 100% rename from lectures/cpp-intro/sak.jpg rename to lectures/1-cpp-intro/sak.jpg diff --git a/lectures/cpp-intro/sum/sum.cpp b/lectures/1-cpp-intro/sum/sum.cpp similarity index 100% rename from lectures/cpp-intro/sum/sum.cpp rename to lectures/1-cpp-intro/sum/sum.cpp diff --git a/lectures/threads-1/README.md b/lectures/10.1-threads/README.md similarity index 100% rename from lectures/threads-1/README.md rename to lectures/10.1-threads/README.md diff --git a/lectures/threads-1/index.html b/lectures/10.1-threads/index.html similarity index 100% rename from lectures/threads-1/index.html rename to lectures/10.1-threads/index.html diff --git a/lectures/threads-2/README.md b/lectures/10.2-threads-cont/README.md similarity index 100% rename from lectures/threads-2/README.md rename to lectures/10.2-threads-cont/README.md diff --git a/lectures/threads-2/index.html b/lectures/10.2-threads-cont/index.html similarity index 100% rename from lectures/threads-2/index.html rename to lectures/10.2-threads-cont/index.html diff --git a/lectures/threads-2/omp-v-thread.png b/lectures/10.2-threads-cont/omp-v-thread.png similarity index 100% rename from lectures/threads-2/omp-v-thread.png rename to lectures/10.2-threads-cont/omp-v-thread.png diff --git a/lectures/classes/README.md b/lectures/2-classes/README.md similarity index 100% rename from lectures/classes/README.md rename to lectures/2-classes/README.md diff --git a/lectures/classes/index.html b/lectures/2-classes/index.html similarity index 100% rename from lectures/classes/index.html rename to lectures/2-classes/index.html diff --git a/lectures/loops-containers/README.md b/lectures/3-loops-containers/README.md similarity index 100% rename from lectures/loops-containers/README.md rename to lectures/3-loops-containers/README.md diff --git a/lectures/loops-containers/domain_decomp.png b/lectures/3-loops-containers/domain_decomp.png similarity index 100% rename from lectures/loops-containers/domain_decomp.png rename to lectures/3-loops-containers/domain_decomp.png diff --git a/lectures/loops-containers/index.html b/lectures/3-loops-containers/index.html similarity index 100% rename from lectures/loops-containers/index.html rename to lectures/3-loops-containers/index.html diff --git a/lectures/resources/README.md b/lectures/4-resources/README.md similarity index 100% rename from lectures/resources/README.md rename to lectures/4-resources/README.md diff --git a/lectures/RAII-cont/index.html b/lectures/4-resources/index.html similarity index 100% rename from lectures/RAII-cont/index.html rename to lectures/4-resources/index.html diff --git a/lectures/RAII-cont/mem_layout.jpg b/lectures/4-resources/mem_layout.jpg similarity index 100% rename from lectures/RAII-cont/mem_layout.jpg rename to lectures/4-resources/mem_layout.jpg diff --git a/lectures/RAII-cont/sample/.gitignore b/lectures/4-resources/sample/.gitignore similarity index 100% rename from lectures/RAII-cont/sample/.gitignore rename to lectures/4-resources/sample/.gitignore diff --git a/lectures/RAII-cont/sample/Makefile b/lectures/4-resources/sample/Makefile similarity index 100% rename from lectures/RAII-cont/sample/Makefile rename to lectures/4-resources/sample/Makefile diff --git a/lectures/RAII-cont/sample/arr1.cpp b/lectures/4-resources/sample/arr1.cpp similarity index 100% rename from lectures/RAII-cont/sample/arr1.cpp rename to lectures/4-resources/sample/arr1.cpp diff --git a/lectures/RAII-cont/sample/arr2.cpp b/lectures/4-resources/sample/arr2.cpp similarity index 100% rename from lectures/RAII-cont/sample/arr2.cpp rename to lectures/4-resources/sample/arr2.cpp diff --git a/lectures/RAII-cont/sample/arr3.cpp b/lectures/4-resources/sample/arr3.cpp similarity index 100% rename from lectures/RAII-cont/sample/arr3.cpp rename to lectures/4-resources/sample/arr3.cpp diff --git a/lectures/RAII-cont/sample/dyn1.cpp b/lectures/4-resources/sample/dyn1.cpp similarity index 100% rename from lectures/RAII-cont/sample/dyn1.cpp rename to lectures/4-resources/sample/dyn1.cpp diff --git a/lectures/RAII-cont/sample/dyn2.cpp b/lectures/4-resources/sample/dyn2.cpp similarity index 100% rename from lectures/RAII-cont/sample/dyn2.cpp rename to lectures/4-resources/sample/dyn2.cpp diff --git a/lectures/RAII-cont/sample/dyn3.cpp b/lectures/4-resources/sample/dyn3.cpp similarity index 100% rename from lectures/RAII-cont/sample/dyn3.cpp rename to lectures/4-resources/sample/dyn3.cpp diff --git a/lectures/RAII-cont/sample/shared.cpp b/lectures/4-resources/sample/shared.cpp similarity index 100% rename from lectures/RAII-cont/sample/shared.cpp rename to lectures/4-resources/sample/shared.cpp diff --git a/lectures/templates/README.md b/lectures/5-templates/README.md similarity index 100% rename from lectures/templates/README.md rename to lectures/5-templates/README.md diff --git a/lectures/templates/index.html b/lectures/5-templates/index.html similarity index 100% rename from lectures/templates/index.html rename to lectures/5-templates/index.html diff --git a/lectures/RAII/README.md b/lectures/6.1-RAII/README.md similarity index 100% rename from lectures/RAII/README.md rename to lectures/6.1-RAII/README.md diff --git a/lectures/RAII/index.html b/lectures/6.1-RAII/index.html similarity index 100% rename from lectures/RAII/index.html rename to lectures/6.1-RAII/index.html diff --git a/lectures/RAII/mem_layout.jpg b/lectures/6.1-RAII/mem_layout.jpg similarity index 100% rename from lectures/RAII/mem_layout.jpg rename to lectures/6.1-RAII/mem_layout.jpg diff --git a/lectures/RAII/sample/.gitignore b/lectures/6.1-RAII/sample/.gitignore similarity index 100% rename from lectures/RAII/sample/.gitignore rename to lectures/6.1-RAII/sample/.gitignore diff --git a/lectures/RAII/sample/Makefile b/lectures/6.1-RAII/sample/Makefile similarity index 100% rename from lectures/RAII/sample/Makefile rename to lectures/6.1-RAII/sample/Makefile diff --git a/lectures/RAII/sample/arr1.cpp b/lectures/6.1-RAII/sample/arr1.cpp similarity index 100% rename from lectures/RAII/sample/arr1.cpp rename to lectures/6.1-RAII/sample/arr1.cpp diff --git a/lectures/RAII/sample/arr2.cpp b/lectures/6.1-RAII/sample/arr2.cpp similarity index 100% rename from lectures/RAII/sample/arr2.cpp rename to lectures/6.1-RAII/sample/arr2.cpp diff --git a/lectures/RAII/sample/arr3.cpp b/lectures/6.1-RAII/sample/arr3.cpp similarity index 100% rename from lectures/RAII/sample/arr3.cpp rename to lectures/6.1-RAII/sample/arr3.cpp diff --git a/lectures/RAII/sample/dyn1.cpp b/lectures/6.1-RAII/sample/dyn1.cpp similarity index 100% rename from lectures/RAII/sample/dyn1.cpp rename to lectures/6.1-RAII/sample/dyn1.cpp diff --git a/lectures/RAII/sample/dyn2.cpp b/lectures/6.1-RAII/sample/dyn2.cpp similarity index 100% rename from lectures/RAII/sample/dyn2.cpp rename to lectures/6.1-RAII/sample/dyn2.cpp diff --git a/lectures/RAII/sample/dyn3.cpp b/lectures/6.1-RAII/sample/dyn3.cpp similarity index 100% rename from lectures/RAII/sample/dyn3.cpp rename to lectures/6.1-RAII/sample/dyn3.cpp diff --git a/lectures/RAII/sample/shared.cpp b/lectures/6.1-RAII/sample/shared.cpp similarity index 100% rename from lectures/RAII/sample/shared.cpp rename to lectures/6.1-RAII/sample/shared.cpp diff --git a/lectures/RAII-cont/README.md b/lectures/6.2-RAII-cont/README.md similarity index 100% rename from lectures/RAII-cont/README.md rename to lectures/6.2-RAII-cont/README.md diff --git a/lectures/resources/index.html b/lectures/6.2-RAII-cont/index.html similarity index 100% rename from lectures/resources/index.html rename to lectures/6.2-RAII-cont/index.html diff --git a/lectures/resources/mem_layout.jpg b/lectures/6.2-RAII-cont/mem_layout.jpg similarity index 100% rename from lectures/resources/mem_layout.jpg rename to lectures/6.2-RAII-cont/mem_layout.jpg diff --git a/lectures/resources/sample/.gitignore b/lectures/6.2-RAII-cont/sample/.gitignore similarity index 100% rename from lectures/resources/sample/.gitignore rename to lectures/6.2-RAII-cont/sample/.gitignore diff --git a/lectures/resources/sample/Makefile b/lectures/6.2-RAII-cont/sample/Makefile similarity index 100% rename from lectures/resources/sample/Makefile rename to lectures/6.2-RAII-cont/sample/Makefile diff --git a/lectures/resources/sample/arr1.cpp b/lectures/6.2-RAII-cont/sample/arr1.cpp similarity index 100% rename from lectures/resources/sample/arr1.cpp rename to lectures/6.2-RAII-cont/sample/arr1.cpp diff --git a/lectures/resources/sample/arr2.cpp b/lectures/6.2-RAII-cont/sample/arr2.cpp similarity index 100% rename from lectures/resources/sample/arr2.cpp rename to lectures/6.2-RAII-cont/sample/arr2.cpp diff --git a/lectures/resources/sample/arr3.cpp b/lectures/6.2-RAII-cont/sample/arr3.cpp similarity index 100% rename from lectures/resources/sample/arr3.cpp rename to lectures/6.2-RAII-cont/sample/arr3.cpp diff --git a/lectures/resources/sample/dyn1.cpp b/lectures/6.2-RAII-cont/sample/dyn1.cpp similarity index 100% rename from lectures/resources/sample/dyn1.cpp rename to lectures/6.2-RAII-cont/sample/dyn1.cpp diff --git a/lectures/resources/sample/dyn2.cpp b/lectures/6.2-RAII-cont/sample/dyn2.cpp similarity index 100% rename from lectures/resources/sample/dyn2.cpp rename to lectures/6.2-RAII-cont/sample/dyn2.cpp diff --git a/lectures/resources/sample/dyn3.cpp b/lectures/6.2-RAII-cont/sample/dyn3.cpp similarity index 100% rename from lectures/resources/sample/dyn3.cpp rename to lectures/6.2-RAII-cont/sample/dyn3.cpp diff --git a/lectures/resources/sample/shared.cpp b/lectures/6.2-RAII-cont/sample/shared.cpp similarity index 100% rename from lectures/resources/sample/shared.cpp rename to lectures/6.2-RAII-cont/sample/shared.cpp diff --git a/lectures/Combining-classes/Polymorphism-in-CPP.png b/lectures/7-combining-classes/Polymorphism-in-CPP.png similarity index 100% rename from lectures/Combining-classes/Polymorphism-in-CPP.png rename to lectures/7-combining-classes/Polymorphism-in-CPP.png diff --git a/lectures/Combining-classes/README.md b/lectures/7-combining-classes/README.md similarity index 100% rename from lectures/Combining-classes/README.md rename to lectures/7-combining-classes/README.md diff --git a/lectures/Combining-classes/diamond-problem-in-cpp.webp b/lectures/7-combining-classes/diamond-problem-in-cpp.webp similarity index 100% rename from lectures/Combining-classes/diamond-problem-in-cpp.webp rename to lectures/7-combining-classes/diamond-problem-in-cpp.webp diff --git a/lectures/Combining-classes/index.html b/lectures/7-combining-classes/index.html similarity index 100% rename from lectures/Combining-classes/index.html rename to lectures/7-combining-classes/index.html diff --git a/lectures/algorithms-lambdas-traits/README.md b/lectures/8-algorithms-lambdas-traits/README.md similarity index 100% rename from lectures/algorithms-lambdas-traits/README.md rename to lectures/8-algorithms-lambdas-traits/README.md diff --git a/lectures/algorithms-lambdas-traits/index.html b/lectures/8-algorithms-lambdas-traits/index.html similarity index 100% rename from lectures/algorithms-lambdas-traits/index.html rename to lectures/8-algorithms-lambdas-traits/index.html diff --git a/lectures/algorithms-lambdas-traits/looptests/opt0.svg b/lectures/8-algorithms-lambdas-traits/looptests/opt0.svg similarity index 100% rename from lectures/algorithms-lambdas-traits/looptests/opt0.svg rename to lectures/8-algorithms-lambdas-traits/looptests/opt0.svg diff --git a/lectures/algorithms-lambdas-traits/looptests/opt2.svg b/lectures/8-algorithms-lambdas-traits/looptests/opt2.svg similarity index 100% rename from lectures/algorithms-lambdas-traits/looptests/opt2.svg rename to lectures/8-algorithms-lambdas-traits/looptests/opt2.svg diff --git a/lectures/eigen/README.md b/lectures/9-eigen/README.md similarity index 100% rename from lectures/eigen/README.md rename to lectures/9-eigen/README.md diff --git a/lectures/eigen/index.html b/lectures/9-eigen/index.html similarity index 100% rename from lectures/eigen/index.html rename to lectures/9-eigen/index.html diff --git a/lectures/README.md b/lectures/README.md index c29c0a4..c5861e1 100644 --- a/lectures/README.md +++ b/lectures/README.md @@ -1,14 +1,14 @@ # Lectures -* [Course introduction](course-intro) -* [A brief introduction to C++](cpp-intro) -* [Class types](classes) -* [Loops, containers, and iterators](loops-containers) -* [Managing resources](resources) -* [RAII](RAII) -* [Templates for generic programming](templates) -* [RAII continued](RAII-cont) -* [Combining Classes](Combining-classes) -* [Algorithms, lambdas and traits](algorithms-lambdas-traits) -* [Linear algebra with Eigen](eigen) -* [Threads with C++](threads-1) and [Further topics with threads](threads-2) +* [Course introduction](0-course-intro) +* [A brief introduction to C++](1-cpp-intro) +* [Class types](2-classes) +* [Loops, containers, and iterators](3-loops-containers) +* [Managing resources](4-resources) +* [RAII](6.1-RAII) +* [Templates for generic programming](5-templates) +* [RAII continued](6.2-RAII-cont) +* [Combining Classes](7-combining-classes) +* [Algorithms, lambdas and traits](8-algorithms-lambdas-traits) +* [Linear algebra with Eigen](9-eigen) +* [Threads with C++](10.1-threads) and [Further topics with threads](10.2-threads-cont) From 7e3a799c0c8298203f2ef0d9a0b826974fb59ddf Mon Sep 17 00:00:00 2001 From: nmannall Date: Thu, 17 Apr 2025 12:10:57 +0100 Subject: [PATCH 67/91] Update eigen exercise to run on ARCHER2 --- exercises/9-eigen/modules.sh | 2 +- exercises/9-eigen/movie.py | 47 ++++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/exercises/9-eigen/modules.sh b/exercises/9-eigen/modules.sh index dcc0da0..29f2a84 100644 --- a/exercises/9-eigen/modules.sh +++ b/exercises/9-eigen/modules.sh @@ -1,5 +1,5 @@ #!/bin/bash module load PrgEnv-gnu module load cray-python +module load matplotlib module load eigen/3.4.0 - diff --git a/exercises/9-eigen/movie.py b/exercises/9-eigen/movie.py index 01e149d..b419109 100644 --- a/exercises/9-eigen/movie.py +++ b/exercises/9-eigen/movie.py @@ -1,28 +1,39 @@ +import argparse import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation -n = 20 -steps = 200 +def plot(simulation): + n = 20 + steps = 200 -fig, ax = plt.subplots() -xdata = np.arange(0, n) -ydata = [] -ln, = ax.plot([], [], 'ro') + fig, ax = plt.subplots() + xdata = np.arange(0, n) + ydata = [] + ln, = ax.plot([], [], 'ro') -data = np.loadtxt("implicit_sim.txt") -def init(): - ax.set_xlim(0,n) - ax.set_ylim(0,1.1) - return ln, + data = np.loadtxt(f"{simulation}_sim.txt") -def update(frame): - ydata = data[frame] - ln.set_data(xdata, ydata) - return ln, + def init(): + ax.set_xlim(0,n) + ax.set_ylim(0,1.1) + return ln, -ani = FuncAnimation(fig, update, frames=np.arange(0, 200), - init_func=init, blit=True) + def update(frame): + ydata = data[frame] + ln.set_data(xdata, ydata) + return ln, + ani = FuncAnimation(fig, update, frames=np.arange(0, 200), + init_func=init, blit=True) -plt.show() + ani.save(f"{simulation}.gif", writer="pillow") + plt.show() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(prog='movie.py', description='Create animation from simulation output.') + parser.add_argument('simulation', default='implicit', const='implicit', nargs='?', choices=['implicit', 'explicit', 'sparse']) + + args = parser.parse_args() + plot(args.simulation) From bec368ba0d869320020405ff22db1e09c14666c1 Mon Sep 17 00:00:00 2001 From: nmannall Date: Thu, 17 Apr 2025 12:16:27 +0100 Subject: [PATCH 68/91] Update makefiles to C++17 --- exercises/2.1-class-types/Makefile | 2 +- exercises/2.2-complex/Makefile | 2 +- exercises/5-templates/part1/Makefile | 2 +- exercises/5-templates/part2/Makefile | 7 +------ 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/exercises/2.1-class-types/Makefile b/exercises/2.1-class-types/Makefile index cec9ba2..da851f6 100644 --- a/exercises/2.1-class-types/Makefile +++ b/exercises/2.1-class-types/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS = --std=c++14 -I../include +CXXFLAGS = --std=c++17 -I../include test : complex.o test.o $(CXX) $^ -o $@ diff --git a/exercises/2.2-complex/Makefile b/exercises/2.2-complex/Makefile index cec9ba2..da851f6 100644 --- a/exercises/2.2-complex/Makefile +++ b/exercises/2.2-complex/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS = --std=c++14 -I../include +CXXFLAGS = --std=c++17 -I../include test : complex.o test.o $(CXX) $^ -o $@ diff --git a/exercises/5-templates/part1/Makefile b/exercises/5-templates/part1/Makefile index 892ae1e..26db992 100644 --- a/exercises/5-templates/part1/Makefile +++ b/exercises/5-templates/part1/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS = --std=c++14 -I../../include +CXXFLAGS = --std=c++17 -I../../include sum : sum.o $(CXX) $^ -o $@ diff --git a/exercises/5-templates/part2/Makefile b/exercises/5-templates/part2/Makefile index 6c9dd97..ca3fc0c 100644 --- a/exercises/5-templates/part2/Makefile +++ b/exercises/5-templates/part2/Makefile @@ -1,13 +1,8 @@ -CXXFLAGS = --std=c++14 -I../../include +CXXFLAGS = --std=c++17 -I../../include test : complex.o test.o $(CXX) $^ -o $@ -test.o : catch.hpp - -catch.hpp : - wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp - run : test ./test From 030bbfa723e75bda9a5b369e99154694d12acdb1 Mon Sep 17 00:00:00 2001 From: nmannall Date: Thu, 17 Apr 2025 12:16:41 +0100 Subject: [PATCH 69/91] Update Catch version --- exercises/include/catch.hpp | 2838 +++++++++++++++++++++++------------ 1 file changed, 1850 insertions(+), 988 deletions(-) diff --git a/exercises/include/catch.hpp b/exercises/include/catch.hpp index 5feb2a4..36eaeb2 100644 --- a/exercises/include/catch.hpp +++ b/exercises/include/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.9.2 - * Generated: 2019-08-08 13:35:12.279703 + * Catch v2.13.6 + * Generated: 2021-04-16 18:23:38.044268 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 9 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 6 #ifdef __clang__ # pragma clang system_header @@ -66,13 +66,16 @@ #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX @@ -132,36 +135,51 @@ namespace Catch { #endif -#if defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + #endif -#ifdef __clang__ +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ @@ -186,6 +204,7 @@ namespace Catch { // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// @@ -219,11 +238,10 @@ namespace Catch { //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#ifdef _MSC_VER +#if defined(_MSC_VER) -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) // Universal Windows platform does not support SEH // Or console colours (or console at all...) @@ -236,9 +254,12 @@ namespace Catch { // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + #endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) @@ -286,49 +307,46 @@ namespace Catch { #define CATCH_CONFIG_COLOUR_NONE #endif -//////////////////////////////////////////////////////////////////////////////// -// Check if string_view is available and usable -// The check is split apart to work around v140 (VS2015) preprocessor issue... -#if defined(__has_include) -#if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -#endif +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER #endif -//////////////////////////////////////////////////////////////////////////////// -// Check if optional is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include - -//////////////////////////////////////////////////////////////////////////////// -// Check if byte is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_BYTE -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include - -//////////////////////////////////////////////////////////////////////////////// -// Check if variant is available and usable +// Various stdlib support checks that require __has_include #if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 -# include -# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# define CATCH_CONFIG_NO_CPP17_VARIANT -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__clang__) && (__clang_major__ < 8) -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER @@ -353,10 +371,6 @@ namespace Catch { # define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif @@ -389,21 +403,49 @@ namespace Catch { # define CATCH_CONFIG_USE_ASYNC #endif +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) @@ -468,7 +510,7 @@ namespace Catch { SourceLineInfo( SourceLineInfo&& ) noexcept = default; SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - bool empty() const noexcept; + bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; @@ -509,9 +551,10 @@ namespace Catch { } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h @@ -551,53 +594,30 @@ namespace Catch { #include #include #include +#include namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. c_str() must return a null terminated - /// string, however, and so the StringRef will internally take ownership - /// (taking a copy), if necessary. In theory this ownership is not externally - /// visible - but it does mean (substring) StringRefs should not be shared between - /// threads. + /// it may not be null terminated. class StringRef { public: using size_type = std::size_t; + using const_iterator = const char*; private: - friend struct StringRefTestAccess; - - char const* m_start; - size_type m_size; - - char* m_data = nullptr; - - void takeOwnership(); - static constexpr char const* const s_empty = ""; - public: // construction/ assignment - StringRef() noexcept - : StringRef( s_empty, 0 ) - {} - - StringRef( StringRef const& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ) - {} + char const* m_start = s_empty; + size_type m_size = 0; - StringRef( StringRef&& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ), - m_data( other.m_data ) - { - other.m_data = nullptr; - } + public: // construction + constexpr StringRef() noexcept = default; StringRef( char const* rawChars ) noexcept; - StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} @@ -607,101 +627,64 @@ namespace Catch { m_size( stdString.size() ) {} - ~StringRef() noexcept { - delete[] m_data; - } - - auto operator = ( StringRef const &other ) noexcept -> StringRef& { - delete[] m_data; - m_data = nullptr; - m_start = other.m_start; - m_size = other.m_size; - return *this; + explicit operator std::string() const { + return std::string(m_start, m_size); } - operator std::string() const; - - void swap( StringRef& other ) noexcept; - public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } - auto operator[] ( size_type index ) const noexcept -> char; + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } public: // named queries - auto empty() const noexcept -> bool { + constexpr auto empty() const noexcept -> bool { return m_size == 0; } - auto size() const noexcept -> size_type { + constexpr auto size() const noexcept -> size_type { return m_size; } - auto numberOfCharacters() const noexcept -> size_type; + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches - auto substr( size_type start, size_type size ) const noexcept -> StringRef; + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; - // Returns the current start pointer. - // Note that the pointer can change when if the StringRef is a substring - auto currentData() const noexcept -> char const*; + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; - private: // ownership queries - may not be consistent between calls - auto isOwned() const noexcept -> bool; - auto isSubstring() const noexcept -> bool; - }; + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; - auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } - } // namespace Catch -inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { return Catch::StringRef( rawChars, size ); } // end catch_stringref.h -// start catch_type_traits.hpp - - -#include - -namespace Catch{ - -#ifdef CATCH_CPP17_OR_GREATER - template - inline constexpr auto is_unique = std::true_type{}; - - template - inline constexpr auto is_unique = std::bool_constant< - (!std::is_same_v && ...) && is_unique - >{}; -#else - -template -struct is_unique : std::true_type{}; - -template -struct is_unique : std::integral_constant -::value - && is_unique::value - && is_unique::value ->{}; - -#endif -} - -// end catch_type_traits.hpp // start catch_preprocessor.hpp @@ -786,7 +769,7 @@ struct is_unique : std::integral_constant #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) @@ -798,35 +781,49 @@ struct is_unique : std::integral_constant template struct TypeList {};\ template\ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ \ - template class L1, typename...E1, template class L2, typename...E2> \ - constexpr auto append(L1, L2) noexcept -> L1 { return {}; }\ + template \ + struct append { using type = T; };\ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - constexpr auto append(L1, L2, Rest...) noexcept -> decltype(append(L1{}, Rest{}...)) { return {}; }\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ template< template class L1, typename...E1, typename...Rest>\ - constexpr auto append(L1, TypeList, Rest...) noexcept -> L1 { return {}; }\ + struct append, TypeList, Rest...> { using type = L1; };\ \ template< template class Container, template class List, typename...elems>\ - constexpr auto rewrap(List) noexcept -> TypeList> { return {}; }\ + struct rewrap, List> { using type = TypeList>; };\ template< template class Container, template class List, class...Elems, typename...Elements>\ - constexpr auto rewrap(List,Elements...) noexcept -> decltype(append(TypeList>{}, rewrap(Elements{}...))) { return {}; }\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ \ template