Presentation is loading. Please wait.

Presentation is loading. Please wait.

Maths and Technologies for Games Modern C++ Techniques C++11 and Beyond CO3303 Week 21.

Similar presentations


Presentation on theme: "Maths and Technologies for Games Modern C++ Techniques C++11 and Beyond CO3303 Week 21."— Presentation transcript:

1 Maths and Technologies for Games Modern C++ Techniques C++11 and Beyond
CO3303 Week 21

2 Today’s Lecture C++ History and Standards C++ Compilers and Standards
C++11 Feature Summary Main C++11 Features

3 C++ History C++ originated as “C with classes” in 1979
Developed by Bjarne Stroustrup Same intent as it has today, a high-performance language with high-level features (initially classes, inheritance and strong typing) Renamed to “C++” in 1983 First commercial compiler and reference work in 1985 C in 1990 Multiple inheritance, static/const/protected members, templates, exceptions and more Eventually standardised by the ISO in 1998 (C++98) The next major revision was C++11, in 2011

4 C++ Standards There have been four versions of the standard:
The basis of most C++ code even today 2003: C++03 Addressed defects in the original standard text, no C++ changes 2011: C++11 A significant number of new features added. A major version. 2014: C++14 A few minor extensions and fixes to the features added in C++11 20??: C++17 Being worked on. So far not much content, but expected to grow.

5 C++ Compilers In practice, our code must target the flavour of C++ that our compiler supports, not the latest standards Standards are only relevant if the major compilers support them Until recent years, Visual Studio has lagged behind in adopting standards However, Microsoft has moved to a more rapid release cycle and now all major compilers are adopting new features quickly Perhaps the four most significant compilers are: MSVC (Visual Studio), GCC, Clang and Intel C++ All now support the bulk of C++11 MSVC is still behind (constexpr support), but not too far C++14 / 17 support is also rapidly becoming available

6 “Modern” C++ With new C++ features now widely available, developers should begin to become familiar with them C++ has in many ways become a “new” language Some new features are extremely powerful and their use potentially very complex People are still discovering how this “new” C++ can be used It certainly is more customisable than ever before. To truly understand every detail requires even more expertise. Whilst many appreciate the developments, some argue against the increasing complexity of the language. Yet C++ adheres to “you don’t pay for what you don’t use”, so this complexity is not necessarily a problem.

7 C++11 New Features Summary
Not an exhaustive list, but a summary of the essentials: Types: auto, nullptr, new enums, new style functions, decltype Simplified initialisation, member defaults Range-based for loop Parameters & return values: rvalues and move semantics Constructor delegation and deletion, “final” virtual functions Standard library: smart pointers, hash-tables, regex and much more Lambda expressions: (anonymous functions, Javascript-like) Constant expressions: code executed by the compiler Template extensions such as variadic templates Extensive support for multithreading For more details see: Bjarne Stroustrup’s C++11 FAQ Wikipedia’s C++11 feature list

8 Type-related Features
auto, automatic type deduction: auto i = 0; // a is int auto f = 0.0f; // f is a float auto it = myVector.begin(); // “it” is the appropriate iterator // Compare the above with the C++98 version: vector<MyClass>::iterator it = myVector.begin(); decltype, type of a given expression: int a = 5; float b = 12.5; decltype(a*b) r = a*b; // r is the type you get when you multiply auto would do the same, so is this useful? We will return to it…

9 Type-related Features
nullptr, replacement for NULL or 0: int* p = 0; // Pointer to nothing, C++98 style int* p = nullptr; // C++11 version. Won’t be mixed up with int “Class” enum enum class Button // New optional word “class” { Start, Stop, Pause }; Button FirstButton = Button::Start; // Must write Button:: int Start = 0; // So this is not an error (would be in C++98) Typed enum, select the integer type you want to use enum Flag : char // Add the exact type you want to use after : { Debug, NoWarnings, Strict };

10 Type-related Features
New function style: int Add(int a, int b) // Ordinary style (still available) auto Add(int a, int b) -> int // New style Why…? Because this kind of thing is impossible in C++98: // Multiply any two things together template<class T, class U> ??? mul(T x, U y) { return x*y; } // What is the return type? Use new function style and decltype auto mul(T x, U y) -> decltype(x*y) { return x*y; } Very useful for powerful template code

11 Uniform Initialisation
Curly bracketed lists can be used to initialise most things struct Point{ int x; int y; }; Point A = {1,2}; Point B{1,2}; // Same meaning, prefer {} instead of () in C++11 Point* C = new Point{10,20}; void Draw( Point p ) {…} Draw({45,15}); #include <initializer_list> // Needed for STL {} initialiser support int squares[4] = {1,4,9,16}; // Ordinary array, C++98 OK std::list<int> grades = { 41, 56, 65, 51, 62, 78 }; // Now STL also using namespace std; // Should use std::, but this slide is small! map<string, vector<string>> telephoneNumbers = { {"Bob", {" ", " "} }, {"Ann", {" "} }, };

12 Class Defaults and Using
Can provide default values for class members As an alternative to setting them in the constructor class BankAccount { public: BankAccount() {} std::string mType = "Current"; float mBalance = 0.0f; float mOverdraft = f; }; BankAccount account1; // All members set to defaults More readable version of typedef Can make template definitions this way too using IntIter = std::vector<int>::iterator; IntIter it = myVec.begin();

13 Range-based for Loop for loop over a container, C++98 style:
vector<int> scores; vector<int>::iterator it; for (it = scores.begin(); it != scores.end(); ++it) cout << *it; Range-based loop: for (value : scores) cout << value; // No iterator used at all, get value directly Works for anything that has begin() and end() E.g. string, istream, STL containers, your own types etc. for (auto x : {1,2,3,5,8}) // Using auto and initializer_list cout << x;

14 Constructor Delegation and Deletion
Constructors can call other constructors: class Widget { Widget(int area) {…} Widget(int width, int height) : Widget(width * height); } Constructors can be deleted or defaulted: class Widget { Widget() = default; // Make a default constructor Widget(Widget&) = delete; // Delete copy constructor, can’t make copies

15 “Final” virtual functions
Virtual functions can be specified as final, which means that inherited classes cannot override them class IModel // Interface class { virtual void Render(); } class Model : public IModel // Inherited implementation class { virtual void Render() final {…}; // Implement the interface function } class SpecialModel : public Model // Further inheritance { virtual void Render() {…}; // Error, can’t override final version } Make sure there is a good reason to do this.

16 New Standard Library Features
Many new features in the C++11 standard library For example: Hash maps: <unordered_set>, <unordered_map> etc. Linked lists: <forward_list> Replacement for built-in array, <array>: std::array<int,5> numbers = {1,2,3,4,5}; numbers[2] = 5; Arbitrary collection of different types, <tuple> std::tuple<int, string, float> things = {5, "Hello", 1.5f}; cout << std::get<1>(things); // Outputs "Hello" tuple is an example of a new feature, variadic templates, that allows any number of template parameters. In this case it allows tuples to contain any number of types

17 New Standard Library Features
More examples: Generic function pointers <functional>: int multiply (int a, int b) { return a*b; } std::function<int(int,int)> myFn = multiply; int r = multiply(5,6); Powerful (advanced) random number library <random> Timer libraries <system_clock>, <monotonic_clock>, <high_resolution_clock> Regular expressions (complex string matching) <regex>

18 Smart Pointers Smart pointers are a new essential feature of the STL
Defined in the include file <memory> The C++98 approach: void Parse(File textFile, int size) { Dictionary* d = new Dictionary(size); d->read(); //... etc. delete d; // If you forget this -> memory leak } Same operation in C++11: void Parse(File textFile , int size) { std::unique_ptr<Dictionary> d(new Dictionary(size)); d->read(); … } // Automatically deleted when it goes out of scope

19 Smart Pointers The syntax can be simplified:
void Parse(File textFile, int size) { auto d = std::make_unique<Dictionary>(size); // "forwards" parameters to ctor d->read(); } // d automatically deleted (strictly make_unique is C++14, it was left out of C++11 by accident!) unique_ptr is for objects that have a single “owner” Copying unique_ptr transfers ownership, must use move semantics: std::unique_ptr<int> a(new int); // Dynamically allocate int *a = 5; std::unique_ptr<int> b = std::move(a); // b points at the int, a is nullptr *b = 6; *a = 7; // error

20 Smart Pointers If have a class or function that needs access to the contents of your pointer, but you don’t want to transfer ownership, then just use a “raw” pointer: bool Validate(const Dictionary* d) {…} // Want to use the dictionary, not own it std::unique_ptr<Dictionary> d(new Dictionary(size)); if (!Validate( d.get() )) // get() function gives you a raw pointer {…} It is the programmer’s responsibility to check object lifetimes in their program in cases like this. If the unique pointer is destroyed, the raw pointer will be invalid. However, cases like the one illustrated above are common. One object will own the unique_ptr, other objects and functions will use it through raw pointers. Aim to have all your classes use unique_ptrs for ownership. You should almost never need to write new and delete again!

21 Smart Pointers Sometimes, multiple objects share a pointer, and the object should only be deleted when all the owners are deleted. E.g. the mesh of a game object that has many entities using it. Only destroy the mesh when all the entities have gone. In such cases, you might use shared_ptr Has an internal reference count to decide when to delete itself Basic usage is the same as unique_ptr (use make_shared) (Note: there is a helper weak_ptr to avoid cycles of shared_ptrs) When passing / returning shared_ptr you have a choice: By value, internal count is increased By reference, internal count is not increased However, shared_ptr should be used rarely In the above case, a mesh manager could hold a unique_ptr instead

22 Lambda Expressions Lambda expressions are functions declared “inline”
// Some 2D points struct Point {int x; int y;}; std::vector<Point> pts = {{5,1}, {1,2}, {3,7}, {10,-1}}; // Sort function, pass vector of points and a less-than function for the sorting void Sort(std::vector<Point>& data, std::function<bool(Point,Point)> lessThan) { if (!lessThan(data[0], data[1])) std::swap(data[0], data[1]); //... } // Using the Sort function with lambdas // [] starts the lambda, then the parameter list, then the function code // These lambda functions are passed to the 2nd parameter of Sort above Sort(pts, [](Point a, Point b){ return a.x < b.x; }); // Sort points on x value Sort(pts, [](Point a, Point b){ return a.y < b.y; }); // Sort points on y value // Sort points on Manhattan distance from origin Sort(pts, [](Point a, Point b){ return a.x + a.y < b.x + b.y; });

23 Lambda Expressions Lambdas can “capture” variables from the outer scope Very useful feature, but be clear how it works: auto x = 5; // Variables must be in scope when we declare lambdas using them // Store the lambdas in variables in this example auto inc_x = [&](){ x++; }; // Increase (by reference) variable called x inc_x(); auto x_plus_5 = [=](){ return x+5; }; // Return 5 + copy of x taken on this line cout << x << x_plus_5(); // outputs 7, 11 (because x_plus_5 captured x at 6) The capture is specified in the [] [] Capture nothing [&] Capture any variables by reference [=] Capture any variable by making a copy [=, &v] Capture any variable by copy, but v by reference [this] Capture the this pointer of the enclosing class

24 Lambda Expressions Lambdas are very useful with STL <algorithm> functions Similar code style to several Javascript libraries // Output container of integers std::for_each(v.begin(), v.end(), [] (int i) { cout << i; }); // Remove odd numbers from container std::remove_if(v.begin(), v.end(), [] (int i) return ((i%2)==1); // Find first rectangle in container with area > 100 auto x = std::find_if(v.begin(), v.end(), [] (Rect r) return r.w * r.h > 100;

25 Constant Expressions Functions that are declared constexpr will be evaluated by the compiler where possible: constexpr int multiply(int x, int y) { return x * y; } const int val1 = multiply(10, 10); // Function is executed by compiler int a, b; cin >> a >> b; int val2 = multiply(a, b); // Function is executed at runtime There are many limitations on the kinds of functions that can be executed at compile time. In particular they must be only a single return statement Use constexpr for compile-time tests or to replace macros: constexpr int GetDefaultArraySize(int multiplier) { return 10 * multiplier; } int myArray[ GetDefaultArraySize(3) ];

26 Constant Expressions Simple objects can also be constructed using constexpr class Circle { public: constexpr Circle (int x, int y, int radius) : mX(x), mY(y), mRadius(radius) {} constexpr double getArea() { return mRadius * mRadius * ; } private: int mX; int mY; int mRadius; }; constexpr Circle c( 0, 0, 10 ); // Object constructed by compiler constexpr double area = c.getArea(); // Area calculated by compiler Pixel circlePixels[int(area)]; // Fixed size array based on calculation

27 Multithreading Support
Threading support is provided in the standard library Simple example: #include <thread> void threadStart() { // Do something in first thread } int main() { std::thread t1(threadStart); // Start first thread at given function std::thread t2([](){ cout << "I am a thread"; }); // Lambda second thread // Do something in main thread t1.detatch(); // Let 1st thread run on its own (don’t wait for it) t2.join(); // Wait for the second thread to finish }

28 Multithreading Support
Full range of locking features is available std::mutex funcLock; void func() { funcLock.lock(); // Do something that needs synchronisation funcLock.unlock(); // Also std::lock_guard, which auto-unlocks on destruction } std::thread t1(func); std::thread t2(func); // Threads t1 and t2 cannot run the func code at the same time due to the mutex t1.join(); t2.join();

29 Rvalue References Broadly speaking, an rvalue is an expression that is used temporarily and does not persist onto the next line. int square(int x) { return x*x; } int a = square(12); // square(12) is an rvalue, 12 is also an rvalue For example, you cannot assign into rvalues 12 = a; // Error square(12) = 143; // Error In C++98, rvalue references are not allowed void increment(int& x) { x++; } // Reference parameter int a = 5; increment(a); // OK, a becomes 6 increment(3); // Error, incrementing a literal value??? increment(square(12)); // Error, incrementing the return value???

30 Rvalue References In C++11, rvalue references are supported
void increment(int&& x) { x++; } // Two &’s for an rvalue reference increment(3); // Makes temp variable that is increased (then discarded) increment(square(12)); // Same, makes a temporary variable Of course this is useless and not the intent. This is just an illustration that rvalue references capture temporary values. However constructing objects with rvalues is very useful class BigThing { // “Move” constructor. “x” is a rvalue reference so we know that it is a // temporary variable and we can steal its resources, no need to copy anything BigThing( BigThing&& x ); }; This “move” constructor can save on copies where temporaries occur So when is this useful? Very often it turns out…

31 Rvalue References Consider this typical operation in C++98:
template<class T> swap(T& a, T& b) { T temp(a); // Makes a complete copy of a (copy constructor) a = b; // Copies entire content of b to a (assignment) b = temp; // Copies entire content of tmp to b } Three copy operations are needed, possibly expensive But no new data was actually needed… Same operation in C++11 (shown in detail on the next slide): template<class T> swap(T& a, T& b) { T temp(std::move(a)); // std::move converts “a” into a temporary… a = std::move(b); // If the type T has a move constructor… b = std::move(temp); // …then no copying occurs at all }

32 Rvalue references In more detail…
template<class T> swap(T& a, T& b) { a is converted to a temporary, which means it doesn’t need to keep its data. So the type T can have a move constructor that simply steals the data of a. For example, if a contains an array of 1000 integers, the new variable temp just points at that array as its own, knowing that a doesn’t need it anymore. No copy needed. T temp(std::move(a)); a still exists as a variable though, and now we assign it to b. This time a move assignment operator is used: same idea: a just steals b’s data. a = std::move(b); Same again, b steals the data back from temp. Process complete and no copying occurred at all. b = std::move(temp); } This process requires us to write move constructors and move assignment operators. They must intelligently copy pointers, and not the actual data (in fact, this is a deliberate shallow copy).

33 Rvalue References Usage
Rvalue references remove unnecessary copying The STL contains move constructors and move assignment operators wherever possible. Algorithms within the STL use “move semantics” if they can For example vector::push_back can take an rvalue parameter, so myVector.push_back(BigThing()); will not copy the BigThing, it will construct directly into the vector That means that many of the benefits of rvalue references come simply by using the STL Write move constructors & assignments for expensive-to-construct objects that you expect to pass around One by-product is that passing or returning by value is no longer inefficient if you are using classes that use move semantics

34 Finally Presented the basics of most C++11 new features
Too much to cover in one lecture, so do explore the earlier links Support for these features is here now, so start using them! Quick list of interesting topics not covered: Variadic templates, <type_traits> Template programming is now incredibly powerful. Can effectively be used to extend the language. Research “Tempate Metaprogramming” Guru level difficulty. Use sparingly for core libraries only. Alignment of variables: alignof, alignas static_assert. User-errors at compile time, good with constexpr New literal types, e.g. Unicode literals, user-defined literal styles long long int type, guaranteed to be 64-bit


Download ppt "Maths and Technologies for Games Modern C++ Techniques C++11 and Beyond CO3303 Week 21."

Similar presentations


Ads by Google