Difference between revisions of "Intermediate C++ Game Programming Tutorial 25"
(→Video Timestamp Index) |
(→Tutorial 25 - Solution) |
||
(145 intermediate revisions by the same user not shown) | |||
Line 14: | Line 14: | ||
== Video Timestamp Index == | == Video Timestamp Index == | ||
− | [https://www.youtube.com/watch?v=S2ZEEkquGy4 Tutorial 25] | + | === [https://www.youtube.com/watch?v=S2ZEEkquGy4 Tutorial 25 - Function Pointers] === |
− | * | + | <div class="mw-collapsible mw-collapsed"><br /> |
− | ** | + | * How to switch on a string? (execute some code based on the string passed to the switch): using Functionoids [https://youtu.be/S2ZEEkquGy4?t=15s 0:15] |
− | ::<syntaxhighlight lang=" | + | <div class="mw-collapsible-content"> |
+ | :* One way to do this is with a map to polymorphic functors | ||
+ | :* The Functionoid Pattern (objects that encapsulate function pointers) looks like this: | ||
+ | ::<syntaxhighlight lang="python" line> | ||
+ | #include <iostream> | ||
+ | #include <memory> | ||
+ | #include <unordered_map> | ||
+ | #include <string> | ||
+ | |||
struct CaseFunction | struct CaseFunction | ||
{ | { | ||
Line 29: | Line 37: | ||
} | } | ||
}; | }; | ||
+ | |||
int main() | int main() | ||
{ | { | ||
+ | std::unordered_map<std::string,std::unique_ptr<CaseFunction>> sw; | ||
+ | sw["sixty-nine"] = std::make_unique<SixtyNine>(); | ||
+ | (*sw["sixty-nine"])(); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :* But Chili is not a fan of this: | ||
+ | ::- awkward syntax to invoke the switch, | ||
+ | ::- need to revert to unique pointer protection | ||
+ | ::- forced to put data on the heap | ||
+ | ::- need to code derived classes for new functors | ||
+ | ::- function definitions are seperated from the point in the code where we map them | ||
+ | ::+ On the plus side: provides good type safety | ||
+ | </div> | ||
+ | * Introducing Function Pointers [https://youtu.be/S2ZEEkquGy4?t=5m13s 5:13] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Idea is similar to pointing to data (stores the address of the data in memory | ||
+ | ::- Pointer to function stores the address to the first instruction of a function in memory | ||
+ | ::- Functions are just a collection of instruction bytes in memory. If you jump to the memory address of the first instruction, you essentially execute the function | ||
+ | :* Declaring a function pointer: <code>int(*pFunc)(int);</code>. pFunc can now point to any function that takes an int and returns an int, and is invoked with <code>()</code> | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | int Double( int x ) | ||
+ | { | ||
+ | return x * 2; | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | int ( *pFunc )(int); | ||
+ | pFunc = &Double; | ||
+ | // Note: the 'Address Of' operator & is optional, [ pFunc = Double; ] also works | ||
+ | std::cout << pFunc( 2 ) << std::endl; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | * [ | + | :* Now you can point to different functions and invoke them through one single pointer dynamically (at runtime) |
+ | </div> | ||
+ | * Using Function Pointers with a map [https://youtu.be/S2ZEEkquGy4?t=7m27s 7:27] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | void SixtyNine() | ||
+ | { | ||
+ | std::cout << "The S number\n"; | ||
+ | } | ||
− | [https://youtu.be/ | + | int main() |
+ | { | ||
+ | std::unordered_map<std::string,void(*)()> sw; | ||
+ | sw["s9"] = SixtyNine; | ||
+ | sw["s9"](); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :* Chili's opinion of this solution: | ||
+ | ::+ Nicer switch semantics | ||
+ | ::- But with less type safety, <code>sw</code> maps to any function with this signature | ||
+ | ::- While this is a good solution, you still need to declare a new function for every case in the switch | ||
+ | ::- Function definitions are still seperate from where we actually map the cases to the strings | ||
+ | </div> | ||
+ | * Alternative route: use <code>std::function<></code> in order to map to lambda functions [https://youtu.be/S2ZEEkquGy4?t=9m33a 9:33] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Problem is, every lambda function is its own seperate type, and we have to give a single type for all values in the map | ||
+ | :* In order to get this to work, we need to <code>#include <functional></code> so that we can use the type <code>std::function</code> | ||
+ | :* <code>std::function<></code> is a wrapper around "all callable things" and bring them all under one single polymorphic interface. They hold callables with a specific signature | ||
+ | :* In our example, you use it like so: <code>std::function<void()></code>, where <code>void()</code> is the actual type of the function (in this case, functions that take no parameters and return void) | ||
+ | :* It's like the function pointer syntax <code>void(*)()</code> (which is a pointer to a function), but without the <code>(*)</code> | ||
+ | :* Now we can assign lambdas to the switch that maps to that function type | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | ... | ||
+ | #include <functional> | ||
− | [https://youtu.be/Vh2JW5MHJX0 Tutorial 25 - Solution] | + | int main() |
+ | { | ||
+ | std::unordered_map<std::string,std::function<void()>> sw; | ||
+ | sw["sixty-nine"] = []() | ||
+ | { | ||
+ | std::cout << "The S value"; | ||
+ | } | ||
+ | sw["sixty-nine"](); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :* Finally, Chili is happy: | ||
+ | ::+ We have our function definition in the same place where we map it | ||
+ | ::+ We don't need seperate function definitions | ||
+ | ::+ <code>std::function</code> is the way forward! | ||
+ | :* It allows us to bring different lambda functors (that all have different types) into the same container. | ||
+ | :* Note that we can also pass the other function definitions to the map through <code>std::function</code> (the functionoid and function pointer definitions) | ||
+ | </div> | ||
+ | * Example: making a single string switch class [https://youtu.be/S2ZEEkquGy4?t=13m01s 13:01] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Implementation of the StringSwitch class: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | #pragma once | ||
+ | #include <functional> | ||
+ | #include <unordered_map> | ||
+ | #include <string> | ||
+ | #include <iostream> | ||
+ | |||
+ | class StringSwitch | ||
+ | { | ||
+ | public: | ||
+ | std::function<void()>& Case(const std::string& str) | ||
+ | { | ||
+ | return map[str]; | ||
+ | } | ||
+ | std::function<void()>& Default() | ||
+ | { | ||
+ | return def; | ||
+ | } | ||
+ | void operator[](const std::string& str) const | ||
+ | { | ||
+ | auto it = map.find(str); | ||
+ | if (it == map.end()) | ||
+ | { | ||
+ | def(); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | it->second(); | ||
+ | } | ||
+ | } | ||
+ | private: | ||
+ | std::unordered_map<std::string, std::function<void()>> map; | ||
+ | std::function<void()> def = [](){}; | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | :* Note that you can do useful things that you couldn't do with a normal switch: | ||
+ | ::- It's an object, so you can pass it around to different functions | ||
+ | ::- You can access variables outside of the switch (by capturing values, [] in the lambda definition) | ||
+ | ::- You can have switches that take multiple parameters (() in the lambda definition) | ||
+ | ::- Your switches can return values | ||
+ | </div> | ||
+ | * Homework assignment: time to face the music [https://youtu.be/S2ZEEkquGy4?t=19m18s 20:06] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | ::1 Dependency on box2D: install VCPKG and use that to install box2D on your system [https://youtu.be/S2ZEEkquGy4?t=21m36s 21:36] | ||
+ | ::2 Have the simulation destroy boxes when two boxes of the same color hit each other (collision event handling) | ||
+ | ::3 Create a pattern matching system using an unordered map [https://youtu.be/S2ZEEkquGy4?t=22m47s 22:47] | ||
+ | ::4 Implement a box splitting effect (into 4, maintaining properties) upon collision [https://youtu.be/S2ZEEkquGy4?t=23m55s 23:55] | ||
+ | </div> | ||
+ | </div> | ||
+ | |||
+ | === [https://youtu.be/4EZVTNHmojc Tutorial 25 - Bonus] === | ||
+ | <div class="mw-collapsible mw-collapsed"><br /> | ||
+ | * Pointers to a member function of a class/struct [https://youtu.be/4EZVTNHmojc?t=7s 0:07] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* To define a pointer to a member function, use <code>int(Foo::*pFooFunc)(int)</code> (example signature for a member function that takes an int and returns an int) | ||
+ | :* To point to an actual member function, use <code>pFooFunc = &Foo::SomeMemberFunction;</code> | ||
+ | ::-(Note: 'Address of' operator <code>&</code> is required here) | ||
+ | :* To call it, you can use it on a class instance foo <code>(foo.*pFooFunc)(3);</code> | ||
+ | :* If you have a pointer to a class instance <code>Foo* pF = &foo;</code>, the syntax to invoke through the function pointer is: <code>(pF->*pFooFunc)(3);</code> | ||
+ | :* This syntax is obviously not preferable, just good to know it exists | ||
+ | </div> | ||
+ | * Defining <code>std::function</code> to hold a pointer to a member function [https://youtu.be/4EZVTNHmojc?t=3m30s 3:30] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* When you wrap a member function in <code>std::function</code>, the instance becomes the first parameter of the function | ||
+ | :* The syntax: <code>std::function<int(Foo,int)> func = &Foo::SomeMemberFunction;</code> | ||
+ | ::- (Note, you could also take a Foo object by reference <code>std::function<int(Foo&,int)>...</code> | ||
+ | :* And to call: <code>func(foo,2);</code> | ||
+ | </div> | ||
+ | * <code>std::bind</code> from the Utilities library adapts function signatures <functional>[https://youtu.be/4EZVTNHmojc?t=5m57s 5:57] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Works as a sort of adaptor for functions; it allows you to bind values to input parameters | ||
+ | :* For instance say you have some free function <code>int Foo(int x)</code> | ||
+ | :* <code>std::bind(Foo,69)</code> defines a new function that calls <code>Foo</code> with the value 69. That new function will now take zero parameters: it essentially changes the signature | ||
+ | :* Useful if you want to map functors that have more input parameters than your map definition; we can adapt a function that normally would not fit in our function containers so that it fits | ||
+ | :* Here's how you apply this principle to "reduce" a function that takes mulutiple parameters: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | #include <functional> | ||
+ | |||
+ | int Thing(int x, int y) | ||
+ | { | ||
+ | return 2*x + y; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | std::function<int(int)> f; | ||
+ | f = std::bind( Thing, std::placeholders::_1 , 5 ); | ||
+ | f(10); // returns: 25 | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | ::- the placeholder ensures that the first parameter of f is forwarded to the first parameter of Thing | ||
+ | ::- we have "bound", or "adapted" Thing into the function pointer object | ||
+ | </div> | ||
+ | * Reference wrappers <code>std::reference_wrapper</code> [https://youtu.be/4EZVTNHmojc?t=10m20s 10:20] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Note that if you include a variable in the <code>std::bind</code> statement and you want it passed by reference, you need to use <code>std::bind( SomeFunc,std::ref(x) );</code> | ||
+ | </div> | ||
+ | * <code>mem_fn</code> creates a function object out of a pointer to a member [https://youtu.be/4EZVTNHmojc?t=12m12s 12:12] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Convenience function, lets you rewrite something like: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | std::function<int(Foo&,int)> f = &Foo::DoublePlus; | ||
+ | </syntaxhighlight> | ||
+ | :* into: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | auto f = std::mem_fn( &Foo::DoublePlus ); | ||
+ | </syntaxhighlight> | ||
+ | </div> | ||
+ | * Lambda functions can bind much more elegantly [https://youtu.be/4EZVTNHmojc?t=12m36s 12:36] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Binding <code>Thing</code> in the example 2 code boxes above with a lambda: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | auto g = [](int x) { return Thing(x,5); }; | ||
+ | g(10); // returns 25 | ||
+ | </syntaxhighlight> | ||
+ | :* Or, to bind a call to a member function of an objeect of class Foo with a Lambda (myfoo is an instance): | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | std::function<void()> f; | ||
+ | f = [&myfoo](){myfoo.DoublePlus(69);}; | ||
+ | f(); | ||
+ | </syntaxhighlight> | ||
+ | :* You don't stricly need to bind a lambda through <code>std::function</code>, you could also assign a lambda to a normal function pointer (with matching signature), but then you cannot use capture variables | ||
+ | </div> | ||
+ | * Operator function objects [https://youtu.be/4EZVTNHmojc?t=14m55s 14:55] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Useful when dealing with algorithms instead of writing your own custom predicates | ||
+ | :* Examples: <code>equal_to</code>, <code>modulus</code>, etc. | ||
+ | </div> | ||
+ | * Making a function pointer to a static member function [https://youtu.be/4EZVTNHmojc?t=16m30s 16:30] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* This is easier to do than in case of normal member functions, because static member functions do not operate to any specific instance of a class | ||
+ | :* So, say you have a static member function <code>static int DoStatic( int y )</code> inside you Foo class: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | int(*pStaticFunc)(int) = &Foo::DoStatic; // & is optional here | ||
+ | std::cout << pStaticFunc(2); // same as: | ||
+ | std::cout << (*pStaticFunc)(2); | ||
+ | </syntaxhighlight> | ||
+ | </div> | ||
+ | </div> | ||
+ | |||
+ | === [https://youtu.be/Vh2JW5MHJX0 Tutorial 25 - Solution] === | ||
+ | <div class="mw-collapsible mw-collapsed"><br /> | ||
+ | * A look at <code>world.step( dt,8,3 );</code> in <code>Game::UpdateModel()</code> [https://youtu.be/Vh2JW5MHJX0?t=28m39s 28:39] | ||
+ | * Solution to 1st part: destroy boxes of same color when they collide [https://youtu.be/Vh2JW5MHJX0?t=33m50s 33:50] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Interesting code snippet: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | // Remove dying boxes | ||
+ | boxPtrs.erase( | ||
+ | std::remove_if( boxPtrs.begin(),boxPtrs.end(),std::mem_fn(&Box::HasToDie) ), | ||
+ | boxPtrs.end(); | ||
+ | ); | ||
+ | </syntaxhighlight> | ||
+ | :* Does the same as: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | // Remove dying boxes | ||
+ | boxPtrs.erase( | ||
+ | std::remove_if( boxPtrs.begin(),boxPtrs.end(),[](auto& b){return b->HasToDie();}), | ||
+ | boxPtrs.end(); | ||
+ | ); | ||
+ | </syntaxhighlight> | ||
+ | </div> | ||
+ | * Solution to 2nd part: pattern matching [https://youtu.be/Vh2JW5MHJX0?t=42m20s 42:20] | ||
+ | <div class="mw-collapsible-content"> | ||
+ | :* Interesting code snippet: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | // Use std::bind to swap the order of the parameters of a function | ||
+ | // (f is of type std::function<void(T,T)>) | ||
+ | std::bind(f,std::placeholder::_2,std::placeholder::_1); | ||
+ | </syntaxhighlight> | ||
+ | :* Does the same as: | ||
+ | ::<syntaxhighlight lang="cpp" line> | ||
+ | [](const T& a,const T& b) {f(b,a);}; [[CHECK are these const refs? !!]] | ||
+ | </syntaxhighlight> | ||
+ | </div> | ||
+ | * The Spawn function of the <code>Box</code> class [https://youtu.be/Vh2JW5MHJX0?t=53m00s 53:00] | ||
+ | * Solution to 3rd part: add box tagging pattern [https://youtu.be/Vh2JW5MHJX0?t=58m46s 58:46] | ||
+ | * Solution to 3rd part: add box splitting pattern [https://youtu.be/Vh2JW5MHJX0?t=59m39s 59:39] | ||
+ | * Solution to 3rd part: add box splitting pattern [https://youtu.be/Vh2JW5MHJX0?t=1h10m06s 1:10:06] | ||
+ | * WORK-IN-PROGRESS | ||
+ | </div> | ||
== Homework Assignment == | == Homework Assignment == | ||
Line 57: | Line 328: | ||
== See also == | == See also == | ||
* [[Intermediate C++ Game Programming Series]] | * [[Intermediate C++ Game Programming Series]] | ||
+ | * Chili [https://youtu.be/lj9niRFCVGc grabbing a package using vcpkg] (note, SFML is not relevant for this tutorial, it's Box2D you want to grab) | ||
+ | * Chili [https://youtu.be/XywXJD8khHg grabbing a package using NuGet] |
Latest revision as of 07:07, 9 March 2020
Function pointers allow you to store and change what function is to be called dynamically at runtime. Combine that with containers to have collections of actions/behaviors that can be selected from, and then jam in the power std::function
to allow you to wrap up pretty much any kind of callable thing and bring them together in one container. Groovy.
Contents
Topics Covered
- Function pointers
- Functionoids
-
std::function
Bonus Video
- Pointers to member functions
- std::bind
- std::mem_fn
- std::reference_wrapper (a little)
- Pointer to static member function (it's the same as for normal funcy bois)
- You can use normal func pointers for lambdas if they do not capture anything
Video Timestamp Index
Tutorial 25 - Function Pointers
- How to switch on a string? (execute some code based on the string passed to the switch): using Functionoids 0:15
- One way to do this is with a map to polymorphic functors
- The Functionoid Pattern (objects that encapsulate function pointers) looks like this:
#include <iostream> #include <memory> #include <unordered_map> #include <string> struct CaseFunction { virtual void operator()() const = 0; }; struct SixtyNine : public CaseFunction { void operator()() const override; { std::cout << "The S number\n"; } }; int main() { std::unordered_map<std::string,std::unique_ptr<CaseFunction>> sw; sw["sixty-nine"] = std::make_unique<SixtyNine>(); (*sw["sixty-nine"])(); return 0; }
- But Chili is not a fan of this:
- - awkward syntax to invoke the switch,
- - need to revert to unique pointer protection
- - forced to put data on the heap
- - need to code derived classes for new functors
- - function definitions are seperated from the point in the code where we map them
- + On the plus side: provides good type safety
- Introducing Function Pointers 5:13
- Idea is similar to pointing to data (stores the address of the data in memory
- - Pointer to function stores the address to the first instruction of a function in memory
- - Functions are just a collection of instruction bytes in memory. If you jump to the memory address of the first instruction, you essentially execute the function
- Declaring a function pointer:
int(*pFunc)(int);
. pFunc can now point to any function that takes an int and returns an int, and is invoked with()
int Double( int x ) { return x * 2; } int main() { int ( *pFunc )(int); pFunc = &Double; // Note: the 'Address Of' operator & is optional, [ pFunc = Double; ] also works std::cout << pFunc( 2 ) << std::endl; }
- Now you can point to different functions and invoke them through one single pointer dynamically (at runtime)
- Using Function Pointers with a map 7:27
void SixtyNine() { std::cout << "The S number\n"; } int main() { std::unordered_map<std::string,void(*)()> sw; sw["s9"] = SixtyNine; sw["s9"](); }
- Chili's opinion of this solution:
- + Nicer switch semantics
- - But with less type safety,
sw
maps to any function with this signature - - While this is a good solution, you still need to declare a new function for every case in the switch
- - Function definitions are still seperate from where we actually map the cases to the strings
- Alternative route: use
std::function<>
in order to map to lambda functions 9:33
- Problem is, every lambda function is its own seperate type, and we have to give a single type for all values in the map
- In order to get this to work, we need to
#include <functional>
so that we can use the typestd::function
-
std::function<>
is a wrapper around "all callable things" and bring them all under one single polymorphic interface. They hold callables with a specific signature - In our example, you use it like so:
std::function<void()>
, wherevoid()
is the actual type of the function (in this case, functions that take no parameters and return void) - It's like the function pointer syntax
void(*)()
(which is a pointer to a function), but without the(*)
- Now we can assign lambdas to the switch that maps to that function type
... #include <functional> int main() { std::unordered_map<std::string,std::function<void()>> sw; sw["sixty-nine"] = []() { std::cout << "The S value"; } sw["sixty-nine"](); }
- Finally, Chili is happy:
- + We have our function definition in the same place where we map it
- + We don't need seperate function definitions
- +
std::function
is the way forward!
- It allows us to bring different lambda functors (that all have different types) into the same container.
- Note that we can also pass the other function definitions to the map through
std::function
(the functionoid and function pointer definitions)
- Example: making a single string switch class 13:01
- Implementation of the StringSwitch class:
#pragma once #include <functional> #include <unordered_map> #include <string> #include <iostream> class StringSwitch { public: std::function<void()>& Case(const std::string& str) { return map[str]; } std::function<void()>& Default() { return def; } void operator[](const std::string& str) const { auto it = map.find(str); if (it == map.end()) { def(); } else { it->second(); } } private: std::unordered_map<std::string, std::function<void()>> map; std::function<void()> def = [](){}; };
- Note that you can do useful things that you couldn't do with a normal switch:
- - It's an object, so you can pass it around to different functions
- - You can access variables outside of the switch (by capturing values, [] in the lambda definition)
- - You can have switches that take multiple parameters (() in the lambda definition)
- - Your switches can return values
- Homework assignment: time to face the music 20:06
- 1 Dependency on box2D: install VCPKG and use that to install box2D on your system 21:36
- 2 Have the simulation destroy boxes when two boxes of the same color hit each other (collision event handling)
- 3 Create a pattern matching system using an unordered map 22:47
- 4 Implement a box splitting effect (into 4, maintaining properties) upon collision 23:55
Tutorial 25 - Bonus
- Pointers to a member function of a class/struct 0:07
- To define a pointer to a member function, use
int(Foo::*pFooFunc)(int)
(example signature for a member function that takes an int and returns an int) - To point to an actual member function, use
pFooFunc = &Foo::SomeMemberFunction;
- -(Note: 'Address of' operator
&
is required here)
- To call it, you can use it on a class instance foo
(foo.*pFooFunc)(3);
- If you have a pointer to a class instance
Foo* pF = &foo;
, the syntax to invoke through the function pointer is:(pF->*pFooFunc)(3);
- This syntax is obviously not preferable, just good to know it exists
- To define a pointer to a member function, use
- Defining
std::function
to hold a pointer to a member function 3:30
- When you wrap a member function in
std::function
, the instance becomes the first parameter of the function - The syntax:
std::function<int(Foo,int)> func = &Foo::SomeMemberFunction;
- - (Note, you could also take a Foo object by reference
std::function<int(Foo&,int)>...
- And to call:
func(foo,2);
- When you wrap a member function in
-
std::bind
from the Utilities library adapts function signatures <functional>5:57
- Works as a sort of adaptor for functions; it allows you to bind values to input parameters
- For instance say you have some free function
int Foo(int x)
-
std::bind(Foo,69)
defines a new function that callsFoo
with the value 69. That new function will now take zero parameters: it essentially changes the signature - Useful if you want to map functors that have more input parameters than your map definition; we can adapt a function that normally would not fit in our function containers so that it fits
- Here's how you apply this principle to "reduce" a function that takes mulutiple parameters:
#include <functional> int Thing(int x, int y) { return 2*x + y; } int main() { std::function<int(int)> f; f = std::bind( Thing, std::placeholders::_1 , 5 ); f(10); // returns: 25 }
- - the placeholder ensures that the first parameter of f is forwarded to the first parameter of Thing
- - we have "bound", or "adapted" Thing into the function pointer object
- Reference wrappers
std::reference_wrapper
10:20
- Note that if you include a variable in the
std::bind
statement and you want it passed by reference, you need to usestd::bind( SomeFunc,std::ref(x) );
- Note that if you include a variable in the
-
mem_fn
creates a function object out of a pointer to a member 12:12
- Convenience function, lets you rewrite something like:
std::function<int(Foo&,int)> f = &Foo::DoublePlus;
- into:
auto f = std::mem_fn( &Foo::DoublePlus );
- Lambda functions can bind much more elegantly 12:36
- Binding
Thing
in the example 2 code boxes above with a lambda:
auto g = [](int x) { return Thing(x,5); }; g(10); // returns 25
- Or, to bind a call to a member function of an objeect of class Foo with a Lambda (myfoo is an instance):
std::function<void()> f; f = [&myfoo](){myfoo.DoublePlus(69);}; f();
- You don't stricly need to bind a lambda through
std::function
, you could also assign a lambda to a normal function pointer (with matching signature), but then you cannot use capture variables
- Binding
- Operator function objects 14:55
- Useful when dealing with algorithms instead of writing your own custom predicates
- Examples:
equal_to
,modulus
, etc.
- Making a function pointer to a static member function 16:30
- This is easier to do than in case of normal member functions, because static member functions do not operate to any specific instance of a class
- So, say you have a static member function
static int DoStatic( int y )
inside you Foo class:
int(*pStaticFunc)(int) = &Foo::DoStatic; // & is optional here std::cout << pStaticFunc(2); // same as: std::cout << (*pStaticFunc)(2);
Tutorial 25 - Solution
- A look at
world.step( dt,8,3 );
inGame::UpdateModel()
28:39 - Solution to 1st part: destroy boxes of same color when they collide 33:50
- Interesting code snippet:
// Remove dying boxes boxPtrs.erase( std::remove_if( boxPtrs.begin(),boxPtrs.end(),std::mem_fn(&Box::HasToDie) ), boxPtrs.end(); );
- Does the same as:
// Remove dying boxes boxPtrs.erase( std::remove_if( boxPtrs.begin(),boxPtrs.end(),[](auto& b){return b->HasToDie();}), boxPtrs.end(); );
- Solution to 2nd part: pattern matching 42:20
- Interesting code snippet:
// Use std::bind to swap the order of the parameters of a function // (f is of type std::function<void(T,T)>) std::bind(f,std::placeholder::_2,std::placeholder::_1);
- Does the same as:
[](const T& a,const T& b) {f(b,a);}; [[CHECK are these const refs? !!]]
Homework Assignment
This homework might wreck your butthole a little, but hopefully not too badly. Anyways, you get the code from the repo, you try and get it to build. The project requires Box2D, but the repo doesn't have it included so you're gonna need to wrangle it yourself. The easiest way to accomplish this is to pull in dependencies into your system with vcpkg. Some of you are probably going to run into some speed bumps at this point getting vcpkg to work on your system, but I recommend powering through and not giving the fuck up, because vcpkg is immensely useful for adding amazing libraries to your projects easily. If you search YouTube, you'll find a video of me showing how to install vcpkg and use it to grab SFML, so that might be a good starting point.
After you get that shit working, the party has just started. Then you need to figure out how the codebase actually works. Use all the tools at your disposal (debugger, Google, Box2D documentation, etc.), and don't get bogged down in unimportant distractions (you don't need to know how the triangle rendering pipeline works to understand the general simulation system, for example).
The actual tasks are, as laid out in the video:
- Implement destruction of boxes when two boxes with the same color trait hit each other
- Implement a box splitting mechanic
- Implement a pattern matching collision event manager based on std::unordered_map (this is the main task that ties into Intermediate 25)
- Use the pattern matching system together with box splitting and other effects to define simulation with various action that occur based on what colors of boxes collide with each other.
Chili will hook you up with a solution video that A) explains the starting codebase in some detail and B) goes over the process of implementing all the the above.
The repo: GitHub
See also
- Intermediate C++ Game Programming Series
- Chili grabbing a package using vcpkg (note, SFML is not relevant for this tutorial, it's Box2D you want to grab)
- Chili grabbing a package using NuGet