├── .clang-format ├── LICENSE ├── LessonOutlines ├── Lesson1.md ├── Lesson2.md ├── Lesson3.md ├── Lesson4.md ├── Lesson5.md └── Lesson6.md ├── MeetingCodes ├── Meeting1 │ ├── hello.cpp │ └── trap.cpp ├── Meeting10 │ ├── TaggedTuple.cpp │ ├── Tags.cpp │ ├── basic_tmpl.cpp │ ├── brigand.hpp │ ├── start │ │ ├── Tags.cpp │ │ ├── brigand.hpp │ │ └── tagged_tuple.hpp │ └── tagged_tuple.hpp ├── Meeting11 │ ├── basic_brigand.cpp │ ├── basic_tmpl.cpp │ ├── brigand.hpp │ ├── factorial.cpp │ ├── start │ │ ├── basic_tmpl.cpp │ │ ├── brigand.hpp │ │ └── tagged_tuple.hpp │ └── tagged_tuple.hpp ├── Meeting12 │ ├── ScaleBigScalarField.cpp │ ├── start │ │ └── README.md │ └── test_gsl.cpp ├── Meeting13 │ └── BigScalar.cpp ├── Meeting2 │ ├── constexpr.cpp │ ├── main.cpp │ ├── trap.cpp │ └── trap.hpp ├── Meeting3 │ ├── Cast.cpp │ ├── Main.cpp │ ├── Rectangle.cpp │ └── Rectangle.hpp ├── Meeting4 │ ├── Main.cpp │ ├── Polygon.hpp │ ├── Rectangle.cpp │ ├── Rectangle.hpp │ ├── Square.cpp │ ├── Square.hpp │ └── Template.cpp ├── Meeting5 │ ├── Containers.cpp │ └── Square.cpp ├── Meeting6 │ ├── ArraySum.cpp │ └── SpatialVector.cpp ├── Meeting7 │ ├── SpatialVector.cpp │ └── SpatialVector.hpp ├── Meeting8 │ ├── SpatialVector.cpp │ ├── SpatialVector.hpp │ ├── Trap.cpp │ └── start │ │ ├── SpatialVector.cpp │ │ └── SpatialVector.hpp └── Meeting9 │ ├── MathOnArray.cpp │ ├── Start │ ├── MathOnArray.cpp │ └── tagged_tuple.hpp │ ├── Tags.cpp │ └── tagged_tuple.hpp ├── README.md └── Solutions ├── Lesson1 ├── constexpr.cpp ├── hello.cpp ├── integrate │ ├── Left.cpp │ ├── Left.hpp │ ├── Main.cpp │ ├── Trap.cpp │ └── Trap.hpp └── trap.cpp ├── Lesson2 ├── Poly │ ├── Main.cpp │ ├── Polygon.hpp │ ├── Rectangle.cpp │ ├── Rectangle.hpp │ ├── RightTriangle.cpp │ ├── RightTriangle.hpp │ ├── Square.cpp │ └── Square.hpp └── Rect │ └── Rect.cpp ├── Lesson3 ├── Complex.cpp ├── Containers.cpp ├── SpatialVector.cpp └── Square.cpp ├── Lesson4 ├── MathOnArray.cpp ├── SpatialVector.cpp ├── SpatialVector.hpp ├── SpatialVectorStart.hpp ├── TaggedTuple.cpp ├── Tags.cpp ├── Trap.cpp └── tagged_tuple.hpp ├── Lesson5 ├── basic_brigand.cpp ├── basic_tmpl_list.cpp ├── brigand.hpp ├── factorial.cpp └── tagged_tuple.hpp └── Lesson6 ├── ReferenceVsMove.cpp └── ScaleBigScalar.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # We'll use defaults from the Google style. 3 | # See http://clang.llvm.org/docs/ClangFormat.html for help. 4 | # 5 | # We include the full configuration to avoid changes with our code 6 | # formatting when there are changes added in clang-format or changes 7 | # in Google's style 8 | Language: Cpp 9 | 10 | # BasedOnStyle: Google 11 | AccessModifierOffset: -1 12 | AlignAfterOpenBracket: Align 13 | AlignConsecutiveAssignments: false 14 | AlignConsecutiveDeclarations: false 15 | AlignEscapedNewlines: Left 16 | AlignOperands: true 17 | AlignTrailingComments: true 18 | AllowAllParametersOfDeclarationOnNextLine: true 19 | AllowShortBlocksOnASingleLine: false 20 | AllowShortCaseLabelsOnASingleLine: false 21 | AllowShortFunctionsOnASingleLine: All 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakBeforeMultilineStrings: true 25 | AlwaysBreakTemplateDeclarations: true 26 | BinPackArguments: true 27 | BinPackParameters: true 28 | BraceWrapping: 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | BeforeCatch: false 38 | BeforeElse: false 39 | IndentBraces: false 40 | SplitEmptyFunction: true 41 | SplitEmptyRecord: true 42 | SplitEmptyNamespace: true 43 | BreakBeforeBinaryOperators: None 44 | BreakBeforeBraces: Attach 45 | BreakBeforeInheritanceComma: false 46 | BreakBeforeTernaryOperators: true 47 | BreakConstructorInitializersBeforeComma: false 48 | BreakConstructorInitializers: BeforeColon 49 | BreakAfterJavaFieldAnnotations: false 50 | BreakStringLiterals: true 51 | ColumnLimit: 80 52 | CommentPragmas: '^ IWYU pragma:' 53 | CompactNamespaces: false 54 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 55 | ConstructorInitializerIndentWidth: 4 56 | ContinuationIndentWidth: 4 57 | Cpp11BracedListStyle: true 58 | DisableFormat: false 59 | ExperimentalAutoDetectBinPacking: false 60 | FixNamespaceComments: true 61 | ForEachMacros: 62 | - foreach 63 | - Q_FOREACH 64 | - BOOST_FOREACH 65 | IncludeCategories: 66 | - Regex: '^<.*\.h>' 67 | Priority: 1 68 | - Regex: '^<.*' 69 | Priority: 2 70 | - Regex: '.*' 71 | Priority: 3 72 | IncludeIsMainRegex: '([-_](test|unittest))?$' 73 | IndentCaseLabels: true 74 | IndentWidth: 2 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: false 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: None 83 | ObjCBlockIndentWidth: 2 84 | ObjCSpaceAfterProperty: false 85 | ObjCSpaceBeforeProtocolList: false 86 | PenaltyBreakAssignment: 2 87 | PenaltyBreakBeforeFirstCallParameter: 1 88 | PenaltyBreakComment: 300 89 | PenaltyBreakFirstLessLess: 120 90 | PenaltyBreakString: 1000 91 | PenaltyExcessCharacter: 1000000 92 | PenaltyReturnTypeOnItsOwnLine: 200 93 | ReflowComments: true 94 | SortIncludes: true 95 | SortUsingDeclarations: true 96 | SpaceAfterCStyleCast: false 97 | SpaceAfterTemplateKeyword: true 98 | SpaceBeforeAssignmentOperators: true 99 | SpaceBeforeParens: ControlStatements 100 | SpaceInEmptyParentheses: false 101 | SpacesBeforeTrailingComments: 2 102 | SpacesInAngles: false 103 | SpacesInContainerLiterals: true 104 | SpacesInCStyleCastParentheses: false 105 | SpacesInParentheses: false 106 | SpacesInSquareBrackets: false 107 | Standard: Auto 108 | TabWidth: 8 109 | UseTab: Never 110 | 111 | AllowShortIfStatementsOnASingleLine: false 112 | AllowShortLoopsOnASingleLine: false 113 | PointerAlignment: Left 114 | DerivePointerAlignment: false 115 | IncludeCategories: 116 | - Regex: '^<.*' 117 | Priority: 1 118 | - Regex: '.*' 119 | Priority: 2 120 | ... 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Geoffrey Lovelace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LessonOutlines/Lesson1.md: -------------------------------------------------------------------------------- 1 | ## Lesson 1 2 | 3 | ### Meeting 1 4 | 5 | - Hello 6 | 7 | - Add an arg that isn’t const 8 | 9 | - Make function use to compute sqrt(1-x^2) 10 | 11 | - Add evil_print() function that prints x then changes it, show how const 12 | saves you from evil_print() 13 | 14 | - const and constexpr for variables 15 | 16 | - Add function to do left integral 17 | 18 | - noexcept: enables compiler optimizations, says your code will not throw an exception 19 | 20 | ### Meeting 2 21 | 22 | - constexpr example 23 | 24 | - remake our example computing pi with the left-point rule 25 | 26 | - Add correction to do trap instead of left, show how error drops 27 | 28 | - Add overload taking function pointer, instead of hard-coding function f 29 | 30 | - Challenge: add your own function that returns exp(x) 31 | 32 | - Factor trap into hpp, cpp 33 | 34 | - Copy trap into hpp, cpp, 35 | 36 | - Add a new class, Left.hpp and Left.cpp 37 | 38 | - Challenge: have the executable print out difference from Pi for both Trap and Left 39 | 40 | - spectre examples of functions, const, noexcept, constexpr: irreducible mass, christodoulou mass, ... 41 | -------------------------------------------------------------------------------- /LessonOutlines/Lesson2.md: -------------------------------------------------------------------------------- 1 | ## Lesson 2 2 | 3 | - Start with Hello 4 | 5 | - Empty square struct 6 | 7 | - Length, width variables, print perimeter 8 | 9 | - Perimeter function 10 | 11 | - Constructor function instead of setting members by hand 12 | 13 | - Challenge: make a second rectangle, rect2, and check that the perimeter is correct 14 | 15 | - Private length and width 16 | 17 | - Struct vs class 18 | 19 | - Challenge: add a member function that returns the area 20 | 21 | - Add evil_perimeter() that changes the length after computing the area, show how 22 | const member functions prevents this 23 | 24 | - Add static const number_of_sides variable and function: same for all rectangles, introduce , size_t 25 | 26 | - Change static variable to static function that takes no arguments and returns the number of sides 27 | 28 | - Split into hpp, cpp ... #pragma once to avoid multiple definitions of rect 29 | 30 | - Make class square inherit from rectangle (': public Rectangle' after class name), different constructor 31 | 32 | - Make new base class polygon with pure virtual functions for area and perimeter 33 | 34 | - Make rect inherit from polygon, override virtual functions 35 | 36 | - Override keyword: ensures that base class functions are pure virtual. If base class function signatures change, this will catch it, instead of assuming you just added an overload instead 37 | 38 | - Challenge: make a RightTriangle class that inherits from Polygon, and print a right triangle's area and perimeter 39 | 40 | - Bonus challenge: make an abstract base class `Function` with a virtual member function `double operator()(const double x)`. Make a concrete class `ExpX` that returns exp(x) for operator(). You can then call an object of type `ExpX` the same way you call a function. Modify the trapezoid rule code from lesson one to accept objects of type `Function` instead of function pointers. 41 | 42 | - Spectre example: MathFunctions 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /LessonOutlines/Lesson3.md: -------------------------------------------------------------------------------- 1 | ## Lesson 3 2 | 3 | - Recap: a "square" function templated on type 4 | - Demonstrate calling for double, int, and manually setting type 5 | - Make a "double" and "halve" function 6 | - Using templates: std::array and std::vector demo 7 | 8 | - Challenge: Build a complex struct using doubles for re, im part 9 | - Add free functions for real, imag, abs, arg, +, -, scale by real factor 10 | - Challenge: Add functions for *, / 11 | - Add a print() function that prints complex numbers 12 | - Template Complex and free functions on type, try with double, int 13 | 14 | - SpatialVector 15 | - Start with a 3D vector of doubles 16 | - Constructor 17 | - runtime get function 18 | - runtime error for size out of bounds 19 | - template on type 20 | - compile-time bounds check for get<> 21 | - constexpr auto get(), index is template parameter 22 | - store volume_dim, data_type in class 23 | - static assert 24 | - call runtime get (return const auto&) 25 | - template on dimension 26 | - tempate on frame 27 | - Dot 28 | 29 | - Challenge: implement cross product for SpatialVector, checking dimension is 2 or 3 at compile time 30 | - Challenge: implement SpacetimeVector 31 | - Challenge: implement spatial rank-2 tensor 32 | 33 | - Import valarray as a stand-in for DataVector 34 | - Overload operator<< for valarray 35 | - Try out complex valarray 36 | - Challenge: try a large complex DataVector...how big before it gets slow? 37 | - Bonus challenge: Revisit the integrator from lesson 1. Change f(x) to a templated type instead of a function pointer type, and try passing in a struct that has operator() overloaded inside it 38 | 39 | 40 | -------------------------------------------------------------------------------- /LessonOutlines/Lesson4.md: -------------------------------------------------------------------------------- 1 | ## Lesson 4 2 | - Template wrap-up: SpatialVector of DataVector 3 | - Clean-up and test SpatialVector 4 | - Start with last time's SpatialVector code 5 | - Rename to SpatialVector.hpp, remove main() 6 | - Add container to_string() 7 | - Add SpatialVector to_string() 8 | - In new SpatialVector.cpp, print out dot product of vector of doubles in 2D 9 | - Draw the idea of a grid and our 1D representation 10 | - valarray as DataVector stand-in 11 | - In SpatialVector.cpp, make DataVectors to loop over points of a grid in 2D 12 | - In SpatialVector.cpp, make a spatial vector of the coordinates 13 | - In SpatialVector.cpp, print the dot product 14 | - In SpatialVector.cpp, print out a gaussian in 2D 15 | - Lambdas and functors 16 | - Revisit trapezoid integrator 17 | - Template on type instead of taking a function pointer 18 | - Pass in a function 19 | - Pass in a functor (struct with operator() const member function) 20 | - Pass in a lambda 21 | - Lambdas continued 22 | - Start with a simple print_container() function 23 | - std::iota to make a list of doubles 24 | - apply_math_op closure that squares data in place 25 | - power, pow, as an example of lambda capture 26 | - const lambdas can't modify captures 27 | - Intro to metaprogramming: TaggedTuple 28 | - std::pair: pair of two things of differnt types (e.g. string, double) 29 | - std::tuple: list of variables of different types (e.g. string, 2 doubles) 30 | - idea: label items in memory to access (a "databox") 31 | - spec: access at runtime (keys are strings) 32 | - spectre: access at compile time (keys are types) 33 | - realize this: a DataBox is a tuple of pairs, first type labels item, second type is the type of the item's value 34 | - example: rectangle area 35 | - example: if missing tag, you get a compiler error 36 | - TaggedTuple version of the rectangle area example 37 | -------------------------------------------------------------------------------- /LessonOutlines/Lesson5.md: -------------------------------------------------------------------------------- 1 | ## Lesson 5 2 | - Template metaprogramming 3 | - basic tmpl::list<> 4 | - structs for lists of zero, 1, 2, ... types 5 | - '...' and template parameter packs 6 | - basic integral constant type: 7 | - first parameter is type of value, second is the value 8 | - has member static const value and alias value_type for type of value 9 | - plus : inherits from integral constant, value is the sum 10 | - challenge: implement other arithmetic operations using tmpl::list and tmpl::integral_constant 11 | - boolean constant as specialization of int 12 | - boolen: less 13 | - challenge: implement other comparison operations 14 | - bonus example: if_ 15 | - brigand 16 | - redo the basic example using brigand functions 17 | - example algorithm: sort integral types 18 | -------------------------------------------------------------------------------- /LessonOutlines/Lesson6.md: -------------------------------------------------------------------------------- 1 | - gsl::not_null, gsl::at 2 | - BigScalarField 3 | - Make a scalar field class, with the field stored as a std::vecto and the size as a member variable 4 | - Add a resize function to change the size of a scalar field 5 | 6 | - Add a free function initialize_grid_scalar_field that initializes a scalar field to a 1D array of integers 7 | 8 | - Add a function that (by value) scales the scalar by a double 9 | 10 | - Try out the function for a small size (10): create a vector and print the value in the middle (at size / 2) 11 | 12 | - Increase the size until it takes 10-20 seconds to run, even with -O3 13 | 14 | - Include , measure timing 15 | 16 | - Replace the scale function with a version that returns by not_null, have the version that returns by value call the non-null version 17 | 18 | - change [size / 2] to [size + 40]...undefined behavior 19 | - replace with gsl::at...code terminates 20 | - fix all [] to gsl::at 21 | 22 | - add a std::move version of the scale_scalar_field function 23 | - delete the copy construtor and copy assignment, and all uses of it 24 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting1/hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | std::string name = "Geoffrey"; 6 | std::cout << "Hello world, " << name << "\n"; 7 | } 8 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting1/trap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /// function \f$ \sqrt{1 - x^2} \f$ for testing numerical integration 6 | double f(const double x) noexcept { 7 | // x = x * 2.0; // nope: x is const 8 | return sqrt(fabs(1.0 - x * x)); 9 | } 10 | 11 | /// Function to numerically integrate using left midpoint rule 12 | /// Computes \f$\int_a^b dx f(x)\f$. Here `lower_bound` == \f$a\f$, 13 | /// `upper_bound` == \f$b\f$, `step_size` == \f$dx\f$. 14 | double left(const double step_size, const double lower_bound, 15 | const double upper_bound) noexcept { 16 | double result = 0.0; 17 | for (double x = lower_bound; x < upper_bound; x += step_size) { 18 | result += step_size * f(x); 19 | } 20 | return result; 21 | } 22 | 23 | int main() { 24 | constexpr double test_number = 0.5; 25 | 26 | constexpr double test_lower_bound = 0.0; 27 | constexpr double test_upper_bound = 1.0; 28 | constexpr double test_step_size = 1.0e-11; 29 | 30 | const double pi_numerical = 31 | 4.0 * left(test_step_size, test_lower_bound, test_upper_bound); 32 | std::cout << std::setprecision(15); 33 | std::cout << pi_numerical << "\n"; 34 | std::cout << "Error: " << pi_numerical - M_PI << "\n"; 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting10/TaggedTuple.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tagged_tuple.hpp" 4 | 5 | namespace Rectangle { 6 | struct Length { 7 | using type = double; // Rectangle::Length::type if you want to know the tpye 8 | // of data stored by tag Length 9 | }; 10 | struct Height { 11 | using type = double; 12 | }; 13 | struct Area { 14 | using type = double; 15 | }; 16 | } // namespace Rectangle 17 | 18 | template 19 | double area(const T& box) noexcept { 20 | return tuples::get(box) * 21 | tuples::get(box); 22 | } 23 | 24 | int main() { 25 | tuples::tagged_tuple 26 | box{}; 27 | tuples::get(box) = 4.0; 28 | tuples::get(box) = 5.0; 29 | tuples::get(box) = area(box); 30 | 31 | std::cout << tuples::get(box) << "\n"; 32 | 33 | tuples::tagged_tuple box_no_height{}; 34 | tuples::get(box_no_height) = 4.0; 35 | 36 | // Compiler error: height not in box 37 | // std::cout << area(box_no_height) << "\n"; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting10/Tags.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace Rectangle { 7 | struct Length {}; 8 | struct Height {}; 9 | struct Area {}; 10 | } // namespace Rectangle 11 | 12 | template 13 | double area(const T& box) noexcept { 14 | const double length = 15 | std::get>(box).second; 16 | const double height = 17 | std::get>(box).second; 18 | return length * height; 19 | } 20 | 21 | int main() { 22 | std::tuple test_tuple{"Width", 4.0}; 23 | std::cout << std::get<0>(test_tuple) << " " << std::get<1>(test_tuple) 24 | << "\n"; 25 | 26 | using Length = std::pair; 27 | using Height = std::pair; 28 | std::tuple box{}; 29 | std::get(box).second = 4.0; 30 | std::get(box).second = 5.0; 31 | 32 | std::cout << std::get(box).second << "\n"; 33 | 34 | std::cout << area(box) << "\n"; 35 | 36 | std::tuple box_no_height{}; 37 | std::get(box_no_height).second = 4.0; 38 | 39 | // Compiler error: height not in box 40 | // std::cout << area(box_no_height) << "\n"; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting10/basic_tmpl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tmpl { 4 | template 5 | struct list {}; 6 | 7 | template 8 | struct integral_constant { 9 | static const T value = Value; // tmpl::integral_constant::value is 4 10 | using value_type = T; // allows tmpl::integral_constant::type is int 11 | }; 12 | 13 | template 14 | struct plus 15 | : integral_constant {}; 16 | } // namespace tmpl 17 | 18 | template 19 | struct TypeDisplayer; 20 | 21 | int main() { 22 | using test = tmpl::list; 23 | 24 | using three = tmpl::integral_constant; 25 | using four = tmpl::integral_constant; 26 | using five = tmpl::integral_constant; 27 | 28 | using sum = tmpl::plus; 29 | using result = tmpl::integral_constant; 30 | 31 | // TypeDisplayer print_test; 32 | // TypeDisplayer print_four; 33 | 34 | TypeDisplayer print_sum; 35 | TypeDisplayer print_result; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting10/start/Tags.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace Rectangle { 7 | struct Length {}; 8 | struct Height {}; 9 | struct Area {}; 10 | } // namespace Rectangle 11 | 12 | template 13 | double area(const T& box) noexcept { 14 | const double length = 15 | std::get>(box).second; 16 | const double height = 17 | std::get>(box).second; 18 | return length * height; 19 | } 20 | 21 | int main() { 22 | std::tuple test_tuple{"Width", 4.0}; 23 | std::cout << std::get<0>(test_tuple) << " " << std::get<1>(test_tuple) 24 | << "\n"; 25 | 26 | using Length = std::pair; 27 | using Height = std::pair; 28 | std::tuple box{}; 29 | std::get(box).second = 4.0; 30 | std::get(box).second = 5.0; 31 | 32 | std::cout << std::get(box).second << "\n"; 33 | 34 | std::cout << area(box) << "\n"; 35 | 36 | std::tuple box_no_height{}; 37 | std::get(box_no_height).second = 4.0; 38 | 39 | // Compiler error: height not in box 40 | // std::cout << area(box_no_height) << "\n"; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting10/tagged_tuple.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | @file 3 | @copyright Nils Deppe 2017 4 | (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tuples { 14 | 15 | #if __cplusplus >= 201402L 16 | #define TUPLES_LIB_CONSTEXPR_CXX_14 constexpr 17 | #else 18 | #define TUPLES_LIB_CONSTEXPR_CXX_14 19 | #endif 20 | 21 | namespace tuples_detail { 22 | 23 | template 24 | inline constexpr T&& forward( 25 | typename std::remove_reference::type& t) noexcept { 26 | return static_cast(t); 27 | } 28 | 29 | template 30 | inline constexpr T&& forward( 31 | typename std::remove_reference::type&& t) noexcept { 32 | static_assert(!std::is_lvalue_reference::value, 33 | "cannot forward an rvalue as an lvalue"); 34 | return static_cast(t); 35 | } 36 | 37 | template 38 | struct value_list {}; 39 | 40 | template 41 | struct typelist {}; 42 | 43 | template 44 | struct make_void { 45 | typedef void type; 46 | }; 47 | template 48 | using void_t = typename make_void::type; 49 | 50 | template 51 | using all = typename std::is_same< 52 | value_list, 53 | value_list(Bs), true)...>>::type; 54 | 55 | struct no_such_type { 56 | no_such_type() = delete; 57 | no_such_type(no_such_type const& /*unused*/) = delete; 58 | no_such_type(no_such_type&& /*unused*/) = delete; 59 | ~no_such_type() = delete; 60 | no_such_type& operator=(no_such_type const& /*unused*/) = delete; 61 | no_such_type operator=(no_such_type&& /*unused*/) = delete; 62 | }; 63 | 64 | namespace detail { 65 | using std::swap; 66 | 67 | template ::value and not std::is_void::value> 69 | struct is_swappable_with { 70 | template 71 | static auto test_swap(int) 72 | -> decltype(swap(std::declval(), std::declval())); 73 | template 74 | static tuples::tuples_detail::no_such_type test_swap(...); 75 | 76 | static const bool value = 77 | not std::is_same(0)), 78 | tuples::tuples_detail::no_such_type>::value and 79 | not std::is_same(0)), 80 | tuples::tuples_detail::no_such_type>::value; 81 | }; 82 | 83 | template 84 | struct is_swappable_with : std::false_type {}; 85 | } // namespace detail 86 | 87 | template 88 | using is_swappable_with = detail::is_swappable_with; 89 | 90 | template ::value> 91 | struct is_nothrow_swappable_with { 92 | private: 93 | static constexpr bool check() { 94 | using std::swap; 95 | return noexcept(swap(std::declval(), std::declval())) and 96 | noexcept(swap(std::declval(), std::declval())); 97 | } 98 | 99 | public: 100 | static const bool value = check(); 101 | }; 102 | 103 | template 104 | struct is_nothrow_swappable_with : std::false_type {}; 105 | 106 | template 107 | constexpr char swallow(Ts&&...) noexcept { 108 | return '0'; 109 | } 110 | } // namespace tuples_detail 111 | 112 | namespace tuples_detail { 113 | template ::value && 115 | !__is_final(typename Tag::type)> 116 | class tagged_tuple_leaf; 117 | 118 | template 119 | void swap(tagged_tuple_leaf& lhs, tagged_tuple_leaf& rhs) noexcept( 120 | is_nothrow_swappable_with::value) { 121 | using std::swap; 122 | swap(lhs.get(), rhs.get()); 123 | } 124 | 125 | template 126 | class tagged_tuple_leaf { 127 | using value_type = typename Tag::type; 128 | value_type value_; 129 | 130 | template 131 | static constexpr bool can_bind_reference() noexcept { 132 | using rem_ref_value_type = typename std::remove_reference::type; 133 | using rem_ref_T = typename std::remove_reference::type; 134 | using is_lvalue_type = std::integral_constant< 135 | bool, 136 | std::is_lvalue_reference::value or 137 | std::is_same, 138 | rem_ref_T>::value or 139 | std::is_same::type>, 141 | rem_ref_T>::value>; 142 | return not std::is_reference::value or 143 | (std::is_lvalue_reference::value and 144 | is_lvalue_type::value) or 145 | (std::is_rvalue_reference::value and 146 | not std::is_lvalue_reference::value); 147 | } 148 | 149 | public: 150 | // Tested in constexpr context in Unit.tagged_tuple.Ebo 151 | constexpr tagged_tuple_leaf() noexcept( 152 | std::is_nothrow_default_constructible::value) 153 | : value_() { 154 | static_assert( 155 | !std::is_reference::value, 156 | "Cannot default construct a reference element in a tagged_tuple"); 157 | } 158 | 159 | template ::type, 162 | tagged_tuple_leaf>::value && 163 | std::is_constructible::value>::type* = nullptr> 164 | constexpr explicit tagged_tuple_leaf(T&& t) noexcept( 165 | std::is_nothrow_constructible::value) 166 | : value_(tuples_detail::forward(t)) { 167 | static_assert(can_bind_reference(), 168 | "Cannot construct an lvalue reference with an rvalue"); 169 | } 170 | 171 | constexpr tagged_tuple_leaf(tagged_tuple_leaf const& /*rhs*/) = default; 172 | constexpr tagged_tuple_leaf(tagged_tuple_leaf&& /*rhs*/) = default; 173 | 174 | #if __cplusplus < 201402L 175 | value_type& get() noexcept { return value_; } 176 | #else 177 | constexpr value_type& get() noexcept { return value_; } 178 | #endif 179 | constexpr const value_type& get() const noexcept { return value_; } 180 | 181 | bool swap(tagged_tuple_leaf& t) noexcept( 182 | is_nothrow_swappable_with::value) { 183 | using std::swap; 184 | swap(*this, t); 185 | return false; 186 | } 187 | }; 188 | 189 | template 190 | class tagged_tuple_leaf : private Tag::type { 191 | using value_type = typename Tag::type; 192 | 193 | public: 194 | constexpr tagged_tuple_leaf() noexcept( 195 | std::is_nothrow_default_constructible::value) 196 | : value_type{} {} 197 | 198 | template ::type, 201 | tagged_tuple_leaf>::value && 202 | std::is_constructible::value>::type* = nullptr> 203 | constexpr explicit tagged_tuple_leaf(T&& t) noexcept( 204 | std::is_nothrow_constructible::value) 205 | : value_type(tuples_detail::forward(t)) {} 206 | 207 | constexpr tagged_tuple_leaf(tagged_tuple_leaf const& /*rhs*/) = default; 208 | constexpr tagged_tuple_leaf(tagged_tuple_leaf&& /*rhs*/) = default; 209 | 210 | #if __cplusplus < 201402L 211 | value_type& get() noexcept { return static_cast(*this); } 212 | #else 213 | constexpr value_type& get() noexcept { 214 | return static_cast(*this); 215 | } 216 | #endif 217 | 218 | constexpr const value_type& get() const noexcept { 219 | return static_cast(*this); 220 | } 221 | 222 | bool swap(tagged_tuple_leaf& t) noexcept( 223 | is_nothrow_swappable_with::value) { 224 | using std::swap; 225 | swap(*this, t); 226 | return false; 227 | } 228 | }; 229 | 230 | struct disable_constructors { 231 | static constexpr bool enable_default() noexcept { return false; } 232 | static constexpr bool enable_explicit() noexcept { return false; } 233 | static constexpr bool enable_implicit() noexcept { return false; } 234 | }; 235 | } // namespace tuples_detail 236 | 237 | /*! 238 | * \brief A compile time associative container between structs and data 239 | * 240 | * The template parameters to `tagged_tuple` are `struct`s with a member type 241 | * alias, `type` denoting the type of the data to be associated with the tag. 242 | * An example tag is: 243 | * \snippet runtime_tests.cpp example_tag_simple 244 | * A tagged_tuple is created by, for example 245 | * \snippet runtime_tests.cpp example_simple_create 246 | * Elements are retrieved using the `get` functions as follows: 247 | * \snippet runtime_tests.cpp example_get_function 248 | */ 249 | template 250 | class tagged_tuple; 251 | 252 | template 253 | constexpr const typename Tag::type& get( 254 | const tagged_tuple& t) noexcept; 255 | template 256 | constexpr typename Tag::type& get(tagged_tuple& t) noexcept; 257 | template 258 | constexpr const typename Tag::type&& get( 259 | const tagged_tuple&& t) noexcept; 260 | template 261 | constexpr typename Tag::type&& get(tagged_tuple&& t) noexcept; 262 | 263 | /*! 264 | * \brief Metafunction that returns the type of the Tag 265 | */ 266 | template 267 | using tag_type = typename Tag::type; 268 | 269 | template 270 | class tagged_tuple : private tuples_detail::tagged_tuple_leaf... { 271 | template 272 | struct pack_is_tagged_tuple : std::false_type {}; 273 | template 274 | struct pack_is_tagged_tuple 275 | : std::is_same::type, tagged_tuple> {}; 276 | 277 | template 278 | struct args_constructor : tuples_detail::disable_constructors {}; 279 | 280 | template 281 | struct args_constructor { 282 | static constexpr bool enable_default() { 283 | return tuples_detail::all< 284 | std::is_default_constructible>::value...>::value; 285 | } 286 | 287 | template 288 | static constexpr bool enable_explicit() noexcept { 289 | return tuples_detail::all< 290 | std::is_constructible, 291 | Ts>::value...>::value and 292 | not tuples_detail::all< 293 | std::is_convertible>::value...>::value; 294 | } 295 | template 296 | static constexpr bool enable_implicit() noexcept { 297 | return sizeof...(Ts) == sizeof...(Tags) and 298 | tuples_detail::all< 299 | std::is_constructible, 300 | Ts>::value...>::value and 301 | tuples_detail::all< 302 | std::is_convertible>::value...>::value; 303 | } 304 | }; 305 | 306 | template 308 | struct tuple_like_constructor : tuples_detail::disable_constructors {}; 309 | 310 | template 311 | struct tuple_like_constructor { 312 | template 313 | static constexpr bool enable_explicit() noexcept { 314 | return not tuples_detail::all< 315 | std::is_convertible>::value...>::value; 316 | } 317 | 318 | template 319 | static constexpr bool enable_implicit() noexcept { 320 | return tuples_detail::all< 321 | std::is_convertible>::value...>::value; 322 | } 323 | }; 324 | 325 | template 326 | struct tuple_like_constructor { 327 | template 328 | static constexpr bool enable_explicit() noexcept { 329 | return not tuples_detail::all< 330 | std::is_convertible>::value...>::value and 331 | (not tuples_detail::all>::value...>::value and 333 | not tuples_detail::all, Tuple>::value...>::value and 335 | not tuples_detail::all< 336 | std::is_same, Ts>::value...>::value); 337 | } 338 | 339 | template 340 | static constexpr bool enable_implicit() noexcept { 341 | return tuples_detail::all< 342 | std::is_convertible>::value...>::value and 343 | (not tuples_detail::all>::value...>::value and 345 | not tuples_detail::all, Tuple>::value...>::value and 347 | not tuples_detail::all< 348 | std::is_same, Ts>::value...>::value); 349 | } 350 | }; 351 | 352 | // C++17 Draft 23.5.3.2 Assignment - helper aliases 353 | using is_copy_assignable = 354 | tuples_detail::all>::value...>; 355 | using is_nothrow_copy_assignable = tuples_detail::all< 356 | std::is_nothrow_copy_assignable>::value...>; 357 | using is_move_assignable = 358 | tuples_detail::all>::value...>; 359 | using is_nothrow_move_assignable = tuples_detail::all< 360 | std::is_nothrow_move_assignable>::value...>; 361 | 362 | template 363 | friend constexpr const typename Tag::type& get( 364 | const tagged_tuple& t) noexcept; 365 | template 366 | friend constexpr typename Tag::type& get(tagged_tuple& t) noexcept; 367 | template 368 | friend constexpr const typename Tag::type&& get( 369 | const tagged_tuple&& t) noexcept; 370 | template 371 | friend constexpr typename Tag::type&& get( 372 | tagged_tuple&& t) noexcept; 373 | 374 | public: 375 | // C++17 Draft 23.5.3.1 Construction 376 | template ::enable_default()>::type* = nullptr> 379 | constexpr tagged_tuple() noexcept( 380 | tuples_detail::all>::value...>::value) {} 382 | 383 | constexpr tagged_tuple(tagged_tuple const& /*rhs*/) = default; 384 | constexpr tagged_tuple(tagged_tuple&& /*rhs*/) = default; 385 | 386 | template < 387 | bool Dummy = true, 388 | typename std::enable_if::template enable_explicit< 389 | tag_type const&...>()>::type* = nullptr> 390 | constexpr explicit tagged_tuple(tag_type const&... ts) noexcept( 391 | tuples_detail::all>::value...>::value) 393 | : tuples_detail::tagged_tuple_leaf(ts)... {} 394 | 395 | template < 396 | bool Dummy = true, 397 | typename std::enable_if::template enable_implicit< 398 | tag_type const&...>()>::type* = nullptr> 399 | constexpr tagged_tuple(tag_type const&... ts) noexcept( 400 | tuples_detail::all>::value...>::value) 402 | : tuples_detail::tagged_tuple_leaf(ts)... {} 403 | 404 | template ::value and 407 | sizeof...(Us) == sizeof...(Tags)>:: 408 | template enable_explicit()>::type* = nullptr> 409 | constexpr explicit tagged_tuple(Us&&... us) noexcept( 410 | tuples_detail::all, Us&&>::value...>::value) 412 | : tuples_detail::tagged_tuple_leaf( 413 | tuples_detail::forward(us))... {} 414 | 415 | template ::value and 418 | sizeof...(Us) == sizeof...(Tags)>:: 419 | template enable_implicit()>::type* = nullptr> 420 | constexpr tagged_tuple(Us&&... us) noexcept( 421 | tuples_detail::all, Us&&>::value...>::value) 423 | : tuples_detail::tagged_tuple_leaf( 424 | tuples_detail::forward(us))... {} 425 | 426 | template < 427 | class... UTags, 428 | typename std::enable_if< 429 | tuple_like_constructor< 430 | sizeof...(Tags) == sizeof...(UTags) and 431 | tuples_detail::all, tag_type const&>::value...>::value>:: 433 | template enable_explicit const&, 434 | tag_type...>()>::type* = nullptr> 435 | constexpr explicit tagged_tuple(tagged_tuple const& t) noexcept( 436 | tuples_detail::all, tag_type const&>::value...>::value) 438 | : tuples_detail::tagged_tuple_leaf(get(t))... {} 439 | 440 | template < 441 | class... UTags, 442 | typename std::enable_if< 443 | tuple_like_constructor< 444 | sizeof...(Tags) == sizeof...(UTags) and 445 | tuples_detail::all, tag_type const&>::value...>::value>:: 447 | template enable_implicit const&, 448 | tag_type...>()>::type* = nullptr> 449 | constexpr tagged_tuple(tagged_tuple const& t) noexcept( 450 | tuples_detail::all, tag_type const&>::value...>::value) 452 | : tuples_detail::tagged_tuple_leaf(get(t))... {} 453 | 454 | template < 455 | class... UTags, 456 | typename std::enable_if< 457 | tuple_like_constructor< 458 | sizeof...(Tags) == sizeof...(UTags) and 459 | tuples_detail::all, tag_type&&>::value...>::value>:: 461 | template enable_explicit&&, 462 | tag_type...>()>::type* = nullptr> 463 | constexpr explicit tagged_tuple(tagged_tuple&& t) noexcept( 464 | tuples_detail::all, tag_type&&>::value...>::value) 466 | : tuples_detail::tagged_tuple_leaf( 467 | tuples_detail::forward>(get(t)))... {} 468 | 469 | template < 470 | class... UTags, 471 | typename std::enable_if< 472 | tuple_like_constructor< 473 | sizeof...(Tags) == sizeof...(UTags) and 474 | tuples_detail::all, tag_type&&>::value...>::value>:: 476 | template enable_implicit&&, 477 | tag_type...>()>::type* = nullptr> 478 | constexpr tagged_tuple(tagged_tuple&& t) noexcept( 479 | tuples_detail::all, tag_type&&>::value...>::value) 481 | : tuples_detail::tagged_tuple_leaf( 482 | tuples_detail::forward>(get(t)))... {} 483 | 484 | // C++17 Draft 23.5.3.2 Assignment 485 | tagged_tuple& operator=( 486 | typename std::conditional::type const& 488 | t) noexcept(is_nothrow_copy_assignable::value) { 489 | static_cast( 490 | tuples_detail::swallow((get(*this) = get(t))...)); 491 | return *this; 492 | } 493 | 494 | tagged_tuple& operator=( 495 | typename std::conditional::type&& 497 | t) noexcept(is_nothrow_move_assignable::value) { 498 | static_cast(tuples_detail::swallow( 499 | (get(*this) = 500 | tuples_detail::forward>(get(t)))...)); 501 | return *this; 502 | } 503 | 504 | template &, 509 | tag_type const&>::value...>::value>::type* = nullptr> 510 | tagged_tuple& operator=(tagged_tuple const& t) noexcept( 511 | tuples_detail::all&, tag_type const&>::value...>::value) { 513 | static_cast( 514 | tuples_detail::swallow((get(*this) = get(t))...)); 515 | return *this; 516 | } 517 | 518 | template < 519 | class... UTags, 520 | typename std::enable_if< 521 | sizeof...(Tags) == sizeof...(UTags) and 522 | tuples_detail::all&, tag_type&&>::value...>::value>::type* = 524 | nullptr> 525 | tagged_tuple& operator=(tagged_tuple&& t) noexcept( 526 | tuples_detail::all&, tag_type&&>::value...>::value) { 528 | static_cast(tuples_detail::swallow( 529 | (get(*this) = 530 | tuples_detail::forward>(get(t)))...)); 531 | return *this; 532 | } 533 | 534 | // C++17 Draft 23.5.3.3 swap 535 | void swap(tagged_tuple& t) noexcept( 536 | tuples_detail::all, 538 | tuples_detail::tagged_tuple_leaf>::value...>::value) { 539 | tuples_detail::swallow(tuples_detail::tagged_tuple_leaf::swap( 540 | static_cast&>(t))...); 541 | } 542 | }; 543 | 544 | /// \cond 545 | template <> 546 | class tagged_tuple<> { 547 | public: 548 | tagged_tuple() noexcept {} 549 | void swap(tagged_tuple& /*unused*/) noexcept {} 550 | }; 551 | /// \endcond 552 | 553 | // C++17 Draft 23.5.3.6 Tuple helper classes 554 | /*! 555 | * \brief Has a member variable `value` equal to the number of elements in a 556 | * tagged_tuple 557 | */ 558 | template 559 | struct tuple_size; 560 | 561 | /// \cond 562 | template 563 | struct tuple_size> 564 | : std::integral_constant {}; 565 | template 566 | struct tuple_size> 567 | : tuple_size> {}; 568 | template 569 | struct tuple_size> 570 | : tuple_size> {}; 571 | template 572 | struct tuple_size> 573 | : tuple_size> {}; 574 | /// \endcond 575 | 576 | // C++17 Draft 23.5.3.7 Element access 577 | // @{ 578 | /*! 579 | * \brief Retrieve the element with tag `Tag` from the tagged_tuple `t` 580 | */ 581 | template 582 | inline constexpr const typename Tag::type& get( 583 | const tagged_tuple& t) noexcept { 584 | return static_cast&>(t).get(); 585 | } 586 | template 587 | inline constexpr typename Tag::type& get(tagged_tuple& t) noexcept { 588 | return static_cast&>(t).get(); 589 | } 590 | template 591 | inline constexpr const typename Tag::type&& get( 592 | const tagged_tuple&& t) noexcept { 593 | return static_cast( 594 | static_cast&&>(t).get()); 595 | } 596 | template 597 | inline constexpr typename Tag::type&& get(tagged_tuple&& t) noexcept { 598 | return static_cast( 599 | static_cast&&>(t).get()); 600 | } 601 | // @} 602 | 603 | // C++17 Draft 23.5.3.8 Relational operators 604 | namespace tuples_detail { 605 | struct equal { 606 | template 607 | static TUPLES_LIB_CONSTEXPR_CXX_14 void apply( 608 | T const& lhs, U const& rhs, bool& result) noexcept(noexcept(lhs == rhs)) { 609 | result = result and lhs == rhs; 610 | } 611 | }; 612 | 613 | template 614 | TUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_equal_impl( 615 | tagged_tuple const& lhs, 616 | tagged_tuple const& 617 | rhs) noexcept(noexcept(std::initializer_list{ 618 | (get(lhs) == get(rhs))...})) { 619 | bool equal = true; 620 | // This short circuits in the sense that the operator== is only evaluated if 621 | // the result thus far is true 622 | static_cast(std::initializer_list{ 623 | (equal::apply(get(lhs), get(rhs), equal), '0')...}); 624 | return equal; 625 | } 626 | } // namespace tuples_detail 627 | 628 | template ::type* = 630 | nullptr> 631 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator==( 632 | tagged_tuple const& lhs, 633 | tagged_tuple const& 634 | rhs) noexcept(noexcept(tuples_detail::tuple_equal_impl(lhs, rhs))) { 635 | return tuples_detail::tuple_equal_impl(lhs, rhs); 636 | } 637 | 638 | template ::type* = 640 | nullptr> 641 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator!=( 642 | tagged_tuple const& lhs, 643 | tagged_tuple const& rhs) noexcept(noexcept(lhs == rhs)) { 644 | return not(lhs == rhs); 645 | } 646 | 647 | namespace tuples_detail { 648 | struct less { 649 | template 650 | static TUPLES_LIB_CONSTEXPR_CXX_14 void apply( 651 | T const& lhs, U const& rhs, bool& last_rhs_less_lhs, 652 | bool& result) noexcept(noexcept(lhs < rhs) and noexcept(rhs < lhs)) { 653 | if (result or last_rhs_less_lhs) { 654 | return; 655 | } 656 | result = lhs < rhs; 657 | if (result) { 658 | return; 659 | } 660 | last_rhs_less_lhs = rhs < lhs; 661 | } 662 | }; 663 | 664 | template 665 | TUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_less_impl( 666 | tagged_tuple const& lhs, 667 | tagged_tuple const& 668 | rhs) noexcept(noexcept(std::initializer_list{ 669 | (get(lhs) < get(rhs))...})) { 670 | bool result = false; 671 | bool last_rhs_less_lhs = false; 672 | static_cast(std::initializer_list{ 673 | (less::apply(get(lhs), get(rhs), last_rhs_less_lhs, result), 674 | '0')...}); 675 | return result; 676 | } 677 | } // namespace tuples_detail 678 | 679 | template ::type* = 681 | nullptr> 682 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator<( 683 | tagged_tuple const& lhs, 684 | tagged_tuple const& 685 | rhs) noexcept(noexcept(tuples_detail::tuple_less_impl(lhs, rhs))) { 686 | return tuples_detail::tuple_less_impl(lhs, rhs); 687 | } 688 | 689 | template ::type* = 691 | nullptr> 692 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator>( 693 | tagged_tuple const& lhs, 694 | tagged_tuple const& rhs) noexcept(noexcept(rhs < lhs)) { 695 | return rhs < lhs; 696 | } 697 | 698 | template ::type* = 700 | nullptr> 701 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator<=( 702 | tagged_tuple const& lhs, 703 | tagged_tuple const& rhs) noexcept(noexcept(rhs < lhs)) { 704 | return not(rhs < lhs); 705 | } 706 | 707 | template ::type* = 709 | nullptr> 710 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator>=( 711 | tagged_tuple const& lhs, 712 | tagged_tuple const& rhs) noexcept(noexcept(lhs < rhs)) { 713 | return not(lhs < rhs); 714 | } 715 | 716 | // C++17 Draft 23.5.3.3 swap 717 | template < 718 | class... Tags, 719 | typename std::enable_if, 721 | tuples_detail::tagged_tuple_leaf>::value...>::value>::type* = 722 | nullptr> 723 | void swap(tagged_tuple& lhs, tagged_tuple& rhs) noexcept( 724 | tuples_detail::all, 726 | tuples_detail::tagged_tuple_leaf>::value...>::value) { 727 | lhs.swap(rhs); 728 | } 729 | 730 | namespace tuples_detail { 731 | template > 732 | struct is_streamable : std::false_type {}; 733 | template 734 | struct is_streamable< 735 | S, T, 736 | void_t::type>() 737 | << std::declval()), 738 | typename std::enable_if::value>::type>> 739 | : std::true_type {}; 740 | 741 | template 742 | using is_streamable_t = typename is_streamable::type; 743 | 744 | struct stream_helper { 745 | template 746 | void operator()(std::ostream& os, const T& element, size_t& current_value, 747 | const size_t num_tags, const std::true_type /*meta*/) { 748 | os << element; 749 | current_value++; 750 | if (current_value < num_tags) { 751 | os << std::string(", "); 752 | } 753 | } 754 | 755 | template 756 | void operator()(std::ostream& os, const T& /*element*/, size_t& current_value, 757 | const size_t num_tags, const std::false_type /*meta*/) { 758 | os << std::string("NOT STREAMABLE"); 759 | current_value++; 760 | if (current_value < num_tags) { 761 | os << std::string(", "); 762 | } 763 | } 764 | }; 765 | } // namespace tuples_detail 766 | 767 | template 768 | std::ostream& operator<<(std::ostream& os, const tagged_tuple& t) { 769 | os << std::string("("); 770 | size_t current_value = 0; 771 | tuples_detail::stream_helper helper; 772 | // With empty tagged_tuple's helper is unused 773 | static_cast(helper); 774 | static_cast(std::initializer_list{ 775 | (helper(os, get(t), current_value, sizeof...(Tags), 776 | tuples_detail::is_streamable_t>{}), 777 | '0')...}); 778 | return os << std::string(")"); 779 | } 780 | 781 | } // namespace tuples 782 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting11/basic_brigand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "brigand.hpp" 4 | 5 | template 6 | struct TypeDisplayer; 7 | 8 | namespace tmpl = brigand; 9 | 10 | int main() { 11 | using test = tmpl::list; 12 | 13 | using three = tmpl::integral_constant; 14 | using four = tmpl::integral_constant; 15 | using five = tmpl::integral_constant; 16 | 17 | using sum = tmpl::plus; 18 | using result = tmpl::integral_constant; 19 | 20 | using which_is_smaller = tmpl::if_, three, four>; 21 | 22 | TypeDisplayer::type::value>> 23 | print_bool; 24 | 25 | TypeDisplayer> 26 | print_if; 27 | 28 | using unsorted_list = tmpl::list; 29 | using sorted_list = tmpl::sort; 30 | 31 | TypeDisplayer print_sorted; 32 | 33 | using first_list = tmpl::list; 34 | using second_list = tmpl::list; 35 | using joined_list = tmpl::append; 36 | 37 | TypeDisplayer print_joined; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting11/basic_tmpl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tmpl { 4 | template 5 | struct list {}; 6 | 7 | template 8 | struct integral_constant { 9 | static const T value = Value; // tmpl::integral_constant::value is 4 10 | using value_type = T; // allows tmpl::integral_constant::type is int 11 | }; 12 | 13 | template 14 | struct bool_ : integral_constant {}; 15 | 16 | template 17 | struct less : bool_<(LHS::value < RHS::value)> {}; 18 | 19 | template 20 | struct plus 21 | : integral_constant {}; 22 | 23 | template 24 | struct if_ : integral_constant {}; 25 | 26 | template 27 | struct if_ : integral_constant { 28 | }; 29 | } // namespace tmpl 30 | 31 | template 32 | struct TypeDisplayer; 33 | 34 | int main() { 35 | using test = tmpl::list; 36 | 37 | using three = tmpl::integral_constant; 38 | using four = tmpl::integral_constant; 39 | using five = tmpl::integral_constant; 40 | 41 | using sum = tmpl::plus; 42 | using result = tmpl::integral_constant; 43 | 44 | using which_is_smaller = 45 | tmpl::if_::value, three, four>; 46 | 47 | TypeDisplayer::value>> print_bool; 48 | 49 | TypeDisplayer< 50 | tmpl::integral_constant> 51 | print_if; 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting11/factorial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | size_t decrement() noexcept { 6 | return N - 1; 7 | } 8 | 9 | template <> 10 | size_t decrement<0>() noexcept { 11 | return 0; 12 | } 13 | 14 | template 15 | size_t factorial() noexcept { 16 | return N * factorial(); 17 | } 18 | 19 | template <> 20 | size_t factorial<0>() noexcept { 21 | return 1; 22 | } 23 | 24 | int main() { 25 | std::cout << decrement<2>() << "\n"; 26 | 27 | std::cout << factorial<4>() << "\n"; 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting11/start/basic_tmpl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tmpl { 4 | template 5 | struct list {}; 6 | 7 | template 8 | struct integral_constant { 9 | static const T value = Value; // tmpl::integral_constant::value is 4 10 | using value_type = T; // allows tmpl::integral_constant::type is int 11 | }; 12 | 13 | template 14 | struct plus 15 | : integral_constant {}; 16 | } // namespace tmpl 17 | 18 | template 19 | struct TypeDisplayer; 20 | 21 | int main() { 22 | using test = tmpl::list; 23 | 24 | using three = tmpl::integral_constant; 25 | using four = tmpl::integral_constant; 26 | using five = tmpl::integral_constant; 27 | 28 | using sum = tmpl::plus; 29 | using result = tmpl::integral_constant; 30 | 31 | // TypeDisplayer print_test; 32 | // TypeDisplayer print_four; 33 | 34 | TypeDisplayer print_sum; 35 | TypeDisplayer print_result; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting12/ScaleBigScalarField.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class ScalarField { 8 | public: 9 | std::vector x{}; 10 | size_t size = 0; 11 | 12 | ScalarField(const size_t initial_size) noexcept { 13 | x.resize(initial_size); 14 | size = initial_size; 15 | } 16 | 17 | void resize(const size_t new_size) noexcept { 18 | x.resize(new_size); 19 | size = new_size; 20 | } 21 | }; 22 | 23 | ScalarField initialize_scalar_field(const size_t size, 24 | const double initial_value) noexcept { 25 | ScalarField field{size}; 26 | std::iota(field.x.begin(), field.x.end(), initial_value); 27 | return field; 28 | } 29 | 30 | ScalarField scale_scalar_field(const ScalarField& field, 31 | const double scale_factor) noexcept { 32 | ScalarField result = field; 33 | for (auto it = result.x.begin(); it != result.x.end(); ++it) { 34 | *it *= scale_factor; 35 | } 36 | return result; 37 | } 38 | 39 | ScalarField scale_scalar_field(ScalarField&& field, 40 | const double scale_factor) noexcept { 41 | for (auto it = field.x.begin(); it != field.x.end(); ++it) { 42 | *it *= scale_factor; 43 | } 44 | return std::move(field); 45 | } 46 | 47 | int main() { 48 | constexpr size_t size = 1000000000; 49 | constexpr double initial_value = 0.0; 50 | constexpr double scale_factor = 10.0; 51 | using clock = std::chrono::high_resolution_clock; 52 | auto start_time = clock::now(); 53 | auto stop_time = clock::now(); 54 | 55 | std::cout << "Initializing scalar field of size " << size << "\n"; 56 | start_time = clock::now(); 57 | 58 | ScalarField field_a = initialize_scalar_field(size, initial_value); 59 | 60 | stop_time = clock::now(); 61 | std::cout << field_a.x[size / 2] << " (" 62 | << std::chrono::duration_cast(stop_time - 63 | start_time) 64 | .count() 65 | << " microseconds)\n"; 66 | 67 | std::cout << "Scaling scalar field by " << scale_factor << "\n"; 68 | start_time = clock::now(); 69 | 70 | ScalarField field_b = scale_scalar_field(field_a, scale_factor); 71 | 72 | stop_time = clock::now(); 73 | std::cout << field_b.x[size / 2] << " (" 74 | << std::chrono::duration_cast(stop_time - 75 | start_time) 76 | .count() 77 | << " microseconds)\n"; 78 | 79 | std::cout << "(std::move) scaling scalar field by " << scale_factor << "\n"; 80 | start_time = clock::now(); 81 | 82 | ScalarField field_c = scale_scalar_field(std::move(field_a), scale_factor); 83 | 84 | stop_time = clock::now(); 85 | std::cout << field_c.x[size / 2] << " (" 86 | << std::chrono::duration_cast(stop_time - 87 | start_time) 88 | .count() 89 | << " microseconds)\n"; 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting12/start/README.md: -------------------------------------------------------------------------------- 1 | For meeting 12, please make sure you have the Guidelines Support Library available. 2 | 3 | Here's one way to get it: 4 | 5 | ~~~ 6 | cd /some/path # replace /some/path with location where you clone GSL 7 | git clone https://github.com/Microsoft/GSL 8 | cd /path/to/meeting/codes # /path/to/meeting/codes -> place where you 9 | # will write today's codes 10 | # When including GSL, add `-I /some/path/GSL/include` to your compile command 11 | ~~~ 12 | 13 | Alternatively, the spectre docker container has it: 14 | 15 | https://spectre-code.org/installation.html 16 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting12/test_gsl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting13/BigScalar.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class BigScalar { 8 | public: 9 | std::vector data{}; 10 | 11 | BigScalar(gsl::not_null*> initial_data, 12 | const double special_value) { 13 | (*initial_data)[4] = special_value; 14 | data = *initial_data; 15 | } 16 | 17 | BigScalar(std::vector&& initial_data, const double special_value) { 18 | initial_data[4] = special_value; 19 | data = std::move(initial_data); 20 | } 21 | 22 | // ... lots of other features like doing math with scalars 23 | }; 24 | 25 | // template 26 | // std::array append_to_array( 27 | // std::array&& input_array, double new_value) { 28 | // std::array new_array{}; 29 | // // for loop to fill in the old values 30 | // for (...) { 31 | // gsl::at(new_array, i) = gsl::at(old_array, i); 32 | // } 33 | // new_array[Size] = new_value; 34 | // return new_array; 35 | // } 36 | 37 | int main() { 38 | constexpr auto size = 100000; 39 | constexpr double special_value = 4.0; 40 | std::vector test_vector{}; 41 | test_vector.resize(size); 42 | for (double& element : test_vector) { 43 | element = 5.0; 44 | } 45 | 46 | // BigScalar big_scalar{gsl::make_not_null(&test_vector), special_value}; 47 | 48 | BigScalar big_scalar{std::move(test_vector), special_value}; 49 | // std::cout << test_vector[4] << "\n"; 50 | 51 | std::cout << gsl::at(big_scalar.data, size / 2) << "\n"; 52 | std::cout << big_scalar.data[4] << "\n"; 53 | 54 | double x = std::numeric_limits::signaling_NaN(); 55 | // x = 7; 56 | std::cout << x + big_scalar.data[4] << "\n"; 57 | 58 | /* 59 | tnsr::I x; 60 | tnsr::I y; 61 | double dot_product = get<0>(x) * get<0>(y); 62 | */ 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting2/constexpr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | constexpr size_t sum_numbers_constexpr() noexcept { 5 | size_t result = 0; 6 | for (size_t i = 0; i < 100000; ++i) { 7 | // Note: unsafe to underflow size_t, e.g. subtracting 0 - 1 gives 8 | // max size of size_t 9 | result += i; 10 | } 11 | return result; 12 | } 13 | 14 | size_t sum_numbers() noexcept { 15 | size_t result = 0; 16 | for (size_t i = 0; i < 100000; ++i) { 17 | // Note: unsafe to underflow size_t, e.g. subtracting 0 - 1 gives 18 | // max size of size_t 19 | result += i; 20 | } 21 | return result; 22 | } 23 | 24 | int main() { 25 | size_t result = 0; 26 | constexpr size_t iterations = 20000; 27 | for (size_t i = 0; i < iterations; ++i) { 28 | // result = sum_numbers(); 29 | constexpr size_t result_constexpr = sum_numbers_constexpr(); 30 | if (i == iterations - 1) { 31 | std::cout << result_constexpr << "\n"; 32 | } 33 | } 34 | // std::cout << sum_numbers() << "\n"; 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting2/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "trap.hpp" 6 | 7 | double f(const double x) noexcept { return sqrt(fabs(1.0 - x * x)); } 8 | 9 | int main() { 10 | constexpr double step = 1.0e-6; 11 | constexpr double lower = 0.0; 12 | constexpr double upper = 1.0; 13 | 14 | const double result = 4.0 * trap(f, step, lower, upper); 15 | const double error = result - M_PI; 16 | std::cout << std::setprecision(15) << result << "\n"; 17 | std::cout << error << "\n"; 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting2/trap.cpp: -------------------------------------------------------------------------------- 1 | #include "trap.hpp" 2 | 3 | #include 4 | 5 | double trap(double (*const func)(const double), const double step_size, 6 | const double lower_bound, const double upper_bound) noexcept { 7 | double result = 0.0; 8 | for (double x = lower_bound; x < upper_bound; x += step_size) { 9 | result += func(x) * step_size; 10 | result += 0.5 * step_size * (func(x + step_size) - func(x)); 11 | } 12 | 13 | return result; 14 | } 15 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting2/trap.hpp: -------------------------------------------------------------------------------- 1 | // Use the trapezoid rule (e.g. wikipedia) to integrate a function of 2 | // one variable func, using fixed step size `step_size` between limits 3 | // `lower_bound` and `upper_bound`. 4 | double trap(double (*const func)(const double), double step_size, 5 | double lower_bound, double upper_bound) noexcept; 6 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting3/Cast.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | double x = 4.4; 6 | // int y{4.4}; // nope... {} initialization errors on narrowing conversions 7 | int y{4}; 8 | 9 | int z = static_cast(x) + y; 10 | 11 | std::string message = "Hello"; 12 | 13 | std::string message_with_number = message + std::to_string(x); 14 | 15 | std::cout << z << "\n"; 16 | 17 | std::cout << "Hello world\n"; 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting3/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Rectangle.hpp" 5 | 6 | int main() { 7 | const Rectangle rect{4.0, 3.0}; 8 | 9 | Rectangle rect2{5.0, 6.0}; 10 | 11 | double x{4.0}; 12 | 13 | std::cout << std::setprecision(15) << std::fixed; 14 | std::cout << "Width of rect: " << rect.width() << "\n"; 15 | std::cout << "Width of height: " << rect.height() << "\n"; 16 | std::cout << "Perimeter of rect: " << rect.perimeter() << "\n"; 17 | 18 | std::cout << "Perimeter of rect2: " << rect2.evil_perimeter() << "\n"; 19 | std::cout << "Perimeter of rect2: " << rect2.evil_perimeter() << "\n"; 20 | 21 | std::cout << "Number of sides: " << rect.number_of_sides() << "\n"; 22 | } 23 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting3/Rectangle.cpp: -------------------------------------------------------------------------------- 1 | #include "Rectangle.hpp" 2 | 3 | double Rectangle::perimeter() const { return 2.0 * (width_ + height_); } 4 | 5 | double Rectangle::evil_perimeter() const { 6 | const double result = 2.0 * (width_ + height_); 7 | // width += 4.0; // nope: can't modiy object with const function 8 | // height *= -2.0; // nope: can't modiy object with const function 9 | return result; 10 | } 11 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting3/Rectangle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Rectangle { 4 | public: 5 | Rectangle(const double width, const double height) 6 | : width_{width}, height_(height) {} 7 | 8 | static int number_of_sides() { return 4; } 9 | 10 | double perimeter() const; 11 | 12 | double width() const { return width_; } 13 | double height() const { return height_; } 14 | 15 | double evil_perimeter() const; 16 | 17 | private: 18 | double width_; 19 | double height_; 20 | }; 21 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Rectangle.hpp" 4 | #include "Square.hpp" 5 | 6 | int main() { 7 | Rectangle rect{4.0, 5.0}; 8 | 9 | Square sq{4.0}; 10 | 11 | std::cout << rect.Perimeter() << "\n"; 12 | 13 | std::cout << sq.Perimeter() << "\n"; 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Polygon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Polygon { 4 | public: 5 | virtual double Perimeter() const noexcept = 0; 6 | }; 7 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Rectangle.cpp: -------------------------------------------------------------------------------- 1 | #include "Rectangle.hpp" 2 | 3 | Rectangle::Rectangle(const double length, const double width) noexcept 4 | : length_(length), width_(width) {} 5 | 6 | double Rectangle::Perimeter() const noexcept { 7 | return 2.0 * (length_ + width_); 8 | } 9 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Rectangle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Polygon.hpp" 4 | 5 | class Rectangle : public Polygon { 6 | public: 7 | Rectangle(double length, double width) noexcept; 8 | double Perimeter() const noexcept override; 9 | 10 | protected: 11 | double length_; 12 | double width_; 13 | }; 14 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Square.cpp: -------------------------------------------------------------------------------- 1 | #include "Square.hpp" 2 | 3 | Square::Square(const double length) noexcept : Rectangle(length, length) {} 4 | 5 | double Square::Perimeter() noexcept { return 4.0 * length_; } 6 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Square.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Rectangle.hpp" 4 | 5 | class Square : public Rectangle { 6 | public: 7 | Square(double length) noexcept; 8 | double Perimeter() noexcept; 9 | }; 10 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting4/Template.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | T square(const T x) { return x * x; } 5 | 6 | int main() { 7 | int test = 4; 8 | std::cout << square(test) << "\n"; 9 | 10 | double test_2 = 4.5; 11 | std::cout << square(test_2) << "\n"; 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting5/Containers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | std::string to_string(const T& container) noexcept { 9 | std::stringstream result; 10 | result << "["; 11 | for (auto element : container) { 12 | result << element << " "; 13 | } 14 | // for (auto it = container.begin(); it != container.end(); ++it) { 15 | // result << *it << " "; 16 | // } 17 | 18 | std::string output = result.str(); 19 | output.pop_back(); 20 | 21 | return output + "]"; 22 | } 23 | 24 | template 25 | T operator+(const T& lhs, const T& rhs) noexcept { 26 | T result{lhs}; 27 | auto it_result = result.begin(); 28 | auto it_rhs = rhs.begin(); 29 | for (; it_result != result.end(); ++it_result, ++it_rhs) { 30 | *it_result += *it_rhs; 31 | } 32 | return result; 33 | } 34 | 35 | int main() { 36 | std::vector test_vector{1.0, 4.0, 9.0}; 37 | std::cout << to_string(test_vector) << "\n"; 38 | 39 | test_vector.push_back(16.0); 40 | std::cout << to_string(test_vector) << "\n"; 41 | 42 | test_vector.pop_back(); 43 | test_vector.pop_back(); 44 | 45 | std::cout << to_string(test_vector) << "\n"; 46 | 47 | std::array test_array{{1.0, 2.0, 3.0}}; 48 | std::cout << to_string(test_array) << "\n"; 49 | 50 | std::array test2_array{{2.0, 4.0, 6.0}}; 51 | std::array sum = test_array + test2_array; 52 | std::cout << to_string(sum) << "\n"; 53 | 54 | std::array short_array{{1.0, 4.0}}; 55 | 56 | // std::cout << to_string(sum + short_array) << "\n"; // nope: type T conflict 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting5/Square.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::literals::string_literals; 5 | 6 | template 7 | T square(const T& x) { 8 | return x * x; 9 | } 10 | 11 | template 12 | T add_to_self(const T& x) { 13 | return x + x; 14 | } 15 | 16 | template 17 | T halve(const T& x) { 18 | return x / 2; 19 | } 20 | 21 | template <> 22 | double halve(const double& x) { 23 | return 0.5 * x; 24 | } 25 | 26 | int main() { 27 | std::cout << square(4.0) << "\n"; 28 | std::cout << add_to_self(5.0) << "\n"; 29 | std::cout << halve(3.0) << "\n"; 30 | 31 | std::cout << halve(3) << "\n"; 32 | 33 | std::cout << halve(3) << "\n"; 34 | std::cout << halve(3.0) << "\n"; 35 | 36 | std::cout << add_to_self("Hello"s) << "\n"; 37 | // without string_literals, this would work: 38 | // std::cout << add_to_self(std::string{"Hello"}) << "\n"; 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting6/ArraySum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | std::string to_string(const T& container) noexcept { 8 | std::stringstream stream{}; 9 | 10 | auto it = container.begin(); 11 | stream << "["; 12 | for (; it != container.end(); ++it) { 13 | stream << *it << " "; 14 | } 15 | 16 | // for (element : container){ 17 | // stream << element << " "; 18 | // } 19 | std::string result = stream.str(); 20 | result.pop_back(); 21 | 22 | return result + "]"; 23 | } 24 | 25 | template 26 | T operator+(const T& lhs, const T& rhs) noexcept { 27 | T result{lhs}; 28 | 29 | auto it_result = result.begin(); 30 | auto it_rhs = rhs.begin(); 31 | for (; it_result != result.end(); ++it_result, ++it_rhs) { 32 | *it_result += *it_rhs; 33 | } 34 | 35 | return result; 36 | } 37 | 38 | int main() { 39 | std::array test_array{{1.0, 4.0, 9.0}}; 40 | std::array test_array_2{{4.4, 5.4, 6.4}}; 41 | 42 | std::array test_array_short{{1.0, 2.0}}; 43 | std::array test_array_short_int{{1, 2}}; 44 | 45 | std::cout << to_string(test_array + test_array_2) << "\n"; 46 | 47 | //std::cout << to_string(test_array_short_int + test_array_short) << "\n"; // nope 48 | 49 | std::cout << to_string(operator+(test_array, test_array_2)) << "\n"; 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting6/SpatialVector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace Frame { 6 | struct Grid {}; 7 | struct Inertial {}; 8 | } // namespace Frame 9 | 10 | template 11 | class SpatialVector { 12 | public: 13 | static constexpr size_t volume_dim = VolumeDim; 14 | using data_type = DataType; 15 | 16 | SpatialVector(const std::array& data) noexcept 17 | : data_(data) {} 18 | 19 | const auto& get(const size_t i) const noexcept { 20 | if (i < VolumeDim) { 21 | return data_[i]; 22 | } else { 23 | std::cout << "ERROR: index " << i << " out of bounds for VolumeDim " 24 | << volume_dim << "\n"; 25 | exit(-1); 26 | } 27 | } 28 | 29 | private: 30 | std::array data_; 31 | }; 32 | 33 | template 34 | const auto& get(const T& vector) noexcept { 35 | static_assert(Index < T::volume_dim, 36 | "Index should be nonnegative and less than VolumeDim"); 37 | return vector.get(Index); 38 | } 39 | 40 | template 41 | DataType dot_product( 42 | const SpatialVector& vector_a, 43 | const SpatialVector& vector_b) noexcept { 44 | DataType result{get<0>(vector_a) * get<0>(vector_b)}; 45 | for (size_t i = 1; i < VolumeDim; ++i) { 46 | result += vector_a.get(i) * vector_b.get(i); 47 | } 48 | 49 | return result; 50 | } 51 | 52 | int main() { 53 | SpatialVector test_spatial_vector{ 54 | {{1.0, 4.0, 9.0}}}; 55 | SpatialVector test_vector_2{{{0.0, 4.0, 0.0}}}; 56 | 57 | std::cout << get<1>(test_spatial_vector) << "\n"; 58 | 59 | std::cout << dot_product(test_spatial_vector, test_vector_2) << "\n"; 60 | 61 | // std::cout << get<444>(test_spatial_vector) << "\n"; // fail at compile_time 62 | // std::cout << test_spatial_vector.get(444) << "\n"; // fail at runtime 63 | 64 | return 0; 65 | }; 66 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting7/SpatialVector.cpp: -------------------------------------------------------------------------------- 1 | #include "SpatialVector.hpp" 2 | 3 | #include 4 | 5 | // Stand-in for DataVector 6 | // NOTE: don't do this when programming spectre! 7 | // just use spectre's DataVector 8 | #include 9 | using DataVector = std::valarray; 10 | 11 | int main() { 12 | DataVector axis_coords{{1.0, -1.0, -3.0, -5.0}}; 13 | DataVector axis_coords_2{{4.0, -3.0, -5.0, 5.0}}; 14 | std::array axis_array{{axis_coords, axis_coords_2}}; 15 | 16 | SpatialVector vector_1d{axis_array}; 17 | std::cout << to_string(vector_1d) << "\n"; 18 | 19 | SpatialVector test_vector{{1.0, 4.0, 9.0}}; 20 | std::cout << get<0>(test_vector) << "\n"; 21 | 22 | // A grid calculation 23 | constexpr size_t volume_dim = 2; 24 | constexpr size_t points_per_dimension = 5; 25 | const size_t number_of_points = pow(points_per_dimension, volume_dim); 26 | constexpr double lower_bound = -4.0; 27 | constexpr double upper_bound = 4.0; 28 | constexpr double delta = (upper_bound - lower_bound) / 29 | static_cast(points_per_dimension - 1); 30 | 31 | DataVector x_coords(0.0, number_of_points); 32 | DataVector y_coords(0.0, number_of_points); 33 | 34 | size_t i = 0; 35 | for (double y = lower_bound; y <= upper_bound; y += delta) { 36 | for (double x = lower_bound; x <= upper_bound; x += delta) { 37 | x_coords[i] = x; 38 | y_coords[i] = y; 39 | ++i; 40 | } 41 | } 42 | 43 | SpatialVector coords{{x_coords, y_coords}}; 44 | std::cout << to_string(coords) << "\n"; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting7/SpatialVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Frame { 10 | struct Grid {}; 11 | struct Inertial {}; 12 | } // namespace Frame 13 | 14 | template 15 | class SpatialVector { 16 | public: 17 | static constexpr size_t volume_dim = VolumeDim; 18 | using data_type = DataType; 19 | 20 | SpatialVector(const std::array& data) noexcept 21 | : data_(data) {} 22 | 23 | const auto& get(const size_t i) const noexcept { 24 | if (i < VolumeDim) { 25 | return data_[i]; 26 | } else { 27 | std::cout << "ERROR: index " << i << " out of bounds for VolumeDim " 28 | << volume_dim << "\n"; 29 | exit(-1); 30 | } 31 | } 32 | 33 | private: 34 | std::array data_; 35 | }; 36 | 37 | template 38 | const auto& get(const T& vector) noexcept { 39 | static_assert(Index < T::volume_dim, 40 | "Index should be nonnegative and less than VolumeDim"); 41 | return vector.get(Index); 42 | } 43 | 44 | template 45 | DataType dot_product( 46 | const SpatialVector& vector_a, 47 | const SpatialVector& vector_b) noexcept { 48 | DataType result{get<0>(vector_a) * get<0>(vector_b)}; 49 | for (size_t i = 1; i < VolumeDim; ++i) { 50 | result += vector_a.get(i) * vector_b.get(i); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | // to_string() that returns a std::string from a container (e.g. std::array) 57 | template 58 | std::string to_string(const T& container) noexcept { 59 | std::stringstream output_stream{}; 60 | output_stream << "["; 61 | for (auto element : container) { 62 | output_stream << element << " "; 63 | } 64 | std::string output_string = output_stream.str(); 65 | output_string.pop_back(); 66 | return output_string + "]"; 67 | } 68 | 69 | // to_string() for SpatialVectors 70 | template 71 | std::string to_string( 72 | const SpatialVector& spatial_vector) noexcept { 73 | std::stringstream output_stream{}; 74 | output_stream << "["; 75 | for (size_t i = 0; i < VolumeDim; ++i) { 76 | output_stream << to_string(spatial_vector.get(i)) << " "; 77 | } 78 | std::string output_string = output_stream.str(); 79 | output_string.pop_back(); 80 | return output_string + "]"; 81 | } 82 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting8/SpatialVector.cpp: -------------------------------------------------------------------------------- 1 | #include "SpatialVector.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // Stand-in for DataVector 9 | // NOTE: don't do this when programming spectre! 10 | // just use spectre's DataVector 11 | #include 12 | using DataVector = std::valarray; 13 | 14 | void print_2d_data_vector(const DataVector& vector) { 15 | size_t points_per_row = 16 | static_cast(sqrt(static_cast(vector.size()))); 17 | std::cout << std::setprecision(4) << std::fixed; 18 | for (size_t i = 0; i < vector.size(); ++i) { 19 | if (i % points_per_row == 0) { 20 | std::cout << "\n"; 21 | } 22 | if (vector[i] < 0.0) { 23 | std::cout << vector[i] << " "; 24 | } else { 25 | std::cout << " " << vector[i] << " "; 26 | } 27 | } 28 | std::cout << "\n"; 29 | } 30 | 31 | int main() { 32 | DataVector axis_coords{{1.0, -1.0, -3.0, -5.0}}; 33 | DataVector axis_coords_2{{4.0, -3.0, -5.0, 5.0}}; 34 | std::array axis_array{{axis_coords, axis_coords_2}}; 35 | 36 | SpatialVector vector_1d{axis_array}; 37 | std::cout << to_string(vector_1d) << "\n"; 38 | 39 | SpatialVector test_vector{{1.0, 4.0, 9.0}}; 40 | std::cout << get<0>(test_vector) << "\n"; 41 | 42 | // A grid calculation 43 | constexpr size_t volume_dim = 2; 44 | constexpr size_t points_per_dimension = 9; 45 | const size_t number_of_points = pow(points_per_dimension, volume_dim); 46 | constexpr double lower_bound = -4.0; 47 | constexpr double upper_bound = 4.0; 48 | constexpr double delta = (upper_bound - lower_bound) / 49 | static_cast(points_per_dimension - 1); 50 | 51 | DataVector x_coords(0.0, number_of_points); 52 | DataVector y_coords(0.0, number_of_points); 53 | 54 | size_t i = 0; 55 | for (double y = upper_bound; y >= lower_bound; y -= delta) { 56 | for (double x = lower_bound; x <= upper_bound; x += delta) { 57 | x_coords[i] = x; 58 | y_coords[i] = y; 59 | ++i; 60 | } 61 | } 62 | 63 | SpatialVector coords{{x_coords, y_coords}}; 64 | std::cout << to_string(coords) << "\n"; 65 | 66 | print_2d_data_vector(y_coords); 67 | 68 | print_2d_data_vector(exp(-0.25 * dot_product(coords, coords))); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting8/SpatialVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Frame { 10 | struct Grid {}; 11 | struct Inertial {}; 12 | } // namespace Frame 13 | 14 | template 15 | class SpatialVector { 16 | public: 17 | static constexpr size_t volume_dim = VolumeDim; 18 | using data_type = DataType; 19 | 20 | SpatialVector(const std::array& data) noexcept 21 | : data_(data) {} 22 | 23 | const auto& get(const size_t i) const noexcept { 24 | if (i < VolumeDim) { 25 | return data_[i]; 26 | } else { 27 | std::cout << "ERROR: index " << i << " out of bounds for VolumeDim " 28 | << volume_dim << "\n"; 29 | exit(-1); 30 | } 31 | } 32 | 33 | private: 34 | std::array data_; 35 | }; 36 | 37 | template 38 | const auto& get(const T& vector) noexcept { 39 | static_assert(Index < T::volume_dim, 40 | "Index should be nonnegative and less than VolumeDim"); 41 | return vector.get(Index); 42 | } 43 | 44 | template 45 | DataType dot_product( 46 | const SpatialVector& vector_a, 47 | const SpatialVector& vector_b) noexcept { 48 | DataType result{get<0>(vector_a) * get<0>(vector_b)}; 49 | for (size_t i = 1; i < VolumeDim; ++i) { 50 | result += vector_a.get(i) * vector_b.get(i); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | // to_string() that returns a std::string from a container (e.g. std::array) 57 | template 58 | std::string to_string(const T& container) noexcept { 59 | std::stringstream output_stream{}; 60 | output_stream << "["; 61 | for (auto element : container) { 62 | output_stream << element << " "; 63 | } 64 | std::string output_string = output_stream.str(); 65 | output_string.pop_back(); 66 | return output_string + "]"; 67 | } 68 | 69 | // to_string() for SpatialVectors 70 | template 71 | std::string to_string( 72 | const SpatialVector& spatial_vector) noexcept { 73 | std::stringstream output_stream{}; 74 | output_stream << "["; 75 | for (size_t i = 0; i < VolumeDim; ++i) { 76 | output_stream << to_string(spatial_vector.get(i)) << " "; 77 | } 78 | std::string output_string = output_stream.str(); 79 | output_string.pop_back(); 80 | return output_string + "]"; 81 | } 82 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting8/Trap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | double trap(const T& f, const double lower_bound, const double upper_bound, 6 | const double step_size) noexcept { 7 | double result = 0.0; 8 | for (double x = lower_bound; x <= upper_bound; x += step_size) { 9 | result += 0.5 * step_size * (f(x) + f(x + step_size)); 10 | } 11 | return result; 12 | } 13 | 14 | double test_func(const double x) noexcept { return sqrt(fabs(1.0 - x * x)); } 15 | 16 | template 17 | struct test_struct { 18 | static constexpr double coef = 5.0; 19 | double operator()(const double x) const noexcept { 20 | return coef * sqrt(fabs(1.0 - x * x)); 21 | } 22 | }; 23 | 24 | // By the way, compiler, please make this available at runtime so I don't get 25 | // a linker error...omit this line if not using the value at runtime 26 | template 27 | double test_struct::coef; 28 | 29 | int main() { 30 | std::cout << 4.0 * trap(test_func, 0.0, 1.0, 1.0e-5) << "\n"; 31 | std::cout << trap(test_struct<4>{}, 0.0, 1.0, 1.0e-5) << "\n"; 32 | std::cout << 4.0 * trap(sqrt, 0.0, 1.0, 1.0e-5) << "\n"; 33 | 34 | std::cout << 4.0 * trap([](const auto x) { return sqrt(fabs(1.0 - x * x)); }, 35 | 0.0, 1.0, 1.0e-5) 36 | << "\n"; 37 | return 0; 38 | } -------------------------------------------------------------------------------- /MeetingCodes/Meeting8/start/SpatialVector.cpp: -------------------------------------------------------------------------------- 1 | #include "SpatialVector.hpp" 2 | 3 | #include 4 | 5 | // Stand-in for DataVector 6 | // NOTE: don't do this when programming spectre! 7 | // just use spectre's DataVector 8 | #include 9 | using DataVector = std::valarray; 10 | 11 | int main() { 12 | DataVector axis_coords{{1.0, -1.0, -3.0, -5.0}}; 13 | DataVector axis_coords_2{{4.0, -3.0, -5.0, 5.0}}; 14 | std::array axis_array{{axis_coords, axis_coords_2}}; 15 | 16 | SpatialVector vector_1d{axis_array}; 17 | std::cout << to_string(vector_1d) << "\n"; 18 | 19 | SpatialVector test_vector{{1.0, 4.0, 9.0}}; 20 | std::cout << get<0>(test_vector) << "\n"; 21 | 22 | // A grid calculation 23 | constexpr size_t volume_dim = 2; 24 | constexpr size_t points_per_dimension = 5; 25 | const size_t number_of_points = pow(points_per_dimension, volume_dim); 26 | constexpr double lower_bound = -4.0; 27 | constexpr double upper_bound = 4.0; 28 | constexpr double delta = (upper_bound - lower_bound) / 29 | static_cast(points_per_dimension - 1); 30 | 31 | DataVector x_coords(0.0, number_of_points); 32 | DataVector y_coords(0.0, number_of_points); 33 | 34 | size_t i = 0; 35 | for (double y = lower_bound; y <= upper_bound; y += delta) { 36 | for (double x = lower_bound; x <= upper_bound; x += delta) { 37 | x_coords[i] = x; 38 | y_coords[i] = y; 39 | ++i; 40 | } 41 | } 42 | 43 | SpatialVector coords{{x_coords, y_coords}}; 44 | std::cout << to_string(coords) << "\n"; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting8/start/SpatialVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Frame { 10 | struct Grid {}; 11 | struct Inertial {}; 12 | } // namespace Frame 13 | 14 | template 15 | class SpatialVector { 16 | public: 17 | static constexpr size_t volume_dim = VolumeDim; 18 | using data_type = DataType; 19 | 20 | SpatialVector(const std::array& data) noexcept 21 | : data_(data) {} 22 | 23 | const auto& get(const size_t i) const noexcept { 24 | if (i < VolumeDim) { 25 | return data_[i]; 26 | } else { 27 | std::cout << "ERROR: index " << i << " out of bounds for VolumeDim " 28 | << volume_dim << "\n"; 29 | exit(-1); 30 | } 31 | } 32 | 33 | private: 34 | std::array data_; 35 | }; 36 | 37 | template 38 | const auto& get(const T& vector) noexcept { 39 | static_assert(Index < T::volume_dim, 40 | "Index should be nonnegative and less than VolumeDim"); 41 | return vector.get(Index); 42 | } 43 | 44 | template 45 | DataType dot_product( 46 | const SpatialVector& vector_a, 47 | const SpatialVector& vector_b) noexcept { 48 | DataType result{get<0>(vector_a) * get<0>(vector_b)}; 49 | for (size_t i = 1; i < VolumeDim; ++i) { 50 | result += vector_a.get(i) * vector_b.get(i); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | // to_string() that returns a std::string from a container (e.g. std::array) 57 | template 58 | std::string to_string(const T& container) noexcept { 59 | std::stringstream output_stream{}; 60 | output_stream << "["; 61 | for (auto element : container) { 62 | output_stream << element << " "; 63 | } 64 | std::string output_string = output_stream.str(); 65 | output_string.pop_back(); 66 | return output_string + "]"; 67 | } 68 | 69 | // to_string() for SpatialVectors 70 | template 71 | std::string to_string( 72 | const SpatialVector& spatial_vector) noexcept { 73 | std::stringstream output_stream{}; 74 | output_stream << "["; 75 | for (size_t i = 0; i < VolumeDim; ++i) { 76 | output_stream << to_string(spatial_vector.get(i)) << " "; 77 | } 78 | std::string output_string = output_stream.str(); 79 | output_string.pop_back(); 80 | return output_string + "]"; 81 | } 82 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting9/MathOnArray.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | void print_container(const T& container) noexcept { 9 | for (auto element : container) { 10 | std::cout << element << " "; 11 | } 12 | std::cout << "\n"; 13 | } 14 | 15 | double square(const double x) noexcept { return x * x; } 16 | 17 | // [&power](const double x) { return pow(x, power); } -> 18 | // struct apply_math_op { 19 | // double& power; 20 | // double operator()(const double x) { return pow(x, power); } 21 | // }; 22 | 23 | int main() { 24 | std::array data; 25 | std::iota(data.begin(), data.end(), 0.0); 26 | print_container(data); 27 | 28 | double power = 2.0; 29 | auto apply_math_op = [&power](const double x) { return pow(x, power); }; 30 | std::transform(data.begin(), data.end(), data.begin(), apply_math_op); 31 | print_container(data); 32 | 33 | power = 0.5; 34 | std::transform(data.begin(), data.end(), data.begin(), apply_math_op); 35 | print_container(data); 36 | 37 | // If this isn't const, other_math_op can be evil and change it 38 | const double other_power = 0.0; 39 | auto apply_other_math_op = [&other_power](const double x) noexcept { 40 | // other_power += 1; // other_power is const, so can't be changed 41 | return pow(x, other_power); 42 | }; 43 | std::transform(data.begin(), data.end(), data.begin(), apply_other_math_op); 44 | print_container(data); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting9/Start/MathOnArray.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | void print_container(const T& container) noexcept { 9 | for (auto element : container) { 10 | std::cout << element << " "; 11 | } 12 | std::cout << "\n"; 13 | } 14 | 15 | int main() { 16 | // .... 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /MeetingCodes/Meeting9/Tags.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | T sum(const T& lhs, const T& rhs) noexcept { 7 | return lhs + rhs + static_cast(Extra); 8 | } 9 | 10 | namespace Rect { 11 | struct Height {}; 12 | struct Width {}; 13 | } // namespace Rect 14 | 15 | template 16 | double area(const T& box) noexcept { 17 | return std::get>(box).second * 18 | std::get>(box).second; 19 | } 20 | 21 | int main() { 22 | // std::tuple example 23 | std::tuple, std::pair> 24 | box{{{}, 4.0}, {{}, 5.0}}; 25 | 26 | std::cout << area(box) << "\n"; 27 | 28 | std::tuple> oops_box{{{}, 4.0}}; 29 | // std::cout << area(oops_box) << "\n"; // does not compile 30 | 31 | return 0; 32 | } 33 | 34 | /* 35 | 36 | SpEC way (runtime keys): 37 | 38 | { key -> value } 39 | 40 | { "Lapse" -> 0.1, "Shift" -> {0.1, 0.2, 0.3}, ... } 41 | 42 | // In SpEC, e.g. 43 | EvaluateScalarFormula(A = Lapse; Output = sqrt(Lapse);) 44 | 45 | SpECTRE way (compile-time keys): 46 | 47 | { compile-time-key-1 -> value-1, compile-time-key-2 -> value-2 } 48 | 49 | */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spectre-cpp-basics 2 | Survival guide / crash course on C++ and spectre (https://github.com/sxs-collaboration/spectre) 3 | 4 | This repo contains materials originally designed for a survival guide / crash course on C++ programming for SpECTRE I am teaching my undergraduate researchers and others in SXS. 5 | 6 | The idea is to introduce some of the C++ that comes up a lot in spectre. For each topic, I thought I’d introduce it in a small, self-contained toy program that we build from the ground up together. 7 | 8 | This is not meant to be an authoritative or complete introduction to these topics. I'm still learning myself, and I'm a physicist, not a C++ professional or a computer scientist. But I hope that these introductions will help my students and other new spectre developers tackle the learning curve a bit more easily. 9 | 10 | Prerequisites: 11 | * I'm assuming that students have basic programming knowledge in any language (e.g. python). Defining variables, if/else, for/while loops, defining your own functions, etc. That said, no topic is too basic to ask about in this course! 12 | * I'm also assuming that students have some environment where they can compile and run C++-17 programs. 13 | * The above prerequisites are sufficent for running the toy examples we'll develop. But to apply them to spectre, I also assume students are able to checkout, compile, and run the tests on spectre. Instructions here: (https://spectre-code.org/installation.html) 14 | 15 | Here are some topics that I might cover (I will adjust content and pacing depending on how things go): 16 | 17 | * Free functions, declaration vs definition, const, constexpr, noexcept 18 | * Classes, structs, and inheritance, including static functions and virtual functions 19 | * Braces for initialization 20 | * Introduce template programming, square(), cube(), and inline and SPECTRE_ALWAYS_INLINE 21 | * Iterators and some basic STL stuff, like std::array and std::vector 22 | * GSL, gsl::at, gsl::not_null, pass by reference & pass by value, signaling nan 23 | * Basic template metaprogramming/Brigand, decltype 24 | * Lambdas 25 | * Variadic programming & parameter packs 26 | * Grab bag: ternary operator, auto, trailing return type, static_cast<>, ... 27 | * Minimizing allocations, move and copy semantics 28 | 29 | The content is divided into lessons, but each lesson might take more than one meeting. Solutions prepared in advance for each lesson are in `Solutions/`, while the actual code written during each meeting is in `MeetingCodes/`. 30 | -------------------------------------------------------------------------------- /Solutions/Lesson1/constexpr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This function is evaluated at run time 5 | const double slow_init() noexcept { 6 | size_t turtle = 0; 7 | for (size_t i = 0; i < 100000; ++i) { 8 | turtle += 1; 9 | } 10 | const double result = static_cast(turtle); 11 | return result; 12 | } 13 | 14 | // This function is evaluated by the compiler at compile time 15 | constexpr double slow_init_constexpr() noexcept { 16 | // This loop is just to slow down the function 17 | // If you make the upper bound of i too large, the compiler will complain, 18 | // since it has a limit of how many times it will loop while evaluating 19 | // constexpr functions 20 | size_t turtle = 0; 21 | for (size_t i = 0; i < 100000; ++i) { 22 | turtle += 1; 23 | } 24 | const double result = static_cast(turtle); 25 | return result; 26 | } 27 | 28 | int main() { 29 | // This loop simply makes the program take longer, so run time differences 30 | // are easier to notice 31 | constexpr size_t j_max = 100000; 32 | for (size_t j = 0; j < j_max; ++j) { 33 | // The next line will get run at run time 34 | const double test_result = slow_init(); 35 | 36 | // The compiler evaluates the next line at compile time 37 | // constexpr double test_result_expr = slow_init_constexpr(); 38 | 39 | // Only print the result one time, at the last time through the loop 40 | if (j == j_max - 1) { 41 | std::cout << test_result << "\n"; 42 | // std::cout << test_result_expr << "\n"; 43 | } 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /Solutions/Lesson1/hello.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | #include 6 | 7 | int main() { 8 | std::string name = "Geoffrey"; 9 | std::cout << "Hello " << name << "\n"; 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /Solutions/Lesson1/integrate/Left.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | #include "Left.hpp" 4 | 5 | // Integrate function f using the trapezoid rule 6 | double left(double (*const func)(double), const double dx, 7 | const double a, const double b) noexcept { 8 | double result = 0.0; 9 | for (double x = a; x < b; x += dx) { 10 | result += func(x) * dx; 11 | } 12 | return result; 13 | } 14 | -------------------------------------------------------------------------------- /Solutions/Lesson1/integrate/Left.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | /// Integrate function `func` using the left point rule, using constant spacing 5 | /// `dx` and limits `a` and `b` 6 | double left(double (*const func)(double), double dx, 7 | double a, double b) noexcept; 8 | -------------------------------------------------------------------------------- /Solutions/Lesson1/integrate/Main.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Left.hpp" 9 | #include "Trap.hpp" 10 | 11 | // The integrand 12 | double f(const double x) noexcept { 13 | // evil_print(x); // error: argument would lost const qualifier 14 | return sqrt(fabs(1.0 - x * x)); 15 | } 16 | 17 | double g(const double x) noexcept { return exp(x); } 18 | 19 | int main() { 20 | constexpr double dx = 1.0e-7; 21 | constexpr double a = 0.0; 22 | constexpr double b = 1.0; 23 | 24 | std::cout << "Integrating with dx = " << dx << "\n"; 25 | 26 | const double result_trap = 4.0 * trap(f, dx, a, b); 27 | const double result_left = 4.0 * left(f, dx, a, b); 28 | 29 | std::cout << std::setprecision(15); 30 | 31 | std::cout << "result_trap = " << result_trap << "\n"; 32 | std::cout << "result - pi = " << result_trap - M_PI << "\n"; 33 | std::cout << "result_left = " << result_left << "\n"; 34 | std::cout << "result_left - pi = " << result_left - M_PI << "\n"; 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /Solutions/Lesson1/integrate/Trap.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include "Trap.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | double trap(double (*const func)(double), const double dx, 11 | const double a, const double b) noexcept { 12 | double result = 0.0; 13 | for (double x = a; x < b; x += dx) { 14 | result += func(x) * dx; 15 | result += 0.5 * dx * (func(x + dx) - func(x)); 16 | } 17 | return result; 18 | } 19 | -------------------------------------------------------------------------------- /Solutions/Lesson1/integrate/Trap.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | /// Integrate function `func` using the trapezoid rule with fixed step `dx` 5 | /// and limits `a` and `b`. 6 | double trap(double (*const func)(double), double dx, double a, 7 | double b) noexcept; 8 | -------------------------------------------------------------------------------- /Solutions/Lesson1/trap.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Legal but evil print statement that changes x after printing it 9 | void evil_print(double& x) noexcept { 10 | std::cout << x << "\n"; 11 | x += 0.1; 12 | } 13 | 14 | // The integrand 15 | double f(const double x) noexcept { 16 | // evil_print(x); // error: argument would lost const qualifier 17 | return sqrt(fabs(1.0 - x * x)); 18 | } 19 | 20 | double g(const double x) noexcept { return exp(x); } 21 | 22 | // Integrate function f using the trapezoid rule 23 | double trap(double (*const func)(double), const double dx, const double a, 24 | const double b) noexcept { 25 | double result = 0.0; 26 | for (double x = a; x < b; x += dx) { 27 | result += func(x) * dx; 28 | result += 0.5 * dx * (func(x + dx) - func(x)); 29 | } 30 | return result; 31 | } 32 | 33 | double trap(const double dx, const double a, const double b) noexcept { 34 | return trap(f, dx, a, b); 35 | } 36 | 37 | int main() { 38 | constexpr double dx = 1.0e-7; 39 | constexpr double a = 0.0; 40 | constexpr double b = 1.0; 41 | 42 | // evilprint(dx); // dx would lose const qualifier 43 | std::cout << "Integrating with dx = " << dx << "\n"; 44 | 45 | // constexpr double result = 4.0 * left(dx, 0.0, 1.0); // rhs not constexpr 46 | const double result = 4.0 * trap(dx, a, b); 47 | const double result_f = 4.0 * trap(f, dx, a, b); 48 | const double result_g = 1.0 + trap(g, dx, a, b); 49 | 50 | std::cout << std::setprecision(15); 51 | 52 | std::cout << "result = " << result << "\n"; 53 | std::cout << "result - pi = " << result - M_PI << "\n"; 54 | std::cout << "result - result_f = " << result - result_f << "\n"; 55 | std::cout << "result_g = " << result_g << "\n"; 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/Main.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | 6 | #include "Polygon.hpp" 7 | #include "Rectangle.hpp" 8 | #include "RightTriangle.hpp" 9 | #include "Square.hpp" 10 | 11 | int main() { 12 | constexpr double length{4.0}; 13 | constexpr double width{3.0}; 14 | Rectangle rect{length, width}; 15 | 16 | constexpr double length2 = 7.0; 17 | constexpr double width2 = 5.0; 18 | Rectangle rect2{length2, width2}; 19 | 20 | std::cout << "Rectangle perimeter: " << rect.perimeter() << "\n"; 21 | std::cout << "Rectangle area take 1: " << rect.area() << "\n"; 22 | std::cout << "Rectangle area take 2: " << rect.evil_area() << "\n"; 23 | std::cout << "Rectangle area take 3: " << rect.area() << "\n"; 24 | // std::cout << "Rect length: " << rect.length_ << "\n"; // length_ private 25 | std::cout << "Rectangle number of sides: " << rect.number_of_sides() << "\n"; 26 | 27 | Square sq{length}; 28 | std::cout << "Square perimeter: " << sq.perimeter() << "\n"; 29 | 30 | // Note: static functions can't be virtual, and the base class does not 31 | // define number_of_sides(). You must define number_of_sides() for each 32 | // derived class, and you can't call them for objects whose type is just the 33 | // base class type. The following fails to compile for this reason: 34 | // std::cout << "Square number of sides: " 35 | // << static_cast(sq).number_of_sides(); 36 | std::cout << "Square number of sides: " << sq.number_of_sides() << "\n"; 37 | 38 | RightTriangle tri{3.0, 4.0}; 39 | std::cout << "Right triangle perimeter: " << tri.perimeter() << "\n"; 40 | std::cout << "Right triangle area: " << tri.area() << "\n"; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/Polygon.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | #pragma once 4 | 5 | #include 6 | 7 | /// A base class for representing a polygon that knows how to compute its 8 | /// perimeter and area 9 | class Polygon { 10 | public: 11 | virtual double perimeter() const noexcept = 0; 12 | virtual double area() const noexcept = 0; 13 | 14 | static const size_t number_of_sides(); 15 | }; 16 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/Rectangle.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include "Rectangle.hpp" 5 | 6 | Rectangle::Rectangle(const double length, const double width) noexcept 7 | : length_(length), width_(width){} 8 | 9 | double Rectangle::perimeter() const noexcept { 10 | return 2.0 * (length_ + width_); 11 | } 12 | 13 | double Rectangle::area() const noexcept { return length_ * width_; } 14 | 15 | // Note: same as Rectangle::area() except, if you remove the const before 16 | // noexcept, it can silently change the rectangle after computing the area 17 | double Rectangle::evil_area() const noexcept { 18 | const double area = length_ * width_; 19 | // length_ *= 1.1; // const member functions cannot assign to non-static 20 | return area; 21 | } 22 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/Rectangle.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | #pragma once 4 | 5 | #include 6 | 7 | #include "Polygon.hpp" 8 | 9 | class Rectangle : public Polygon { 10 | public: 11 | Rectangle(double length, double width) noexcept; 12 | 13 | double perimeter() const noexcept override; 14 | double area() const noexcept override; 15 | 16 | static const size_t number_of_sides() { return 4; } 17 | 18 | // same as area(), but remove the const before noexcept, and the function 19 | // is allowed to silently change the member variables 20 | double evil_area() const noexcept; 21 | 22 | private: 23 | double length_; 24 | double width_; 25 | }; 26 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/RightTriangle.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | 6 | #include "RightTriangle.hpp" 7 | 8 | RightTriangle::RightTriangle(const double length_a, 9 | const double length_b) noexcept 10 | : length_a_(length_a), length_b_(length_b) {} 11 | 12 | double RightTriangle::perimeter() const noexcept { 13 | return length_a_ + length_b_ + 14 | sqrt(length_a_ * length_a_ + length_b_ * length_b_); 15 | } 16 | 17 | double RightTriangle::area() const noexcept { 18 | return 0.5 * length_a_ * length_b_; 19 | } 20 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/RightTriangle.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | #pragma once 4 | 5 | #include "Polygon.hpp" 6 | 7 | class RightTriangle : public Polygon { 8 | public: 9 | RightTriangle(double length_a, double length_b) noexcept; 10 | 11 | double perimeter() const noexcept override; 12 | double area() const noexcept override; 13 | 14 | static const size_t number_of_sides() { return 3; } 15 | 16 | private: 17 | double length_a_; 18 | double length_b_; 19 | }; 20 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/Square.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include "Square.hpp" 5 | 6 | Square::Square(const double length) noexcept : Rectangle(length, length) {} 7 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Poly/Square.hpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | #pragma once 4 | 5 | #include "Rectangle.hpp" 6 | 7 | class Square : public Rectangle { 8 | public: 9 | Square(double length) noexcept; 10 | }; 11 | -------------------------------------------------------------------------------- /Solutions/Lesson2/Rect/Rect.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | #include 6 | 7 | class Rectangle { 8 | public: 9 | Rectangle(const double length, const double width) noexcept 10 | : length_(length), width_(width) {} 11 | 12 | double perimeter() const noexcept { return 2.0 * (length_ + width_); } 13 | double area() const noexcept { return length_ * width_; } 14 | 15 | static const size_t number_of_sides() { return 4; } 16 | 17 | // same as area(), but remove the const before noexcept, and the function 18 | // is allowed to silently change the member variables 19 | double evil_area() const noexcept { 20 | const double area = length_ * width_; 21 | // length_ *= 1.1; // const member functions cannot assign to non-static 22 | return area; 23 | } 24 | 25 | private: 26 | double length_; 27 | double width_; 28 | }; 29 | 30 | class Square : public Rectangle { 31 | public: 32 | Square(const double length) noexcept : Rectangle(length, length) {} 33 | }; 34 | 35 | int main() { 36 | constexpr double length{4.0}; 37 | constexpr double width{3.0}; 38 | Rectangle rect{length, width}; 39 | 40 | constexpr double length2 = 7.0; 41 | constexpr double width2 = 5.0; 42 | Rectangle rect2{length2, width2}; 43 | 44 | std::cout << "Rectangle perimeter: " << rect.perimeter() << "\n"; 45 | std::cout << "Rectangle area take 1: " << rect.area() << "\n"; 46 | std::cout << "Rectangle area take 2: " << rect.evil_area() << "\n"; 47 | std::cout << "Rectangle area take 3: " << rect.area() << "\n"; 48 | // std::cout << "Rect length: " << rect.length_ << "\n"; // length_ private 49 | std::cout << "Rectangle number of sides: " << rect.number_of_sides() << "\n"; 50 | 51 | Square sq{length}; 52 | std::cout << "Square perimeter: " << sq.perimeter() << "\n"; 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /Solutions/Lesson3/Complex.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | // NOTE: this class is just an example of templates, with complex numbers 5 | // serving as a concrete goal for the example. In real production code, 6 | // just use std::complex (#include ), not a home-grown class like this. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using std::abs; // otherwise, call to abs() on native types is 14 | // implementation-dependent 15 | using std::to_string; 16 | 17 | #include // Note: in spectre, use DataVector; never #include valarray 18 | using DataVector = std::valarray; 19 | std::ostream& operator<<(std::ostream& os, const DataVector& vector) { 20 | std::string output = "["; 21 | for (auto element : vector) { 22 | output += to_string(element) + " "; 23 | } 24 | output.pop_back(); 25 | output += "]"; 26 | 27 | os << output; 28 | return os; 29 | } 30 | 31 | template 32 | struct Complex { 33 | T real; 34 | T imag; 35 | }; 36 | 37 | template 38 | auto real(const Complex& x) noexcept { 39 | return x.real; 40 | } 41 | 42 | template 43 | auto imag(const Complex& x) noexcept { 44 | return x.imag; 45 | } 46 | 47 | template 48 | auto abs(const Complex& x) noexcept { 49 | return sqrt(x.real * x.real + x.imag * x.imag); 50 | } 51 | 52 | // Note: arg() will not convert integral types T to doubles 53 | template 54 | auto arg(const Complex& x) noexcept { 55 | return atan2(x.imag, x.real); 56 | } 57 | 58 | template 59 | auto operator+(const Complex& lhs, const Complex& rhs) noexcept { 60 | Complex result{lhs}; 61 | result.real += rhs.real; 62 | result.imag += rhs.imag; 63 | return result; 64 | } 65 | 66 | template 67 | auto operator-(const Complex& lhs, const Complex& rhs) noexcept { 68 | Complex result{lhs}; 69 | result.real -= rhs.real; 70 | result.imag -= rhs.imag; 71 | return result; 72 | } 73 | 74 | template 75 | auto operator*(const double real_factor, const Complex& x) noexcept { 76 | Complex result{x}; 77 | result.real *= real_factor; 78 | result.imag *= real_factor; 79 | return result; 80 | } 81 | 82 | template 83 | std::string to_string(const Complex& x) noexcept { 84 | std::stringstream result; 85 | result << real(x) << " + " << imag(x) << "i = " << abs(x) << " exp(" << arg(x) 86 | << "i)\n"; 87 | return result.str(); 88 | } 89 | 90 | void print_complex_doubles() noexcept { 91 | constexpr Complex a{4.0, 3.0}; 92 | constexpr Complex b{1.0, 1.0}; 93 | 94 | std::cout << to_string(a) << "\n"; 95 | std::cout << to_string(b) << "\n"; 96 | std::cout << to_string(a + b) << "\n"; 97 | std::cout << to_string(a - b) << "\n"; 98 | std::cout << to_string(4.0 * a) << "\n"; 99 | std::cout << to_string(0.0 * a) << "\n"; 100 | std::cout << to_string(b - b) << "\n"; 101 | std::cout << "\n\n"; 102 | } 103 | 104 | void print_complex_ints() noexcept { 105 | constexpr Complex i{4, 3}; 106 | constexpr Complex j{1, 1}; 107 | 108 | std::cout << to_string(i) << "\n"; 109 | std::cout << to_string(j) << "\n"; 110 | std::cout << to_string(i + j) << "\n"; 111 | std::cout << "\n\n"; 112 | } 113 | 114 | void print_complex_data_vectors() noexcept { 115 | const Complex dv_a{{1.0, 1.0, 3.0}, {0.0, 1.0, 4.0}}; 116 | const Complex dv_b{{4.0, 4.0, 4.0}, {4.0, 4.0, 4.0}}; 117 | std::cout << to_string(dv_a) << "\n"; 118 | std::cout << to_string(dv_b) << "\n"; 119 | std::cout << to_string(dv_a + dv_b) << "\n"; 120 | std::cout << to_string(dv_a - dv_a) << "\n"; 121 | } 122 | 123 | int main() { 124 | print_complex_doubles(); 125 | print_complex_ints(); 126 | print_complex_data_vectors(); 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /Solutions/Lesson3/Containers.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | std::string to_string(const T& container) noexcept { 12 | std::stringstream result; 13 | result << "["; 14 | for (auto element : container) { 15 | result << element << " "; 16 | } 17 | std::string output = result.str(); 18 | output.pop_back(); 19 | return output + "]"; 20 | } 21 | 22 | int main() { 23 | std::vector x{{1.0, 4.0, 9.0}}; 24 | std::cout << to_string(x) << "\n"; 25 | 26 | x.push_back(16.0); 27 | std::cout << to_string(x) << "\n"; 28 | 29 | std::cout << x[1] << "\n"; 30 | 31 | std::array coord{{4.0, 3.0, 2.0}}; 32 | std::cout << to_string(coord) << "\n"; 33 | std::cout << "\n"; 34 | 35 | coord[1] = 4.444; 36 | std::cout << to_string(coord) << "\n"; 37 | std::cout << "\n"; 38 | 39 | std::cout << coord[0] << "\n"; 40 | 41 | std::array words{"Hello", "world"}; 42 | std::cout << to_string(words) << "\n"; 43 | std::cout << "\n"; 44 | 45 | std::array, 3> matrix{ 46 | {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}}}; 47 | std::cout << to_string(matrix[1]) << "\n"; 48 | std::cout << "\n"; 49 | std::cout << matrix[1][2] << "\n"; 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /Solutions/Lesson3/SpatialVector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Stand-in for spectre's DataVector type 6 | #include // Note: in spectre, never #include valarray 7 | using DataVector = std::valarray; 8 | std::ostream& operator<<(std::ostream& os, const DataVector& vector) { 9 | std::string output = "["; 10 | for (auto element : vector) { 11 | output += to_string(element) + " "; 12 | } 13 | output.pop_back(); 14 | output += "]"; 15 | 16 | os << output; 17 | return os; 18 | } 19 | 20 | namespace Frame { 21 | struct Grid {}; 22 | struct Inertial {}; 23 | } // namespace Frame 24 | 25 | template 26 | class SpatialVector { 27 | public: 28 | static constexpr size_t volume_dim = VolumeDim; 29 | using data_type = DataType; 30 | 31 | SpatialVector(const std::array& data) noexcept 32 | : data_{data} {} 33 | 34 | // Get a component, with run-time index checking 35 | const auto& get(const size_t i) const noexcept { 36 | if (i < VolumeDim and i >= 0) { 37 | return data_[i]; 38 | } else { 39 | std::cout << "ERROR: SpatialVector index " << i 40 | << " should be nonnegative and less than " << VolumeDim << "\n"; 41 | exit(-1); 42 | } 43 | } 44 | 45 | private: 46 | std::array data_; 47 | }; 48 | 49 | // Get a SpatialVector component, with compile-time index checking 50 | template 51 | const auto& get(const T& vector) noexcept { 52 | static_assert(Index >= 0 and Index < T::volume_dim, 53 | "Index should be nonnegative and less than VolumeDim"); 54 | return vector.get(Index); 55 | } 56 | 57 | // Euclidean dot product of two spatial vectors 58 | template 59 | DataType dot_product( 60 | const SpatialVector& vector_a, 61 | const SpatialVector& vector_b) noexcept { 62 | // Instead of initializing to zero and resetting, save time by assigning to 63 | // the first term in the sum 64 | DataType result{get<0>(vetor_a) * get<0>(vector_b)}; 65 | for (size_t i = 1; i < VolumeDim; ++i) { 66 | result += vector_a.get(i) * vector_b.get(i); 67 | } 68 | } 69 | 70 | int main() { 71 | SpatialVector<3, double, Frame::Grid> vector{{{1.1, 2.2, 3.3}}}; 72 | std::cout << vector.get(2) << "\n"; // prints 3.3 73 | std::cout << get<1>(vector) << "\n"; // prints 2.2 74 | 75 | // runtime error: index out of bounds 76 | // std::cout << vector.get(3) << "\n"; 77 | 78 | // compiler error: index out of bounds 79 | // std::cout << get<3>(vector) << "\n"; 80 | 81 | SpatialVector<2, double, Frame::Inertial> a{{{1.0, 2.0}}}; 82 | SpatialVector<2, double, Frame::Inertial> b{{{-1.0, 2.0}}}; 83 | std::cout << dot_product(a, b) << "\n"; 84 | 85 | // compiler error: conflicting types for template parameter Fr 86 | // std::cout << dot_product(a, vector) << "\n"; 87 | 88 | SpatialVector<2, DataVector, Frame::Grid> c{ 89 | {{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}}; 90 | std::cout << dot_product(c, c) << "\n"; 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /Solutions/Lesson3/Square.cpp: -------------------------------------------------------------------------------- 1 | // Distributed under the MIT License. 2 | // See LICENSE.txt for details. 3 | 4 | #include 5 | #include 6 | 7 | template 8 | T square(const T& x) noexcept { 9 | return x * x; 10 | } 11 | 12 | template 13 | T add_to_self(const T& x) noexcept { 14 | return x + x; 15 | } 16 | 17 | template 18 | T halve(const T& x) noexcept { 19 | return x / 2; 20 | } 21 | 22 | int main() { 23 | std::cout << square(4.4) << "\n"; 24 | std::cout << square(5) << "\n"; 25 | 26 | std::cout << add_to_self(4.4) << "\n"; 27 | std::cout << add_to_self(5) << "\n"; 28 | std::string hello = "Hello, world!"; 29 | std::cout << add_to_self(hello) << "\n"; 30 | 31 | std::cout << halve(3.0) << "\n"; 32 | std::cout << halve(3) << "\n"; 33 | std::cout << halve(3) << "\n"; 34 | // std::cout << halve(3.3) 35 | // << "\n"; // warning: implicit conversion changes value 36 | std::cout << halve(static_cast(3.3)) << "\n"; 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /Solutions/Lesson4/MathOnArray.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | void print_container(const T& container) noexcept { 9 | for (auto element : container) { 10 | std::cout << element << " "; 11 | } 12 | std::cout << "\n"; 13 | } 14 | 15 | int main() { 16 | std::array data; 17 | std::iota(data.begin(), data.end(), 0.0); 18 | print_container(data); 19 | 20 | // square the data in place 21 | double power = 2.0; 22 | auto apply_math_op = [power](const double x) { return pow(x, power); }; 23 | std::transform(data.begin(), data.end(), data.begin(), apply_math_op); 24 | print_container(data); 25 | 26 | // sqrt the data in place 27 | power = 0.5; 28 | std::transform(data.begin(), data.end(), data.begin(), apply_math_op); 29 | print_container(data); 30 | 31 | // evil math op: changes power, remove first const on next line and this 32 | // compiles 33 | // const auto evil_math_op = [power](const double x) { 34 | // power += 1; 35 | // return pow(x, power); 36 | // }; 37 | // power = 2.0; 38 | // std::transform(data.begin(), data.end(), data.begin(), apply_math_op); 39 | // print_container(data); 40 | 41 | power = 2.0; 42 | std::transform(data.begin(), data.end(), data.begin(), apply_math_op); 43 | print_container(data); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /Solutions/Lesson4/SpatialVector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "SpatialVector.hpp" 8 | 9 | // Stand-in for DataVector 10 | // NOTE: don't ever include valarray in spectre code; use spectre's DataVector 11 | #include 12 | using DataVector = std::valarray; 13 | 14 | int main() { 15 | constexpr size_t volume_dim = 2; 16 | constexpr size_t points_per_dimension = 9; 17 | const size_t number_of_points = pow(points_per_dimension, volume_dim); 18 | constexpr double lower_bound = -4.0; 19 | constexpr double upper_bound = 4.0; 20 | constexpr double delta = (upper_bound - lower_bound) / 21 | static_cast(points_per_dimension - 1); 22 | 23 | // Note: spectre DataVector constructors take these parameters in 24 | // the opposite order: first the size, then the value to initialize each 25 | // element 26 | DataVector x_coords(std::numeric_limits::signaling_NaN(), 27 | number_of_points); 28 | DataVector y_coords(std::numeric_limits::signaling_NaN(), 29 | number_of_points); 30 | 31 | size_t i = 0; 32 | for (double y = lower_bound; y <= upper_bound; y += delta) { 33 | for (double x = lower_bound; x <= upper_bound; x += delta) { 34 | x_coords[i] = x; 35 | y_coords[i] = y; 36 | ++i; 37 | } 38 | } 39 | 40 | SpatialVector coords{{x_coords, y_coords}}; 41 | std::cout << to_string(coords) << "\n"; 42 | std::cout << to_string(dot_product(coords, coords)) << "\n"; 43 | 44 | std::cout << "\n\n"; 45 | 46 | std::cout << std::fixed << std::setprecision(4); 47 | DataVector gauss = exp(-1.0 * dot_product(coords, coords)); 48 | for (size_t i = 0; i < number_of_points; ++i) { 49 | if (i % points_per_dimension == 0) { 50 | std::cout << "\n"; 51 | } 52 | std::cout << gauss[i] << " "; 53 | } 54 | std::cout << "\n"; 55 | 56 | return 0; 57 | }; 58 | -------------------------------------------------------------------------------- /Solutions/Lesson4/SpatialVector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Code to make a string from a container type 10 | template 11 | std::string to_string(const T& container) noexcept { 12 | std::stringstream output_stream{}; 13 | output_stream << "["; 14 | for (auto element : container) { 15 | output_stream << element << " "; 16 | } 17 | std::string output_string = output_stream.str(); 18 | output_string.pop_back(); 19 | output_string += "]"; 20 | return output_string; 21 | } 22 | 23 | namespace Frame { 24 | struct Grid {}; 25 | struct Inertial {}; 26 | } // namespace Frame 27 | 28 | template 29 | class SpatialVector { 30 | public: 31 | static constexpr size_t volume_dim = VolumeDim; 32 | using data_type = DataType; 33 | 34 | SpatialVector(const std::array& data) noexcept 35 | : data_(data) {} 36 | 37 | const auto& get(const size_t i) const noexcept { 38 | if (i < VolumeDim) { 39 | return data_[i]; 40 | } else { 41 | std::cout << "ERROR: index " << i << " out of bounds for VolumeDim " 42 | << volume_dim << "\n"; 43 | exit(-1); 44 | } 45 | } 46 | 47 | private: 48 | std::array data_; 49 | }; 50 | 51 | template 52 | const auto& get(const T& vector) noexcept { 53 | static_assert(Index < T::volume_dim, 54 | "Index should be nonnegative and less than VolumeDim"); 55 | return vector.get(Index); 56 | } 57 | 58 | template 59 | DataType dot_product( 60 | const SpatialVector& vector_a, 61 | const SpatialVector& vector_b) noexcept { 62 | DataType result{get<0>(vector_a) * get<0>(vector_b)}; 63 | for (size_t i = 1; i < VolumeDim; ++i) { 64 | result += vector_a.get(i) * vector_b.get(i); 65 | } 66 | 67 | return result; 68 | } 69 | 70 | // Code to make a string from a SpatialVector 71 | template 72 | std::string to_string( 73 | const SpatialVector& spatial_vector) noexcept { 74 | std::stringstream output_stream{}; 75 | output_stream << "["; 76 | for (size_t i = 0; i < VolumeDim; ++i) { 77 | output_stream << to_string(spatial_vector.get(i)) << " "; 78 | } 79 | std::string output_string = output_stream.str(); 80 | output_string.pop_back(); 81 | output_string += "]"; 82 | return output_string; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Solutions/Lesson4/SpatialVectorStart.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace Frame { 6 | struct Grid {}; 7 | struct Inertial {}; 8 | } // namespace Frame 9 | 10 | template 11 | class SpatialVector { 12 | public: 13 | static constexpr size_t volume_dim = VolumeDim; 14 | using data_type = DataType; 15 | 16 | SpatialVector(const std::array& data) noexcept 17 | : data_(data) {} 18 | 19 | const auto& get(const size_t i) const noexcept { 20 | if (i < VolumeDim) { 21 | return data_[i]; 22 | } else { 23 | std::cout << "ERROR: index " << i << " out of bounds for VolumeDim " 24 | << volume_dim << "\n"; 25 | exit(-1); 26 | } 27 | } 28 | 29 | private: 30 | std::array data_; 31 | }; 32 | 33 | template 34 | const auto& get(const T& vector) noexcept { 35 | static_assert(Index < T::volume_dim, 36 | "Index should be nonnegative and less than VolumeDim"); 37 | return vector.get(Index); 38 | } 39 | 40 | template 41 | DataType dot_product( 42 | const SpatialVector& vector_a, 43 | const SpatialVector& vector_b) noexcept { 44 | DataType result{get<0>(vector_a) * get<0>(vector_b)}; 45 | for (size_t i = 1; i < VolumeDim; ++i) { 46 | result += vector_a.get(i) * vector_b.get(i); 47 | } 48 | 49 | return result; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Solutions/Lesson4/TaggedTuple.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tagged_tuple.hpp" 4 | 5 | namespace Rectangle { 6 | struct Length { 7 | using type = double; 8 | }; 9 | struct Height { 10 | using type = double; 11 | }; 12 | struct Area { 13 | using type = double; 14 | }; 15 | } // namespace Rectangle 16 | 17 | template 18 | double area(const T& box) noexcept { 19 | return tuples::get(box) * 20 | tuples::get(box); 21 | } 22 | 23 | int main() { 24 | tuples::tagged_tuple simple_box{}; 25 | tuples::get(simple_box) = 6.0; 26 | tuples::get(simple_box) = 7.0; 27 | 28 | std::cout << area(simple_box) << "\n"; 29 | 30 | tuples::tagged_tuple simple_box_no_height{}; 31 | tuples::get(simple_box_no_height) = 6.0; 32 | 33 | // Compiler error: height not in box 34 | // std::cout << simple_area(simple_box_no_height) << "\n"; 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /Solutions/Lesson4/Tags.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace Rectangle { 7 | struct Length {}; 8 | struct Height {}; 9 | struct Area {}; 10 | } // namespace Rectangle 11 | 12 | template 13 | double area(const T& box) noexcept { 14 | const double length = 15 | std::get>(box).second; 16 | const double height = 17 | std::get>(box).second; 18 | return length * height; 19 | } 20 | 21 | int main() { 22 | std::tuple test_tuple{"Width", 4.0}; 23 | std::cout << std::get<0>(test_tuple) << " " << std::get<1>(test_tuple) 24 | << "\n"; 25 | 26 | using Length = std::pair; 27 | using Height = std::pair; 28 | std::tuple box{}; 29 | std::get(box).second = 4.0; 30 | std::get(box).second = 5.0; 31 | 32 | std::cout << std::get(box).second << "\n"; 33 | 34 | std::cout << area(box) << "\n"; 35 | 36 | std::tuple box_no_height{}; 37 | std::get(box_no_height).second = 4.0; 38 | 39 | // Compiler error: height not in box 40 | // std::cout << area(box_no_height) << "\n"; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /Solutions/Lesson4/Trap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | double test_function(const double x) noexcept { 5 | return sqrt(fabs(1.0 - x * x)); 6 | } 7 | 8 | struct test_functor { 9 | double operator()(const double x) const noexcept { 10 | return sqrt(fabs(1.0 - x * x)); 11 | } 12 | }; 13 | 14 | template 15 | double trap(const T& f, const double lower_bound, const double upper_bound, 16 | const double step_size) noexcept { 17 | double result = 0.0; 18 | for (double x = lower_bound; x <= upper_bound; x += step_size) { 19 | result += 0.5 * step_size * (f(x) + f(x + step_size)); 20 | } 21 | 22 | return result; 23 | } 24 | 25 | int main() { 26 | std::cout << 4.0 * trap(test_function, 0.0, 1.0, 0.01) << "\n"; 27 | std::cout << 4.0 * trap(test_functor{}, 0.0, 1.0, 0.01) << "\n"; 28 | std::cout << 4.0 * trap([](const auto x) { return sqrt(fabs(1.0 - x * x)); }, 29 | 0.0, 1.0, 0.01) 30 | << "\n"; 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /Solutions/Lesson4/tagged_tuple.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | @file 3 | @copyright Nils Deppe 2017 4 | (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tuples { 14 | 15 | #if __cplusplus >= 201402L 16 | #define TUPLES_LIB_CONSTEXPR_CXX_14 constexpr 17 | #else 18 | #define TUPLES_LIB_CONSTEXPR_CXX_14 19 | #endif 20 | 21 | namespace tuples_detail { 22 | 23 | template 24 | inline constexpr T&& forward( 25 | typename std::remove_reference::type& t) noexcept { 26 | return static_cast(t); 27 | } 28 | 29 | template 30 | inline constexpr T&& forward( 31 | typename std::remove_reference::type&& t) noexcept { 32 | static_assert(!std::is_lvalue_reference::value, 33 | "cannot forward an rvalue as an lvalue"); 34 | return static_cast(t); 35 | } 36 | 37 | template 38 | struct value_list {}; 39 | 40 | template 41 | struct typelist {}; 42 | 43 | template 44 | struct make_void { 45 | typedef void type; 46 | }; 47 | template 48 | using void_t = typename make_void::type; 49 | 50 | template 51 | using all = typename std::is_same< 52 | value_list, 53 | value_list(Bs), true)...>>::type; 54 | 55 | struct no_such_type { 56 | no_such_type() = delete; 57 | no_such_type(no_such_type const& /*unused*/) = delete; 58 | no_such_type(no_such_type&& /*unused*/) = delete; 59 | ~no_such_type() = delete; 60 | no_such_type& operator=(no_such_type const& /*unused*/) = delete; 61 | no_such_type operator=(no_such_type&& /*unused*/) = delete; 62 | }; 63 | 64 | namespace detail { 65 | using std::swap; 66 | 67 | template ::value and not std::is_void::value> 69 | struct is_swappable_with { 70 | template 71 | static auto test_swap(int) 72 | -> decltype(swap(std::declval(), std::declval())); 73 | template 74 | static tuples::tuples_detail::no_such_type test_swap(...); 75 | 76 | static const bool value = 77 | not std::is_same(0)), 78 | tuples::tuples_detail::no_such_type>::value and 79 | not std::is_same(0)), 80 | tuples::tuples_detail::no_such_type>::value; 81 | }; 82 | 83 | template 84 | struct is_swappable_with : std::false_type {}; 85 | } // namespace detail 86 | 87 | template 88 | using is_swappable_with = detail::is_swappable_with; 89 | 90 | template ::value> 91 | struct is_nothrow_swappable_with { 92 | private: 93 | static constexpr bool check() { 94 | using std::swap; 95 | return noexcept(swap(std::declval(), std::declval())) and 96 | noexcept(swap(std::declval(), std::declval())); 97 | } 98 | 99 | public: 100 | static const bool value = check(); 101 | }; 102 | 103 | template 104 | struct is_nothrow_swappable_with : std::false_type {}; 105 | 106 | template 107 | constexpr char swallow(Ts&&...) noexcept { 108 | return '0'; 109 | } 110 | } // namespace tuples_detail 111 | 112 | namespace tuples_detail { 113 | template ::value && 115 | !__is_final(typename Tag::type)> 116 | class tagged_tuple_leaf; 117 | 118 | template 119 | void swap(tagged_tuple_leaf& lhs, tagged_tuple_leaf& rhs) noexcept( 120 | is_nothrow_swappable_with::value) { 121 | using std::swap; 122 | swap(lhs.get(), rhs.get()); 123 | } 124 | 125 | template 126 | class tagged_tuple_leaf { 127 | using value_type = typename Tag::type; 128 | value_type value_; 129 | 130 | template 131 | static constexpr bool can_bind_reference() noexcept { 132 | using rem_ref_value_type = typename std::remove_reference::type; 133 | using rem_ref_T = typename std::remove_reference::type; 134 | using is_lvalue_type = std::integral_constant< 135 | bool, 136 | std::is_lvalue_reference::value or 137 | std::is_same, 138 | rem_ref_T>::value or 139 | std::is_same::type>, 141 | rem_ref_T>::value>; 142 | return not std::is_reference::value or 143 | (std::is_lvalue_reference::value and 144 | is_lvalue_type::value) or 145 | (std::is_rvalue_reference::value and 146 | not std::is_lvalue_reference::value); 147 | } 148 | 149 | public: 150 | // Tested in constexpr context in Unit.tagged_tuple.Ebo 151 | constexpr tagged_tuple_leaf() noexcept( 152 | std::is_nothrow_default_constructible::value) 153 | : value_() { 154 | static_assert( 155 | !std::is_reference::value, 156 | "Cannot default construct a reference element in a tagged_tuple"); 157 | } 158 | 159 | template ::type, 162 | tagged_tuple_leaf>::value && 163 | std::is_constructible::value>::type* = nullptr> 164 | constexpr explicit tagged_tuple_leaf(T&& t) noexcept( 165 | std::is_nothrow_constructible::value) 166 | : value_(tuples_detail::forward(t)) { 167 | static_assert(can_bind_reference(), 168 | "Cannot construct an lvalue reference with an rvalue"); 169 | } 170 | 171 | constexpr tagged_tuple_leaf(tagged_tuple_leaf const& /*rhs*/) = default; 172 | constexpr tagged_tuple_leaf(tagged_tuple_leaf&& /*rhs*/) = default; 173 | 174 | #if __cplusplus < 201402L 175 | value_type& get() noexcept { return value_; } 176 | #else 177 | constexpr value_type& get() noexcept { return value_; } 178 | #endif 179 | constexpr const value_type& get() const noexcept { return value_; } 180 | 181 | bool swap(tagged_tuple_leaf& t) noexcept( 182 | is_nothrow_swappable_with::value) { 183 | using std::swap; 184 | swap(*this, t); 185 | return false; 186 | } 187 | }; 188 | 189 | template 190 | class tagged_tuple_leaf : private Tag::type { 191 | using value_type = typename Tag::type; 192 | 193 | public: 194 | constexpr tagged_tuple_leaf() noexcept( 195 | std::is_nothrow_default_constructible::value) 196 | : value_type{} {} 197 | 198 | template ::type, 201 | tagged_tuple_leaf>::value && 202 | std::is_constructible::value>::type* = nullptr> 203 | constexpr explicit tagged_tuple_leaf(T&& t) noexcept( 204 | std::is_nothrow_constructible::value) 205 | : value_type(tuples_detail::forward(t)) {} 206 | 207 | constexpr tagged_tuple_leaf(tagged_tuple_leaf const& /*rhs*/) = default; 208 | constexpr tagged_tuple_leaf(tagged_tuple_leaf&& /*rhs*/) = default; 209 | 210 | #if __cplusplus < 201402L 211 | value_type& get() noexcept { return static_cast(*this); } 212 | #else 213 | constexpr value_type& get() noexcept { 214 | return static_cast(*this); 215 | } 216 | #endif 217 | 218 | constexpr const value_type& get() const noexcept { 219 | return static_cast(*this); 220 | } 221 | 222 | bool swap(tagged_tuple_leaf& t) noexcept( 223 | is_nothrow_swappable_with::value) { 224 | using std::swap; 225 | swap(*this, t); 226 | return false; 227 | } 228 | }; 229 | 230 | struct disable_constructors { 231 | static constexpr bool enable_default() noexcept { return false; } 232 | static constexpr bool enable_explicit() noexcept { return false; } 233 | static constexpr bool enable_implicit() noexcept { return false; } 234 | }; 235 | } // namespace tuples_detail 236 | 237 | /*! 238 | * \brief A compile time associative container between structs and data 239 | * 240 | * The template parameters to `tagged_tuple` are `struct`s with a member type 241 | * alias, `type` denoting the type of the data to be associated with the tag. 242 | * An example tag is: 243 | * \snippet runtime_tests.cpp example_tag_simple 244 | * A tagged_tuple is created by, for example 245 | * \snippet runtime_tests.cpp example_simple_create 246 | * Elements are retrieved using the `get` functions as follows: 247 | * \snippet runtime_tests.cpp example_get_function 248 | */ 249 | template 250 | class tagged_tuple; 251 | 252 | template 253 | constexpr const typename Tag::type& get( 254 | const tagged_tuple& t) noexcept; 255 | template 256 | constexpr typename Tag::type& get(tagged_tuple& t) noexcept; 257 | template 258 | constexpr const typename Tag::type&& get( 259 | const tagged_tuple&& t) noexcept; 260 | template 261 | constexpr typename Tag::type&& get(tagged_tuple&& t) noexcept; 262 | 263 | /*! 264 | * \brief Metafunction that returns the type of the Tag 265 | */ 266 | template 267 | using tag_type = typename Tag::type; 268 | 269 | template 270 | class tagged_tuple : private tuples_detail::tagged_tuple_leaf... { 271 | template 272 | struct pack_is_tagged_tuple : std::false_type {}; 273 | template 274 | struct pack_is_tagged_tuple 275 | : std::is_same::type, tagged_tuple> {}; 276 | 277 | template 278 | struct args_constructor : tuples_detail::disable_constructors {}; 279 | 280 | template 281 | struct args_constructor { 282 | static constexpr bool enable_default() { 283 | return tuples_detail::all< 284 | std::is_default_constructible>::value...>::value; 285 | } 286 | 287 | template 288 | static constexpr bool enable_explicit() noexcept { 289 | return tuples_detail::all< 290 | std::is_constructible, 291 | Ts>::value...>::value and 292 | not tuples_detail::all< 293 | std::is_convertible>::value...>::value; 294 | } 295 | template 296 | static constexpr bool enable_implicit() noexcept { 297 | return sizeof...(Ts) == sizeof...(Tags) and 298 | tuples_detail::all< 299 | std::is_constructible, 300 | Ts>::value...>::value and 301 | tuples_detail::all< 302 | std::is_convertible>::value...>::value; 303 | } 304 | }; 305 | 306 | template 308 | struct tuple_like_constructor : tuples_detail::disable_constructors {}; 309 | 310 | template 311 | struct tuple_like_constructor { 312 | template 313 | static constexpr bool enable_explicit() noexcept { 314 | return not tuples_detail::all< 315 | std::is_convertible>::value...>::value; 316 | } 317 | 318 | template 319 | static constexpr bool enable_implicit() noexcept { 320 | return tuples_detail::all< 321 | std::is_convertible>::value...>::value; 322 | } 323 | }; 324 | 325 | template 326 | struct tuple_like_constructor { 327 | template 328 | static constexpr bool enable_explicit() noexcept { 329 | return not tuples_detail::all< 330 | std::is_convertible>::value...>::value and 331 | (not tuples_detail::all>::value...>::value and 333 | not tuples_detail::all, Tuple>::value...>::value and 335 | not tuples_detail::all< 336 | std::is_same, Ts>::value...>::value); 337 | } 338 | 339 | template 340 | static constexpr bool enable_implicit() noexcept { 341 | return tuples_detail::all< 342 | std::is_convertible>::value...>::value and 343 | (not tuples_detail::all>::value...>::value and 345 | not tuples_detail::all, Tuple>::value...>::value and 347 | not tuples_detail::all< 348 | std::is_same, Ts>::value...>::value); 349 | } 350 | }; 351 | 352 | // C++17 Draft 23.5.3.2 Assignment - helper aliases 353 | using is_copy_assignable = 354 | tuples_detail::all>::value...>; 355 | using is_nothrow_copy_assignable = tuples_detail::all< 356 | std::is_nothrow_copy_assignable>::value...>; 357 | using is_move_assignable = 358 | tuples_detail::all>::value...>; 359 | using is_nothrow_move_assignable = tuples_detail::all< 360 | std::is_nothrow_move_assignable>::value...>; 361 | 362 | template 363 | friend constexpr const typename Tag::type& get( 364 | const tagged_tuple& t) noexcept; 365 | template 366 | friend constexpr typename Tag::type& get(tagged_tuple& t) noexcept; 367 | template 368 | friend constexpr const typename Tag::type&& get( 369 | const tagged_tuple&& t) noexcept; 370 | template 371 | friend constexpr typename Tag::type&& get( 372 | tagged_tuple&& t) noexcept; 373 | 374 | public: 375 | // C++17 Draft 23.5.3.1 Construction 376 | template ::enable_default()>::type* = nullptr> 379 | constexpr tagged_tuple() noexcept( 380 | tuples_detail::all>::value...>::value) {} 382 | 383 | constexpr tagged_tuple(tagged_tuple const& /*rhs*/) = default; 384 | constexpr tagged_tuple(tagged_tuple&& /*rhs*/) = default; 385 | 386 | template < 387 | bool Dummy = true, 388 | typename std::enable_if::template enable_explicit< 389 | tag_type const&...>()>::type* = nullptr> 390 | constexpr explicit tagged_tuple(tag_type const&... ts) noexcept( 391 | tuples_detail::all>::value...>::value) 393 | : tuples_detail::tagged_tuple_leaf(ts)... {} 394 | 395 | template < 396 | bool Dummy = true, 397 | typename std::enable_if::template enable_implicit< 398 | tag_type const&...>()>::type* = nullptr> 399 | constexpr tagged_tuple(tag_type const&... ts) noexcept( 400 | tuples_detail::all>::value...>::value) 402 | : tuples_detail::tagged_tuple_leaf(ts)... {} 403 | 404 | template ::value and 407 | sizeof...(Us) == sizeof...(Tags)>:: 408 | template enable_explicit()>::type* = nullptr> 409 | constexpr explicit tagged_tuple(Us&&... us) noexcept( 410 | tuples_detail::all, Us&&>::value...>::value) 412 | : tuples_detail::tagged_tuple_leaf( 413 | tuples_detail::forward(us))... {} 414 | 415 | template ::value and 418 | sizeof...(Us) == sizeof...(Tags)>:: 419 | template enable_implicit()>::type* = nullptr> 420 | constexpr tagged_tuple(Us&&... us) noexcept( 421 | tuples_detail::all, Us&&>::value...>::value) 423 | : tuples_detail::tagged_tuple_leaf( 424 | tuples_detail::forward(us))... {} 425 | 426 | template < 427 | class... UTags, 428 | typename std::enable_if< 429 | tuple_like_constructor< 430 | sizeof...(Tags) == sizeof...(UTags) and 431 | tuples_detail::all, tag_type const&>::value...>::value>:: 433 | template enable_explicit const&, 434 | tag_type...>()>::type* = nullptr> 435 | constexpr explicit tagged_tuple(tagged_tuple const& t) noexcept( 436 | tuples_detail::all, tag_type const&>::value...>::value) 438 | : tuples_detail::tagged_tuple_leaf(get(t))... {} 439 | 440 | template < 441 | class... UTags, 442 | typename std::enable_if< 443 | tuple_like_constructor< 444 | sizeof...(Tags) == sizeof...(UTags) and 445 | tuples_detail::all, tag_type const&>::value...>::value>:: 447 | template enable_implicit const&, 448 | tag_type...>()>::type* = nullptr> 449 | constexpr tagged_tuple(tagged_tuple const& t) noexcept( 450 | tuples_detail::all, tag_type const&>::value...>::value) 452 | : tuples_detail::tagged_tuple_leaf(get(t))... {} 453 | 454 | template < 455 | class... UTags, 456 | typename std::enable_if< 457 | tuple_like_constructor< 458 | sizeof...(Tags) == sizeof...(UTags) and 459 | tuples_detail::all, tag_type&&>::value...>::value>:: 461 | template enable_explicit&&, 462 | tag_type...>()>::type* = nullptr> 463 | constexpr explicit tagged_tuple(tagged_tuple&& t) noexcept( 464 | tuples_detail::all, tag_type&&>::value...>::value) 466 | : tuples_detail::tagged_tuple_leaf( 467 | tuples_detail::forward>(get(t)))... {} 468 | 469 | template < 470 | class... UTags, 471 | typename std::enable_if< 472 | tuple_like_constructor< 473 | sizeof...(Tags) == sizeof...(UTags) and 474 | tuples_detail::all, tag_type&&>::value...>::value>:: 476 | template enable_implicit&&, 477 | tag_type...>()>::type* = nullptr> 478 | constexpr tagged_tuple(tagged_tuple&& t) noexcept( 479 | tuples_detail::all, tag_type&&>::value...>::value) 481 | : tuples_detail::tagged_tuple_leaf( 482 | tuples_detail::forward>(get(t)))... {} 483 | 484 | // C++17 Draft 23.5.3.2 Assignment 485 | tagged_tuple& operator=( 486 | typename std::conditional::type const& 488 | t) noexcept(is_nothrow_copy_assignable::value) { 489 | static_cast( 490 | tuples_detail::swallow((get(*this) = get(t))...)); 491 | return *this; 492 | } 493 | 494 | tagged_tuple& operator=( 495 | typename std::conditional::type&& 497 | t) noexcept(is_nothrow_move_assignable::value) { 498 | static_cast(tuples_detail::swallow( 499 | (get(*this) = 500 | tuples_detail::forward>(get(t)))...)); 501 | return *this; 502 | } 503 | 504 | template &, 509 | tag_type const&>::value...>::value>::type* = nullptr> 510 | tagged_tuple& operator=(tagged_tuple const& t) noexcept( 511 | tuples_detail::all&, tag_type const&>::value...>::value) { 513 | static_cast( 514 | tuples_detail::swallow((get(*this) = get(t))...)); 515 | return *this; 516 | } 517 | 518 | template < 519 | class... UTags, 520 | typename std::enable_if< 521 | sizeof...(Tags) == sizeof...(UTags) and 522 | tuples_detail::all&, tag_type&&>::value...>::value>::type* = 524 | nullptr> 525 | tagged_tuple& operator=(tagged_tuple&& t) noexcept( 526 | tuples_detail::all&, tag_type&&>::value...>::value) { 528 | static_cast(tuples_detail::swallow( 529 | (get(*this) = 530 | tuples_detail::forward>(get(t)))...)); 531 | return *this; 532 | } 533 | 534 | // C++17 Draft 23.5.3.3 swap 535 | void swap(tagged_tuple& t) noexcept( 536 | tuples_detail::all, 538 | tuples_detail::tagged_tuple_leaf>::value...>::value) { 539 | tuples_detail::swallow(tuples_detail::tagged_tuple_leaf::swap( 540 | static_cast&>(t))...); 541 | } 542 | }; 543 | 544 | /// \cond 545 | template <> 546 | class tagged_tuple<> { 547 | public: 548 | tagged_tuple() noexcept {} 549 | void swap(tagged_tuple& /*unused*/) noexcept {} 550 | }; 551 | /// \endcond 552 | 553 | // C++17 Draft 23.5.3.6 Tuple helper classes 554 | /*! 555 | * \brief Has a member variable `value` equal to the number of elements in a 556 | * tagged_tuple 557 | */ 558 | template 559 | struct tuple_size; 560 | 561 | /// \cond 562 | template 563 | struct tuple_size> 564 | : std::integral_constant {}; 565 | template 566 | struct tuple_size> 567 | : tuple_size> {}; 568 | template 569 | struct tuple_size> 570 | : tuple_size> {}; 571 | template 572 | struct tuple_size> 573 | : tuple_size> {}; 574 | /// \endcond 575 | 576 | // C++17 Draft 23.5.3.7 Element access 577 | // @{ 578 | /*! 579 | * \brief Retrieve the element with tag `Tag` from the tagged_tuple `t` 580 | */ 581 | template 582 | inline constexpr const typename Tag::type& get( 583 | const tagged_tuple& t) noexcept { 584 | return static_cast&>(t).get(); 585 | } 586 | template 587 | inline constexpr typename Tag::type& get(tagged_tuple& t) noexcept { 588 | return static_cast&>(t).get(); 589 | } 590 | template 591 | inline constexpr const typename Tag::type&& get( 592 | const tagged_tuple&& t) noexcept { 593 | return static_cast( 594 | static_cast&&>(t).get()); 595 | } 596 | template 597 | inline constexpr typename Tag::type&& get(tagged_tuple&& t) noexcept { 598 | return static_cast( 599 | static_cast&&>(t).get()); 600 | } 601 | // @} 602 | 603 | // C++17 Draft 23.5.3.8 Relational operators 604 | namespace tuples_detail { 605 | struct equal { 606 | template 607 | static TUPLES_LIB_CONSTEXPR_CXX_14 void apply( 608 | T const& lhs, U const& rhs, bool& result) noexcept(noexcept(lhs == rhs)) { 609 | result = result and lhs == rhs; 610 | } 611 | }; 612 | 613 | template 614 | TUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_equal_impl( 615 | tagged_tuple const& lhs, 616 | tagged_tuple const& 617 | rhs) noexcept(noexcept(std::initializer_list{ 618 | (get(lhs) == get(rhs))...})) { 619 | bool equal = true; 620 | // This short circuits in the sense that the operator== is only evaluated if 621 | // the result thus far is true 622 | static_cast(std::initializer_list{ 623 | (equal::apply(get(lhs), get(rhs), equal), '0')...}); 624 | return equal; 625 | } 626 | } // namespace tuples_detail 627 | 628 | template ::type* = 630 | nullptr> 631 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator==( 632 | tagged_tuple const& lhs, 633 | tagged_tuple const& 634 | rhs) noexcept(noexcept(tuples_detail::tuple_equal_impl(lhs, rhs))) { 635 | return tuples_detail::tuple_equal_impl(lhs, rhs); 636 | } 637 | 638 | template ::type* = 640 | nullptr> 641 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator!=( 642 | tagged_tuple const& lhs, 643 | tagged_tuple const& rhs) noexcept(noexcept(lhs == rhs)) { 644 | return not(lhs == rhs); 645 | } 646 | 647 | namespace tuples_detail { 648 | struct less { 649 | template 650 | static TUPLES_LIB_CONSTEXPR_CXX_14 void apply( 651 | T const& lhs, U const& rhs, bool& last_rhs_less_lhs, 652 | bool& result) noexcept(noexcept(lhs < rhs) and noexcept(rhs < lhs)) { 653 | if (result or last_rhs_less_lhs) { 654 | return; 655 | } 656 | result = lhs < rhs; 657 | if (result) { 658 | return; 659 | } 660 | last_rhs_less_lhs = rhs < lhs; 661 | } 662 | }; 663 | 664 | template 665 | TUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_less_impl( 666 | tagged_tuple const& lhs, 667 | tagged_tuple const& 668 | rhs) noexcept(noexcept(std::initializer_list{ 669 | (get(lhs) < get(rhs))...})) { 670 | bool result = false; 671 | bool last_rhs_less_lhs = false; 672 | static_cast(std::initializer_list{ 673 | (less::apply(get(lhs), get(rhs), last_rhs_less_lhs, result), 674 | '0')...}); 675 | return result; 676 | } 677 | } // namespace tuples_detail 678 | 679 | template ::type* = 681 | nullptr> 682 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator<( 683 | tagged_tuple const& lhs, 684 | tagged_tuple const& 685 | rhs) noexcept(noexcept(tuples_detail::tuple_less_impl(lhs, rhs))) { 686 | return tuples_detail::tuple_less_impl(lhs, rhs); 687 | } 688 | 689 | template ::type* = 691 | nullptr> 692 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator>( 693 | tagged_tuple const& lhs, 694 | tagged_tuple const& rhs) noexcept(noexcept(rhs < lhs)) { 695 | return rhs < lhs; 696 | } 697 | 698 | template ::type* = 700 | nullptr> 701 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator<=( 702 | tagged_tuple const& lhs, 703 | tagged_tuple const& rhs) noexcept(noexcept(rhs < lhs)) { 704 | return not(rhs < lhs); 705 | } 706 | 707 | template ::type* = 709 | nullptr> 710 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator>=( 711 | tagged_tuple const& lhs, 712 | tagged_tuple const& rhs) noexcept(noexcept(lhs < rhs)) { 713 | return not(lhs < rhs); 714 | } 715 | 716 | // C++17 Draft 23.5.3.3 swap 717 | template < 718 | class... Tags, 719 | typename std::enable_if, 721 | tuples_detail::tagged_tuple_leaf>::value...>::value>::type* = 722 | nullptr> 723 | void swap(tagged_tuple& lhs, tagged_tuple& rhs) noexcept( 724 | tuples_detail::all, 726 | tuples_detail::tagged_tuple_leaf>::value...>::value) { 727 | lhs.swap(rhs); 728 | } 729 | 730 | namespace tuples_detail { 731 | template > 732 | struct is_streamable : std::false_type {}; 733 | template 734 | struct is_streamable< 735 | S, T, 736 | void_t::type>() 737 | << std::declval()), 738 | typename std::enable_if::value>::type>> 739 | : std::true_type {}; 740 | 741 | template 742 | using is_streamable_t = typename is_streamable::type; 743 | 744 | struct stream_helper { 745 | template 746 | void operator()(std::ostream& os, const T& element, size_t& current_value, 747 | const size_t num_tags, const std::true_type /*meta*/) { 748 | os << element; 749 | current_value++; 750 | if (current_value < num_tags) { 751 | os << std::string(", "); 752 | } 753 | } 754 | 755 | template 756 | void operator()(std::ostream& os, const T& /*element*/, size_t& current_value, 757 | const size_t num_tags, const std::false_type /*meta*/) { 758 | os << std::string("NOT STREAMABLE"); 759 | current_value++; 760 | if (current_value < num_tags) { 761 | os << std::string(", "); 762 | } 763 | } 764 | }; 765 | } // namespace tuples_detail 766 | 767 | template 768 | std::ostream& operator<<(std::ostream& os, const tagged_tuple& t) { 769 | os << std::string("("); 770 | size_t current_value = 0; 771 | tuples_detail::stream_helper helper; 772 | // With empty tagged_tuple's helper is unused 773 | static_cast(helper); 774 | static_cast(std::initializer_list{ 775 | (helper(os, get(t), current_value, sizeof...(Tags), 776 | tuples_detail::is_streamable_t>{}), 777 | '0')...}); 778 | return os << std::string(")"); 779 | } 780 | 781 | } // namespace tuples 782 | -------------------------------------------------------------------------------- /Solutions/Lesson5/basic_brigand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "brigand.hpp" 3 | 4 | namespace tmpl = brigand; 5 | 6 | // Print out the type T at compile time by attempting to instantiate this. 7 | // The attempt will generate a compiler error containing the value 8 | template 9 | struct TypeDisplayer; 10 | 11 | int main() { 12 | using test_list = tmpl::list; 13 | 14 | using three = tmpl::integral_constant; 15 | using four = tmpl::integral_constant; 16 | using five = tmpl::integral_constant; 17 | 18 | using list_of_three_integers = tmpl::list; 19 | using sum = tmpl::plus; 20 | 21 | // Compiler error: prints the LHS and RHS of the sum 22 | TypeDisplayer lhs_and_rhs; 23 | 24 | // Compiler error: prints the result of the sum 25 | TypeDisplayer> lhs_plus_rhs; 26 | 27 | // Compiler error: prints the result of a comparison 28 | TypeDisplayer::value>> is_less; 29 | 30 | // Compiler error: prints which of two integers is smaller 31 | using which_is_smaller = tmpl::if_, three, four>; 32 | TypeDisplayer result; 33 | 34 | using unsorted_list = tmpl::list; 35 | TypeDisplayer unsorted; 36 | 37 | using sorted_list = tmpl::sort; 38 | TypeDisplayer sorted; 39 | 40 | std::cout << sum::value << "\n"; 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /Solutions/Lesson5/basic_tmpl_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tmpl { 4 | 5 | // Use structs to hold a fixed number of types 6 | struct empty_list {}; 7 | 8 | template 9 | struct list_of_one_type {}; 10 | 11 | template 12 | struct list_of_two_types {}; 13 | 14 | // Better: a template parameter pack accepts a variable number of types 15 | template 16 | struct list {}; 17 | 18 | // Example of a type that's an integral constant 19 | template 20 | struct integral_constant { 21 | static const T value = Value; 22 | using value_type = T; 23 | }; 24 | 25 | // Example of a type that's a sum of two integral types 26 | template 27 | struct plus 28 | : integral_constant {}; 29 | 30 | // Special case of integral constant: a boolean 31 | template 32 | using bool_ = integral_constant; 33 | 34 | // Example of a comparison 35 | template 36 | struct less : bool_<(T1::value < T2::value)> {}; 37 | 38 | // Example of an "if" statement: 39 | template 40 | struct if_ : integral_constant; 41 | 42 | template 43 | struct if_ : integral_constant {}; 44 | 45 | template 46 | struct factorial 47 | : integral_constant< 48 | typename T::value_type, 49 | T::value * factorial>::value> {}; 51 | template 52 | struct factorial> : integral_constant {}; 53 | 54 | } // namespace tmpl 55 | 56 | // Print out the type T at compile time by attempting to instantiate this. 57 | // The attempt will generate a compiler error containing the value 58 | template 59 | struct TypeDisplayer; 60 | 61 | int main() { 62 | using test_list = tmpl::list; 63 | 64 | using three = tmpl::integral_constant; 65 | using four = tmpl::integral_constant; 66 | using five = tmpl::integral_constant; 67 | 68 | using list_of_three_integers = tmpl::list; 69 | using sum = tmpl::plus; 70 | 71 | // Compiler error: prints the LHS and RHS of the sum 72 | TypeDisplayer lhs_and_rhs; 73 | 74 | // Compiler error: prints the result of the sum 75 | TypeDisplayer> lhs_plus_rhs; 76 | 77 | // Compiler error: prints the result of a comparison 78 | TypeDisplayer::value>> is_less; 79 | 80 | // Compiler error: prints which of two integers is smaller 81 | using which_is_smaller = 82 | tmpl::if_::value, three, four>; 83 | TypeDisplayer> 84 | result; 85 | 86 | std::cout << sum::value << "\n"; 87 | return 0; 88 | } -------------------------------------------------------------------------------- /Solutions/Lesson5/factorial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | size_t decrement() noexcept { 7 | return N - 1; 8 | } 9 | 10 | template <> 11 | size_t decrement<0>() noexcept { 12 | return 0; 13 | } 14 | 15 | template 16 | size_t factorial() noexcept { 17 | return N * factorial(); 18 | } 19 | 20 | template <> 21 | size_t factorial<0>() noexcept { 22 | return 1; 23 | } 24 | 25 | int main() { 26 | std::cout << decrement<4>() << "\n"; 27 | 28 | std::cout << decrement<0>() << "\n"; 29 | 30 | std::cout << factorial<5>() << "\n"; 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /Solutions/Lesson5/tagged_tuple.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | @file 3 | @copyright Nils Deppe 2017 4 | (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tuples { 14 | 15 | #if __cplusplus >= 201402L 16 | #define TUPLES_LIB_CONSTEXPR_CXX_14 constexpr 17 | #else 18 | #define TUPLES_LIB_CONSTEXPR_CXX_14 19 | #endif 20 | 21 | namespace tuples_detail { 22 | 23 | template 24 | inline constexpr T&& forward( 25 | typename std::remove_reference::type& t) noexcept { 26 | return static_cast(t); 27 | } 28 | 29 | template 30 | inline constexpr T&& forward( 31 | typename std::remove_reference::type&& t) noexcept { 32 | static_assert(!std::is_lvalue_reference::value, 33 | "cannot forward an rvalue as an lvalue"); 34 | return static_cast(t); 35 | } 36 | 37 | template 38 | struct value_list {}; 39 | 40 | template 41 | struct typelist {}; 42 | 43 | template 44 | struct make_void { 45 | typedef void type; 46 | }; 47 | template 48 | using void_t = typename make_void::type; 49 | 50 | template 51 | using all = typename std::is_same< 52 | value_list, 53 | value_list(Bs), true)...>>::type; 54 | 55 | struct no_such_type { 56 | no_such_type() = delete; 57 | no_such_type(no_such_type const& /*unused*/) = delete; 58 | no_such_type(no_such_type&& /*unused*/) = delete; 59 | ~no_such_type() = delete; 60 | no_such_type& operator=(no_such_type const& /*unused*/) = delete; 61 | no_such_type operator=(no_such_type&& /*unused*/) = delete; 62 | }; 63 | 64 | namespace detail { 65 | using std::swap; 66 | 67 | template ::value and not std::is_void::value> 69 | struct is_swappable_with { 70 | template 71 | static auto test_swap(int) 72 | -> decltype(swap(std::declval(), std::declval())); 73 | template 74 | static tuples::tuples_detail::no_such_type test_swap(...); 75 | 76 | static const bool value = 77 | not std::is_same(0)), 78 | tuples::tuples_detail::no_such_type>::value and 79 | not std::is_same(0)), 80 | tuples::tuples_detail::no_such_type>::value; 81 | }; 82 | 83 | template 84 | struct is_swappable_with : std::false_type {}; 85 | } // namespace detail 86 | 87 | template 88 | using is_swappable_with = detail::is_swappable_with; 89 | 90 | template ::value> 91 | struct is_nothrow_swappable_with { 92 | private: 93 | static constexpr bool check() { 94 | using std::swap; 95 | return noexcept(swap(std::declval(), std::declval())) and 96 | noexcept(swap(std::declval(), std::declval())); 97 | } 98 | 99 | public: 100 | static const bool value = check(); 101 | }; 102 | 103 | template 104 | struct is_nothrow_swappable_with : std::false_type {}; 105 | 106 | template 107 | constexpr char swallow(Ts&&...) noexcept { 108 | return '0'; 109 | } 110 | } // namespace tuples_detail 111 | 112 | namespace tuples_detail { 113 | template ::value && 115 | !__is_final(typename Tag::type)> 116 | class tagged_tuple_leaf; 117 | 118 | template 119 | void swap(tagged_tuple_leaf& lhs, tagged_tuple_leaf& rhs) noexcept( 120 | is_nothrow_swappable_with::value) { 121 | using std::swap; 122 | swap(lhs.get(), rhs.get()); 123 | } 124 | 125 | template 126 | class tagged_tuple_leaf { 127 | using value_type = typename Tag::type; 128 | value_type value_; 129 | 130 | template 131 | static constexpr bool can_bind_reference() noexcept { 132 | using rem_ref_value_type = typename std::remove_reference::type; 133 | using rem_ref_T = typename std::remove_reference::type; 134 | using is_lvalue_type = std::integral_constant< 135 | bool, 136 | std::is_lvalue_reference::value or 137 | std::is_same, 138 | rem_ref_T>::value or 139 | std::is_same::type>, 141 | rem_ref_T>::value>; 142 | return not std::is_reference::value or 143 | (std::is_lvalue_reference::value and 144 | is_lvalue_type::value) or 145 | (std::is_rvalue_reference::value and 146 | not std::is_lvalue_reference::value); 147 | } 148 | 149 | public: 150 | // Tested in constexpr context in Unit.tagged_tuple.Ebo 151 | constexpr tagged_tuple_leaf() noexcept( 152 | std::is_nothrow_default_constructible::value) 153 | : value_() { 154 | static_assert( 155 | !std::is_reference::value, 156 | "Cannot default construct a reference element in a tagged_tuple"); 157 | } 158 | 159 | template ::type, 162 | tagged_tuple_leaf>::value && 163 | std::is_constructible::value>::type* = nullptr> 164 | constexpr explicit tagged_tuple_leaf(T&& t) noexcept( 165 | std::is_nothrow_constructible::value) 166 | : value_(tuples_detail::forward(t)) { 167 | static_assert(can_bind_reference(), 168 | "Cannot construct an lvalue reference with an rvalue"); 169 | } 170 | 171 | constexpr tagged_tuple_leaf(tagged_tuple_leaf const& /*rhs*/) = default; 172 | constexpr tagged_tuple_leaf(tagged_tuple_leaf&& /*rhs*/) = default; 173 | 174 | #if __cplusplus < 201402L 175 | value_type& get() noexcept { return value_; } 176 | #else 177 | constexpr value_type& get() noexcept { return value_; } 178 | #endif 179 | constexpr const value_type& get() const noexcept { return value_; } 180 | 181 | bool swap(tagged_tuple_leaf& t) noexcept( 182 | is_nothrow_swappable_with::value) { 183 | using std::swap; 184 | swap(*this, t); 185 | return false; 186 | } 187 | }; 188 | 189 | template 190 | class tagged_tuple_leaf : private Tag::type { 191 | using value_type = typename Tag::type; 192 | 193 | public: 194 | constexpr tagged_tuple_leaf() noexcept( 195 | std::is_nothrow_default_constructible::value) 196 | : value_type{} {} 197 | 198 | template ::type, 201 | tagged_tuple_leaf>::value && 202 | std::is_constructible::value>::type* = nullptr> 203 | constexpr explicit tagged_tuple_leaf(T&& t) noexcept( 204 | std::is_nothrow_constructible::value) 205 | : value_type(tuples_detail::forward(t)) {} 206 | 207 | constexpr tagged_tuple_leaf(tagged_tuple_leaf const& /*rhs*/) = default; 208 | constexpr tagged_tuple_leaf(tagged_tuple_leaf&& /*rhs*/) = default; 209 | 210 | #if __cplusplus < 201402L 211 | value_type& get() noexcept { return static_cast(*this); } 212 | #else 213 | constexpr value_type& get() noexcept { 214 | return static_cast(*this); 215 | } 216 | #endif 217 | 218 | constexpr const value_type& get() const noexcept { 219 | return static_cast(*this); 220 | } 221 | 222 | bool swap(tagged_tuple_leaf& t) noexcept( 223 | is_nothrow_swappable_with::value) { 224 | using std::swap; 225 | swap(*this, t); 226 | return false; 227 | } 228 | }; 229 | 230 | struct disable_constructors { 231 | static constexpr bool enable_default() noexcept { return false; } 232 | static constexpr bool enable_explicit() noexcept { return false; } 233 | static constexpr bool enable_implicit() noexcept { return false; } 234 | }; 235 | } // namespace tuples_detail 236 | 237 | /*! 238 | * \brief A compile time associative container between structs and data 239 | * 240 | * The template parameters to `tagged_tuple` are `struct`s with a member type 241 | * alias, `type` denoting the type of the data to be associated with the tag. 242 | * An example tag is: 243 | * \snippet runtime_tests.cpp example_tag_simple 244 | * A tagged_tuple is created by, for example 245 | * \snippet runtime_tests.cpp example_simple_create 246 | * Elements are retrieved using the `get` functions as follows: 247 | * \snippet runtime_tests.cpp example_get_function 248 | */ 249 | template 250 | class tagged_tuple; 251 | 252 | template 253 | constexpr const typename Tag::type& get( 254 | const tagged_tuple& t) noexcept; 255 | template 256 | constexpr typename Tag::type& get(tagged_tuple& t) noexcept; 257 | template 258 | constexpr const typename Tag::type&& get( 259 | const tagged_tuple&& t) noexcept; 260 | template 261 | constexpr typename Tag::type&& get(tagged_tuple&& t) noexcept; 262 | 263 | /*! 264 | * \brief Metafunction that returns the type of the Tag 265 | */ 266 | template 267 | using tag_type = typename Tag::type; 268 | 269 | template 270 | class tagged_tuple : private tuples_detail::tagged_tuple_leaf... { 271 | template 272 | struct pack_is_tagged_tuple : std::false_type {}; 273 | template 274 | struct pack_is_tagged_tuple 275 | : std::is_same::type, tagged_tuple> {}; 276 | 277 | template 278 | struct args_constructor : tuples_detail::disable_constructors {}; 279 | 280 | template 281 | struct args_constructor { 282 | static constexpr bool enable_default() { 283 | return tuples_detail::all< 284 | std::is_default_constructible>::value...>::value; 285 | } 286 | 287 | template 288 | static constexpr bool enable_explicit() noexcept { 289 | return tuples_detail::all< 290 | std::is_constructible, 291 | Ts>::value...>::value and 292 | not tuples_detail::all< 293 | std::is_convertible>::value...>::value; 294 | } 295 | template 296 | static constexpr bool enable_implicit() noexcept { 297 | return sizeof...(Ts) == sizeof...(Tags) and 298 | tuples_detail::all< 299 | std::is_constructible, 300 | Ts>::value...>::value and 301 | tuples_detail::all< 302 | std::is_convertible>::value...>::value; 303 | } 304 | }; 305 | 306 | template 308 | struct tuple_like_constructor : tuples_detail::disable_constructors {}; 309 | 310 | template 311 | struct tuple_like_constructor { 312 | template 313 | static constexpr bool enable_explicit() noexcept { 314 | return not tuples_detail::all< 315 | std::is_convertible>::value...>::value; 316 | } 317 | 318 | template 319 | static constexpr bool enable_implicit() noexcept { 320 | return tuples_detail::all< 321 | std::is_convertible>::value...>::value; 322 | } 323 | }; 324 | 325 | template 326 | struct tuple_like_constructor { 327 | template 328 | static constexpr bool enable_explicit() noexcept { 329 | return not tuples_detail::all< 330 | std::is_convertible>::value...>::value and 331 | (not tuples_detail::all>::value...>::value and 333 | not tuples_detail::all, Tuple>::value...>::value and 335 | not tuples_detail::all< 336 | std::is_same, Ts>::value...>::value); 337 | } 338 | 339 | template 340 | static constexpr bool enable_implicit() noexcept { 341 | return tuples_detail::all< 342 | std::is_convertible>::value...>::value and 343 | (not tuples_detail::all>::value...>::value and 345 | not tuples_detail::all, Tuple>::value...>::value and 347 | not tuples_detail::all< 348 | std::is_same, Ts>::value...>::value); 349 | } 350 | }; 351 | 352 | // C++17 Draft 23.5.3.2 Assignment - helper aliases 353 | using is_copy_assignable = 354 | tuples_detail::all>::value...>; 355 | using is_nothrow_copy_assignable = tuples_detail::all< 356 | std::is_nothrow_copy_assignable>::value...>; 357 | using is_move_assignable = 358 | tuples_detail::all>::value...>; 359 | using is_nothrow_move_assignable = tuples_detail::all< 360 | std::is_nothrow_move_assignable>::value...>; 361 | 362 | template 363 | friend constexpr const typename Tag::type& get( 364 | const tagged_tuple& t) noexcept; 365 | template 366 | friend constexpr typename Tag::type& get(tagged_tuple& t) noexcept; 367 | template 368 | friend constexpr const typename Tag::type&& get( 369 | const tagged_tuple&& t) noexcept; 370 | template 371 | friend constexpr typename Tag::type&& get( 372 | tagged_tuple&& t) noexcept; 373 | 374 | public: 375 | // C++17 Draft 23.5.3.1 Construction 376 | template ::enable_default()>::type* = nullptr> 379 | constexpr tagged_tuple() noexcept( 380 | tuples_detail::all>::value...>::value) {} 382 | 383 | constexpr tagged_tuple(tagged_tuple const& /*rhs*/) = default; 384 | constexpr tagged_tuple(tagged_tuple&& /*rhs*/) = default; 385 | 386 | template < 387 | bool Dummy = true, 388 | typename std::enable_if::template enable_explicit< 389 | tag_type const&...>()>::type* = nullptr> 390 | constexpr explicit tagged_tuple(tag_type const&... ts) noexcept( 391 | tuples_detail::all>::value...>::value) 393 | : tuples_detail::tagged_tuple_leaf(ts)... {} 394 | 395 | template < 396 | bool Dummy = true, 397 | typename std::enable_if::template enable_implicit< 398 | tag_type const&...>()>::type* = nullptr> 399 | constexpr tagged_tuple(tag_type const&... ts) noexcept( 400 | tuples_detail::all>::value...>::value) 402 | : tuples_detail::tagged_tuple_leaf(ts)... {} 403 | 404 | template ::value and 407 | sizeof...(Us) == sizeof...(Tags)>:: 408 | template enable_explicit()>::type* = nullptr> 409 | constexpr explicit tagged_tuple(Us&&... us) noexcept( 410 | tuples_detail::all, Us&&>::value...>::value) 412 | : tuples_detail::tagged_tuple_leaf( 413 | tuples_detail::forward(us))... {} 414 | 415 | template ::value and 418 | sizeof...(Us) == sizeof...(Tags)>:: 419 | template enable_implicit()>::type* = nullptr> 420 | constexpr tagged_tuple(Us&&... us) noexcept( 421 | tuples_detail::all, Us&&>::value...>::value) 423 | : tuples_detail::tagged_tuple_leaf( 424 | tuples_detail::forward(us))... {} 425 | 426 | template < 427 | class... UTags, 428 | typename std::enable_if< 429 | tuple_like_constructor< 430 | sizeof...(Tags) == sizeof...(UTags) and 431 | tuples_detail::all, tag_type const&>::value...>::value>:: 433 | template enable_explicit const&, 434 | tag_type...>()>::type* = nullptr> 435 | constexpr explicit tagged_tuple(tagged_tuple const& t) noexcept( 436 | tuples_detail::all, tag_type const&>::value...>::value) 438 | : tuples_detail::tagged_tuple_leaf(get(t))... {} 439 | 440 | template < 441 | class... UTags, 442 | typename std::enable_if< 443 | tuple_like_constructor< 444 | sizeof...(Tags) == sizeof...(UTags) and 445 | tuples_detail::all, tag_type const&>::value...>::value>:: 447 | template enable_implicit const&, 448 | tag_type...>()>::type* = nullptr> 449 | constexpr tagged_tuple(tagged_tuple const& t) noexcept( 450 | tuples_detail::all, tag_type const&>::value...>::value) 452 | : tuples_detail::tagged_tuple_leaf(get(t))... {} 453 | 454 | template < 455 | class... UTags, 456 | typename std::enable_if< 457 | tuple_like_constructor< 458 | sizeof...(Tags) == sizeof...(UTags) and 459 | tuples_detail::all, tag_type&&>::value...>::value>:: 461 | template enable_explicit&&, 462 | tag_type...>()>::type* = nullptr> 463 | constexpr explicit tagged_tuple(tagged_tuple&& t) noexcept( 464 | tuples_detail::all, tag_type&&>::value...>::value) 466 | : tuples_detail::tagged_tuple_leaf( 467 | tuples_detail::forward>(get(t)))... {} 468 | 469 | template < 470 | class... UTags, 471 | typename std::enable_if< 472 | tuple_like_constructor< 473 | sizeof...(Tags) == sizeof...(UTags) and 474 | tuples_detail::all, tag_type&&>::value...>::value>:: 476 | template enable_implicit&&, 477 | tag_type...>()>::type* = nullptr> 478 | constexpr tagged_tuple(tagged_tuple&& t) noexcept( 479 | tuples_detail::all, tag_type&&>::value...>::value) 481 | : tuples_detail::tagged_tuple_leaf( 482 | tuples_detail::forward>(get(t)))... {} 483 | 484 | // C++17 Draft 23.5.3.2 Assignment 485 | tagged_tuple& operator=( 486 | typename std::conditional::type const& 488 | t) noexcept(is_nothrow_copy_assignable::value) { 489 | static_cast( 490 | tuples_detail::swallow((get(*this) = get(t))...)); 491 | return *this; 492 | } 493 | 494 | tagged_tuple& operator=( 495 | typename std::conditional::type&& 497 | t) noexcept(is_nothrow_move_assignable::value) { 498 | static_cast(tuples_detail::swallow( 499 | (get(*this) = 500 | tuples_detail::forward>(get(t)))...)); 501 | return *this; 502 | } 503 | 504 | template &, 509 | tag_type const&>::value...>::value>::type* = nullptr> 510 | tagged_tuple& operator=(tagged_tuple const& t) noexcept( 511 | tuples_detail::all&, tag_type const&>::value...>::value) { 513 | static_cast( 514 | tuples_detail::swallow((get(*this) = get(t))...)); 515 | return *this; 516 | } 517 | 518 | template < 519 | class... UTags, 520 | typename std::enable_if< 521 | sizeof...(Tags) == sizeof...(UTags) and 522 | tuples_detail::all&, tag_type&&>::value...>::value>::type* = 524 | nullptr> 525 | tagged_tuple& operator=(tagged_tuple&& t) noexcept( 526 | tuples_detail::all&, tag_type&&>::value...>::value) { 528 | static_cast(tuples_detail::swallow( 529 | (get(*this) = 530 | tuples_detail::forward>(get(t)))...)); 531 | return *this; 532 | } 533 | 534 | // C++17 Draft 23.5.3.3 swap 535 | void swap(tagged_tuple& t) noexcept( 536 | tuples_detail::all, 538 | tuples_detail::tagged_tuple_leaf>::value...>::value) { 539 | tuples_detail::swallow(tuples_detail::tagged_tuple_leaf::swap( 540 | static_cast&>(t))...); 541 | } 542 | }; 543 | 544 | /// \cond 545 | template <> 546 | class tagged_tuple<> { 547 | public: 548 | tagged_tuple() noexcept {} 549 | void swap(tagged_tuple& /*unused*/) noexcept {} 550 | }; 551 | /// \endcond 552 | 553 | // C++17 Draft 23.5.3.6 Tuple helper classes 554 | /*! 555 | * \brief Has a member variable `value` equal to the number of elements in a 556 | * tagged_tuple 557 | */ 558 | template 559 | struct tuple_size; 560 | 561 | /// \cond 562 | template 563 | struct tuple_size> 564 | : std::integral_constant {}; 565 | template 566 | struct tuple_size> 567 | : tuple_size> {}; 568 | template 569 | struct tuple_size> 570 | : tuple_size> {}; 571 | template 572 | struct tuple_size> 573 | : tuple_size> {}; 574 | /// \endcond 575 | 576 | // C++17 Draft 23.5.3.7 Element access 577 | // @{ 578 | /*! 579 | * \brief Retrieve the element with tag `Tag` from the tagged_tuple `t` 580 | */ 581 | template 582 | inline constexpr const typename Tag::type& get( 583 | const tagged_tuple& t) noexcept { 584 | return static_cast&>(t).get(); 585 | } 586 | template 587 | inline constexpr typename Tag::type& get(tagged_tuple& t) noexcept { 588 | return static_cast&>(t).get(); 589 | } 590 | template 591 | inline constexpr const typename Tag::type&& get( 592 | const tagged_tuple&& t) noexcept { 593 | return static_cast( 594 | static_cast&&>(t).get()); 595 | } 596 | template 597 | inline constexpr typename Tag::type&& get(tagged_tuple&& t) noexcept { 598 | return static_cast( 599 | static_cast&&>(t).get()); 600 | } 601 | // @} 602 | 603 | // C++17 Draft 23.5.3.8 Relational operators 604 | namespace tuples_detail { 605 | struct equal { 606 | template 607 | static TUPLES_LIB_CONSTEXPR_CXX_14 void apply( 608 | T const& lhs, U const& rhs, bool& result) noexcept(noexcept(lhs == rhs)) { 609 | result = result and lhs == rhs; 610 | } 611 | }; 612 | 613 | template 614 | TUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_equal_impl( 615 | tagged_tuple const& lhs, 616 | tagged_tuple const& 617 | rhs) noexcept(noexcept(std::initializer_list{ 618 | (get(lhs) == get(rhs))...})) { 619 | bool equal = true; 620 | // This short circuits in the sense that the operator== is only evaluated if 621 | // the result thus far is true 622 | static_cast(std::initializer_list{ 623 | (equal::apply(get(lhs), get(rhs), equal), '0')...}); 624 | return equal; 625 | } 626 | } // namespace tuples_detail 627 | 628 | template ::type* = 630 | nullptr> 631 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator==( 632 | tagged_tuple const& lhs, 633 | tagged_tuple const& 634 | rhs) noexcept(noexcept(tuples_detail::tuple_equal_impl(lhs, rhs))) { 635 | return tuples_detail::tuple_equal_impl(lhs, rhs); 636 | } 637 | 638 | template ::type* = 640 | nullptr> 641 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator!=( 642 | tagged_tuple const& lhs, 643 | tagged_tuple const& rhs) noexcept(noexcept(lhs == rhs)) { 644 | return not(lhs == rhs); 645 | } 646 | 647 | namespace tuples_detail { 648 | struct less { 649 | template 650 | static TUPLES_LIB_CONSTEXPR_CXX_14 void apply( 651 | T const& lhs, U const& rhs, bool& last_rhs_less_lhs, 652 | bool& result) noexcept(noexcept(lhs < rhs) and noexcept(rhs < lhs)) { 653 | if (result or last_rhs_less_lhs) { 654 | return; 655 | } 656 | result = lhs < rhs; 657 | if (result) { 658 | return; 659 | } 660 | last_rhs_less_lhs = rhs < lhs; 661 | } 662 | }; 663 | 664 | template 665 | TUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_less_impl( 666 | tagged_tuple const& lhs, 667 | tagged_tuple const& 668 | rhs) noexcept(noexcept(std::initializer_list{ 669 | (get(lhs) < get(rhs))...})) { 670 | bool result = false; 671 | bool last_rhs_less_lhs = false; 672 | static_cast(std::initializer_list{ 673 | (less::apply(get(lhs), get(rhs), last_rhs_less_lhs, result), 674 | '0')...}); 675 | return result; 676 | } 677 | } // namespace tuples_detail 678 | 679 | template ::type* = 681 | nullptr> 682 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator<( 683 | tagged_tuple const& lhs, 684 | tagged_tuple const& 685 | rhs) noexcept(noexcept(tuples_detail::tuple_less_impl(lhs, rhs))) { 686 | return tuples_detail::tuple_less_impl(lhs, rhs); 687 | } 688 | 689 | template ::type* = 691 | nullptr> 692 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator>( 693 | tagged_tuple const& lhs, 694 | tagged_tuple const& rhs) noexcept(noexcept(rhs < lhs)) { 695 | return rhs < lhs; 696 | } 697 | 698 | template ::type* = 700 | nullptr> 701 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator<=( 702 | tagged_tuple const& lhs, 703 | tagged_tuple const& rhs) noexcept(noexcept(rhs < lhs)) { 704 | return not(rhs < lhs); 705 | } 706 | 707 | template ::type* = 709 | nullptr> 710 | TUPLES_LIB_CONSTEXPR_CXX_14 bool operator>=( 711 | tagged_tuple const& lhs, 712 | tagged_tuple const& rhs) noexcept(noexcept(lhs < rhs)) { 713 | return not(lhs < rhs); 714 | } 715 | 716 | // C++17 Draft 23.5.3.3 swap 717 | template < 718 | class... Tags, 719 | typename std::enable_if, 721 | tuples_detail::tagged_tuple_leaf>::value...>::value>::type* = 722 | nullptr> 723 | void swap(tagged_tuple& lhs, tagged_tuple& rhs) noexcept( 724 | tuples_detail::all, 726 | tuples_detail::tagged_tuple_leaf>::value...>::value) { 727 | lhs.swap(rhs); 728 | } 729 | 730 | namespace tuples_detail { 731 | template > 732 | struct is_streamable : std::false_type {}; 733 | template 734 | struct is_streamable< 735 | S, T, 736 | void_t::type>() 737 | << std::declval()), 738 | typename std::enable_if::value>::type>> 739 | : std::true_type {}; 740 | 741 | template 742 | using is_streamable_t = typename is_streamable::type; 743 | 744 | struct stream_helper { 745 | template 746 | void operator()(std::ostream& os, const T& element, size_t& current_value, 747 | const size_t num_tags, const std::true_type /*meta*/) { 748 | os << element; 749 | current_value++; 750 | if (current_value < num_tags) { 751 | os << std::string(", "); 752 | } 753 | } 754 | 755 | template 756 | void operator()(std::ostream& os, const T& /*element*/, size_t& current_value, 757 | const size_t num_tags, const std::false_type /*meta*/) { 758 | os << std::string("NOT STREAMABLE"); 759 | current_value++; 760 | if (current_value < num_tags) { 761 | os << std::string(", "); 762 | } 763 | } 764 | }; 765 | } // namespace tuples_detail 766 | 767 | template 768 | std::ostream& operator<<(std::ostream& os, const tagged_tuple& t) { 769 | os << std::string("("); 770 | size_t current_value = 0; 771 | tuples_detail::stream_helper helper; 772 | // With empty tagged_tuple's helper is unused 773 | static_cast(helper); 774 | static_cast(std::initializer_list{ 775 | (helper(os, get(t), current_value, sizeof...(Tags), 776 | tuples_detail::is_streamable_t>{}), 777 | '0')...}); 778 | return os << std::string(")"); 779 | } 780 | 781 | } // namespace tuples 782 | -------------------------------------------------------------------------------- /Solutions/Lesson6/ReferenceVsMove.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | class BigScalar { 7 | public: 8 | std::vector data; // data for a scalar field 9 | 10 | BigScalar(std::vector& init_vector) noexcept { 11 | init_vector[4] = 4.4444; 12 | // vector = std::move(init_vector); // surprise! init_vector destroyed 13 | data = init_vector; 14 | } 15 | // BigScalar(std::vector& init_vector) { 16 | // init_vector[4] = 4.4444; 17 | // vector = init_vector; 18 | // } 19 | BigScalar(std::vector&& init_data) noexcept { 20 | init_data[4] = 4.4444; 21 | data = std::move(init_data); 22 | } 23 | }; 24 | 25 | BigScalar init_big_scalar(const size_t size) noexcept { 26 | std::vector test_vector{}; 27 | test_vector.resize(size); 28 | BigScalar store{std::move(test_vector)}; 29 | // BigScalar store{test_vector}; 30 | // std::cout << test_vector[4] << "\n"; // fails if after 31 | // std::move(test_vector) 32 | return store; 33 | } 34 | 35 | void multiply_by_constant(gsl::not_null scalar, 36 | const double factor) noexcept { 37 | for (auto it = scalar->data.begin(); it != scalar->data.end(); ++it) { 38 | *it *= factor; 39 | } 40 | } 41 | 42 | int main() { 43 | constexpr size_t size = 1000000000; 44 | auto scalar = init_big_scalar(size); 45 | std::cout << scalar.data[4] << "\n"; 46 | 47 | multiply_by_constant(gsl::make_not_null(&scalar), 10.0); 48 | std::cout << scalar.data[4] << "\n"; 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /Solutions/Lesson6/ScaleBigScalar.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class ScalarField { 10 | public: 11 | std::vector x{}; 12 | size_t size = 0; 13 | 14 | // constructor 15 | ScalarField(const size_t& initial_size) noexcept { 16 | // Resize the vectors, which allocates enough memory to store them all 17 | x.resize(size); 18 | size = initial_size; 19 | } 20 | 21 | void resize(const size_t& new_size) noexcept { 22 | x.resize(new_size); 23 | size = new_size; 24 | } 25 | 26 | // Copy initialize 27 | // ScalarField(const ScalarField& spatial_vector) noexcept = default; 28 | ScalarField(const ScalarField& spatial_vector) noexcept = delete; 29 | 30 | // Copy assign 31 | // ScalarField& operator=(const ScalarField& spatial_vector) noexcept = default; 32 | ScalarField& operator=(const ScalarField& spatial_vector) noexcept = delete; 33 | 34 | // Move initialize 35 | ScalarField(ScalarField&& spatial_vector) noexcept = default; 36 | 37 | // Move assign 38 | ScalarField& operator=(ScalarField&& spatial_vector) noexcept = default; 39 | 40 | // Destructor 41 | ~ScalarField() = default; 42 | }; 43 | 44 | void initialize_scalar_field(gsl::not_null vector, 45 | const size_t size, 46 | const double initial_value) noexcept { 47 | vector->resize(size); 48 | std::iota(vector->x.begin(), vector->x.end(), initial_value); 49 | } 50 | 51 | ScalarField initialize_scalar_field(const size_t size, 52 | const double initial_value) noexcept { 53 | ScalarField result(size); 54 | initialize_scalar_field(gsl::make_not_null(&result), size, initial_value); 55 | return result; 56 | } 57 | 58 | void scale_scalar_field(gsl::not_null lhs, 59 | const double scale_factor) noexcept { 60 | for (auto it = lhs->x.begin(); it != lhs->x.end(); ++it) { 61 | *it *= scale_factor; 62 | } 63 | } 64 | 65 | // ScalarField scale_scalar_field(const ScalarField& field, 66 | // const double scale_factor) noexcept { 67 | // ScalarField result = field; 68 | // scale_scalar_field(gsl::make_not_null(&result), scale_factor); 69 | // return result; 70 | // } 71 | 72 | ScalarField scale_scalar_field(ScalarField&& field, 73 | const double scale_factor) noexcept { 74 | for (auto it = field.x.begin(); it != field.x.end(); ++it) { 75 | *it *= scale_factor; 76 | } 77 | return std::move(field); 78 | } 79 | 80 | int main() { 81 | auto start = std::chrono::high_resolution_clock::now(); 82 | auto stop = std::chrono::high_resolution_clock::now(); 83 | 84 | constexpr size_t size = 1000000000; 85 | constexpr double initial_value = 0.0; 86 | constexpr double scale_factor = 10.0; 87 | 88 | std::cout << "Creating a scalar field of size " << size << "\n"; 89 | start = std::chrono::high_resolution_clock::now(); 90 | 91 | ScalarField field_a = initialize_scalar_field(size, initial_value); 92 | 93 | stop = std::chrono::high_resolution_clock::now(); 94 | std::cout << gsl::at(field_a.x, size / 2) << " (" 95 | << std::chrono::duration_cast(stop - 96 | start) 97 | .count() 98 | << " microsec)\n"; 99 | 100 | // std::cout << "Scaling a scalar field of size " << size << " by " 101 | // << scale_factor << " (by value)\n"; 102 | // start = std::chrono::high_resolution_clock::now(); 103 | // 104 | // ScalarField field_b = scale_scalar_field(field_a, scale_factor); 105 | // 106 | // stop = std::chrono::high_resolution_clock::now(); 107 | // std::cout << gsl::at(field_b.x, size / 2) << " (" 108 | // << std::chrono::duration_cast(stop - 109 | // start) 110 | // .count() 111 | // << " microsec)\n"; 112 | 113 | std::cout << "Scaling a scalar field of size " << size << " by " 114 | << scale_factor << " (by not_null)\n"; 115 | start = std::chrono::high_resolution_clock::now(); 116 | 117 | scale_scalar_field(gsl::make_not_null(&field_a), scale_factor); 118 | 119 | stop = std::chrono::high_resolution_clock::now(); 120 | std::cout << gsl::at(field_a.x, size / 2) << " (" 121 | << std::chrono::duration_cast(stop - 122 | start) 123 | .count() 124 | << " microsec)\n"; 125 | 126 | std::cout << "Scaling a scalar field of size " << size << " by " 127 | << scale_factor << " (by std::move)\n"; 128 | start = std::chrono::high_resolution_clock::now(); 129 | 130 | ScalarField field_c{ 131 | std::move(scale_scalar_field(std::move(field_a), scale_factor))}; 132 | 133 | stop = std::chrono::high_resolution_clock::now(); 134 | std::cout << gsl::at(field_c.x, size / 2) << " (" 135 | << std::chrono::duration_cast(stop - 136 | start) 137 | .count() 138 | << " microsec)\n"; 139 | 140 | return 0; 141 | } 142 | --------------------------------------------------------------------------------