Function Pointers (InputManager Class) | C++ in 2021

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys welcome to the fourth lesson in the c plus plus in 2021 series so today what we're going to do is we're going to create a dummy input manager class that will show off function callbacks and function pointers so that we can register simulated implementation functions for handling input in a hypothetical game engine let's say so we're going to do this in a very uh straightforward way we won't go into all of the details but we'll be fairly thorough in our process at least showing how to using mostly important functions that you'll likely need throughout your journey and if you need more specific functionality again go get the book professional c plus plus by montgomer there is a link down in the description and as always if you like this series you're learning as much as i am hit like subscribe and you know all that crap let's just get into it so the first thing we're going to do is we're going to clean up our main function we're just going to move all this vector stuff into its own function so right vector stuff and we will just literally just take all this vector stuff and move it into here with that done um let's start working on that input manager class so the end goal let's let's um block out kind of what we're looking to do so the in the end goal would be something like we have a game class uh defined here and within our game class we have a function let's say a boolean handle mouse event and we'll just make it take a string for now and the event itself is going to be a string and means it will import string from up here we want to be able to register this handle mouse event into some sort of input handler so we are going to forward declare our input handler for now input handler input manager so here we're going to have a private input manager and what we're going to want to do is inside our input manager we would want to have an let's say an init function that registers uh handle mouse event with input manager right so that's basically what we want to do we want to register this with our input manager and when that's done [Music] whenever the input manager detects input it will call out to any uh any functions that we've registered to it so let's let's create our input manager so let's create a new folder or filter and we'll call it input and we'll create a new a new module in here we'll call it uh input manager module input input manager there you go one thing i did learn um in the interim uh christopher iman i think it was his name um in the comments on the modules video he pointed out how we could separate our implementation from our module interface so we can create a new cpp file we'll call it input manager.cpp and all we need to do here is put module input manager now you see here how it says modules is undefined you can just ignore that basically now inside here we can create our input manager class so we will do class export class input manager and then inside our main.cpp we'll import our input manager class and then we can remove the forward declaration and all that should build i imagine yes and it builds so we've got we've got the the the basics in place so our input manager class what does it what is this going to do uh first there's going to be some public stuff and some private stuff so let's just make those labels in the public section there's going to be a couple of functions there is going to be a register callback function we'll take a type an event type and it will take the callback which event callback now we need to define what these are so the event type is simple enough it's the type of events we could have so in an input manager you can imagine there's a mouse keyboard gamepad let's use those three so let's do enum class event type mouse keyboard gamepad so we've got that now event callback this is where uh we start talking uh about today's subject so we'll start off with the uh legacy way of doing it uh the non-standard way [Music] which what we're going to do is use function pointers so we can use create a type elias i think that's how you say that word a-l-i-a-l-i-a-s elias if it's said differently who cares alias hello darkness my old friend um with the using keyboard using event callback equal and then we will define our function so uh function pointers historically have been defined as uh the return type with a star and then i believe it's like this and our in this case uh if we look here we expected a standard string so we'll do standard string and we need to import string and we will also import iostream as well actually we don't need ios stream here but we'll import string here and what this will do this is a function pointer which can hold a function that returns void and takes a string as a para a parameter right [Music] so we want to be able to hold a list of these callbacks inside a private variable and we were gonna we're gonna hold them in an unordered map of vectors and that will be defined here so instead unordered map and would be event type and vectors of event callback and we'll call this callbacks so let's define our register callback function let's see let intellisense try to do that for us and if it takes too long we'll do it ourselves and we'll do it ourselves let's copy this we'll go into input manager paste it in take that and here we will actually this is where we will import iostream and this will actually be initialized to its null initial zero initializer and that should build again um you see a bunch of squiggles that's because intellisense is incredibly slow as modules do still hopefully microsoft is working on it at least microsoft has support for modules that's something i'll give them so with register callback it's going to be fairly simple what we're going to do is we're going to say callbacks type dot pushback callback that's all we'll do so what that's going to do is it's going to take the callback and it's going to put that callback inside the vector of that type and that should still build good with that done we can now uh we'll need one more function we'll call it void let's say detect input and it will take a type of type and it will have the message and this is just to simulate um a input detection so you can also call this we can we'll call this dispatch message that's what we'll call it and this will dispatch a message to all of the listeners of that type all the callbacks that are listening for that type right so let's uh copy that and implement it down here and again we'll put this and this will be very simple as well we'll do four auto call back in callbacks of type and we'll do callback message and that should work that should build and it builds fine so that's our basic interface uh we shouldn't need to touch this too much until we look at new until we look at newer conventions so the spoiler warning this isn't going to work straight out of the box for this use case because these this function the functions here they are our object functions so they're bound to the object itself so what it will work for right now though is a like a global function an unbound function so if you do void um handle mouse event did string message and then uh let's import format here and we'll just do account format the message message and let's put a new line in there now in theory that should still build b since intellisense isn't working properly i'm building periodically just to make sure i'm not screwing up anywhere because sometimes i rely too much on squiggles so with that being said we can now create an input manager so we'll create an input manager and with that input manager we will register callbacks register callback and this will be input manager event type mouse and this will we will pass it the handle mouse event function so this will register the callback into this unordered map and then when we call dispatch it should dispatch the message that we tell it to dispatch to through that function which will print the message and then the message so with that done we'll hit dispatch input manager event type mouse and then we give it the message and this message is for me and will that build no dispatch is not a member of input manager dispatch message um let's actually rename it dispatch and dispatch all right let's build that again that builds okay and when we play it we should see this the message this message is for me and that's exactly what we were expecting now what if we tried to do this within the function here what would that give us and i'll just just for demonstration purposes we're going to return to the actual game struct later but here in our init function we do something similar im.register callback input manager event type mouse and here we would do something like and uh handle mouse event right and what happens if we try to do that [Music] here it says a legal operation on bound member function expression right so now if we try to do something like this i i am is an undeclared identifier because this is technically static so it's not able to resolve itself it doesn't work um in the end we'll we'll come back to this and i'll show you how to get it to work because this is uh what we want to do but first um let's move away from function pointers so let's rebuild this make sure it builds good so this is the function pointer now this is the old way of doing it and it has all the limitations that it has the limitation i showed you and some more limitations that are not really worth going into um what we could do however is use the standard library provided type stood function which is in the functional header like this so instead of void star here we would do stid function then angle bracket so the the syntax is a little bit different but uh it's a return type open parenthesis parentheses and then the parameter types so in this case stood string and close that and with that done everything should still work you see here it builds and it runs so this in the background um does the same thing as what we were doing with this pointer except for it's a standard function that's all well and good but it still doesn't solve this issue because like i said this is still doing basically the same thing right here we go now one way we can fix this one way that it's recommended to fix this if you want this to work is instead of passing the point of the string directly what you can do is you can actually pass a pass a bound function into the function itself that handle that accepts the callback so what we're going to do here is we are going to use the standard function called stidbine and what sid bind does is it takes first parameter it will take the func the function you're trying to find a game handle mouse event the object that you're trying to bind it to which in this case is this and then the uh parameter list that uh that you want to pass into it in this case is one parameter so what you're going to want is stood placeholders under score one so what this will do is for the first parameter of this function or of this method it's going to put it in the first slot so if you did um this what this would do is um when this callback is called so in here right where is this callback message let's imagine that that the callback message takes two parameters right we call it like this message in one now if for some reason uh you want to uh in the background so here there's like let's just say this takes an int right into num uh and here [Music] there so let's just say um for example you're trying to pass a function in that has this signature but all your other functions have the signature where it's reversed where string is first and into second but you don't want to you can't rewrite the function function definitions because they're already in use you can use this bind functionality with these placeholders to swap those to forward them in different directions so here you'll notice here that the second one the second thing passed in is going into the first slot and the first one passed in is going into the second slot so when we go here the first one which is string will go into the second slot as string and the first one here or the second one which is an end will go into the first slot defined by placeholders2 in the first slot here you can do that and you can also use bind to by non-member function functions as well but um those are to swap the parameter order but it's not really very it's not something you're going to want to use very often so now with this um we can let's head let's copy this put this here and add the message variable here now in theory that should build no no matching token bound uh what am i missing oh i need another one of those that should build i'm undeclared identifier void init struct oh it's not i am it's input manager there right and this one here is just demonstration i should remove it and it builds so the bind functionality here forwarded the um bound the call from to this function into here and stored it into our callbacks list and then properly calls it back here this message is for me and actually that's not right we're not calling it yet so now um so we bound it this way now we can remove this we can actually just create our game create our game call init on it which will register the callback and then we can call uh and we will create a function called run run which we'll just call input manager dot dispatch input manager event type mouse bound message and now we can go down here and call g dot run and we should get a bound message being printed in the debug window there were errors what's here handle mouse event must return value so this is a boolean um let's go here we defined it as void let's change it to boolean so if you want to stack your callbacks and only use a certain amount i'll show you that in a second and yeah so let's play that what's going on handle mouse event must return a value oh i still didn't all right is that going to work now no uh it's a matter of n-type mouse oh and we have this defined at the top from the last lesson and there we go everything's working better now so remember putting stupid defines in your code is probably not a good idea overall but we got it working and now we run it bound message shows up in the window so this is one way to do it the other way to do it is using lambdas now lambdas are useful because you can use lambdas um basically anonymously without doing too much work so let's say um just as a quick demonstration we want to just have a function that just says um uh let's just say we call it is even right and we give it and we give it two numbers uh let's just say template type name t and we'll say t num1 t num2 and we give it a call back right and we say uh uh we give it a call back here now with lambdas uh you can just put auto if you um expect to always use it in the same way and this is called a i believe it's called a templated a templated lambda or something like that and what that will do it will actually generate basically works like templates but with lambdas so it will [Music] determine how your usage of the function and then automatically generate those functions accordingly so uh here we'll call it boolean and let's just say we call return um callback num1 num2 right now i believe um with that we're able to do something like this so we can call um count stood format is even take that and then we do is even and we let's say one to call it is equal to keep it simple is equal and we'll call it is equal 1 2 and then we will give it a lambda so the way lambdas are declared and lambdas are anonymous functions i should have mentioned is we put square brackets that's called the capture list but parentheses which is the parameters it takes and we put curly braces which is the function definition so with that in there we can define it now we know that we're this callback is going to call two numbers together so we are going to call it with uh let's say uh into num1 and num2 because we know we're path passing integers i think we might actually even be able to do this let's try this now auto is super powerful these days so that's why i'm showing off with auto um so auto num1 auto num2 and inside here we will do return num1 equal num2 right and i think that'll work let's see and that worked so what this will do is it will check if it's even and call this function which will which will take two numbers of a an arbitrary type and call them on our callback where it will take two numbers and run equal on it but it's not even doesn't even need to be numbers now we can let's run this we'll see is even as false and now if we take 2 is even is true now since everything is defined as auto and it's all lambda and everything is templated what if we do this what if we take two strings auto auto oh is that going to be equal yes it is even it's equal to each other now before you put a another letter in there is even as false now you're going to see this is the kind of thing that happens actually in a lot of standard function standard library algorithm functions which we'll probably go over at some point and a lot of times this will be done so this is how how it works now let's say let's say we want to [Music] check if it's these two numbers are even to uh like a number here so let's say number two check against five right now let's go here we'll take one and two now if we want all the numbers to be equal to five um we want to have access to number to check again so let's go here number two check i guess and if we try to build that it's going to give us an error because it's not captured now i mentioned that the square brackets are called your capture list or capture brackets or whatever you want to call them it's the capture zone where you capture the things around it to pass into your lambda so if you put an equal you're capturing everything automatically um that's within scope by value and technically you're only capturing the things that you use so if you have number to check against in here it will capture this by value right so if we run here false everything is not equal to five but if i put this to two and i change this to 2 [Music] then this should all become true is even as false why is that false [Music] oh the because boolean one is not equal to two so um and num1 equal number of checkpoints and num2 equal number to check against there that should work true all right okay so if you use equal it it'll pass everything by value so if we do um here at the end here if we add a plus plus here it won't actually even let you do it when you capture stuff by value you're not allowed to change the values of the stuff you capture you can you can however sit allow yourself to change that number that you you captured by value by putting mutable after your parameter that's a parameter list so if you do this this will come out false because what happens is num1 equal num2 and the num1 is equal to 3 right and then this checks against three and it's all it becomes false everything's false now but if we print that number to check against again print number to check against one more time then you'll see that it is still two even though we changed it now if we change to if we decided to pass it by reference which is by doing a default capture with the end symbol which is the reference symbol you'll see that it still returns false but it changes the number to three so that's mostly what you need to know about lambdas there's other things here you can like you can make this a constant explorer and you can give it a return type but we'll get to return types in a minute so that's the basics of lambdas let's delete this we don't need this and let's go back up here our stid functions here can also accept lambdas and it makes lambdas make binding stuff easier without having to remember this bind syntax so we can go here and do input manager register callback and we'll put a second callback inside here input manager event type mouse and then we can put a lambda so we will capture list parameter list and function body declaration [Music] now the important parts about this um we know we're binding to this function right we want access to the stuff in this class in this object so we can also bind this in here and that will give it uh that will basically make it like the lambda is running as part of this class it's the easiest way to explain it there's probably some nuances where that's not quite the case but for the most part that's it um we know that the function signature we're looking for um that where we need to register is a state string and we also know that it needs to return a boolean now to declare your return types you're going to declare it in what i believe is called trailing return form where it's an arrow and bull that's all that's needed now inside here we can what we're going to do is we're simply going to forward this to our handle mouse event and give it we should probably give this a namer parameter and message here there we go now if we play that we should now get two bound messages because we're looping through all the balance callbacks so one last thing that i want to go over is that just quickly in our input manager i mentioned how uh why we were returning boolean because here you notice here that we're looping through everything let's say we want to be able to block if a certain layer has handled the message so we do if this returns true then we'll return all right and if cb returns true the message has been handled and should not propagate further and then we can go inside here so now we'll go back and we'll only see one right we only see the one bound message because it hits this one it goes into the handle mouse event and that returns true now if we flip the order of these uh let's move the comment with it if we flip the order of these and instead of returning handle mouse event here we just call it and we return false it's no longer going to block that layer and we'll keep on going and see now we've got two that's everything we're going to go over today for our input manager and our function pointers function um stood function and our lambdas if again if you're looking for more in-depth information on the subject and a bunch of technical details and a few [Music] other things you can do with buying stuff like that [Music] go buy the book it's a great book otherwise thanks for watching thanks for learning always cite your sources and hit like subscribe and all that crap see you next time
Info
Channel: Ozzadar
Views: 133
Rating: undefined out of 5
Keywords: c function pointer, c fucntion pointer, c function pointer with arguments, c function pointer call, c function pointers typedef, c function pointer input, c function pointer state machine, function returning pointer in c, passing pointer to function in c, passing array pointer to function in c, c++ function pointer, c++ lambdas, c++20, c++ store function, c++ callbacks, c function callbacks, function callback, c++ delegates
Id: Dq4-GaCoFUI
Channel Id: undefined
Length: 36min 5sec (2165 seconds)
Published: Thu Jul 01 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.