Always test with sanitizers, asserts and enable all the warnings 😎 (I just can't understand why the compiler didn't throws an hard error if you forget to return a value...)
@lowlevelgamedev93305 ай бұрын
it actually has, but sometimes it can't figure that out corectly.
@nicholas_obert4 ай бұрын
It really depends on the specific compiler and flags. Most modern compilers will add a return instruction even if you don't include it in the source code so that you can exit from the function. 'int main()' is a perfect example of the compiler automatically adding a return instruction on your behalf. But if you don't specify any return value, what does it return? Let's start by saying that there are multiple function calling conventions. The calling convention I use in the compilers I write (I don't remember if the C standard does it the same way, but it gotta be similar anyway) works as following: - make space on the stack for the return value by pushing the stack pointer by the static size of the return value type (the type of the return value must have a statically known size, otherwise you can return a pointer to a heap-allocated value. Pointers always have a static size). (This step is usually optimized by passing the return value through registers if the size is small enough to fit into a register). - push onto the stack the arguments to the function being called (usually this step is optimized by passing most arguments through registers). - push the current program counter onto the stack (so that the CPU can later jump to the instruction next to the function call when returning. - jump to the address of the function being called and execute its instructions On most architectures, the return instruction (sometimes called 'ret') just pops the topmost element off the stack, interprets it as the return address and jumps to it, effectively returning from where the function was called. Remember that the return value is either located on the stack or in a register, so it exists even if you don't specify any with a return statement in your source code. However, the return value is not automatically initialized and it's your responsibility as the programmer to do so correctly. C and C++, unlike safe Rust, do allow uninitialized values. Because of this, a function of the type 'int foo()' does not need to initialize the return value of type 'int'. That said, I think any compiler should complain about a function returning an unspecified value, except maybe for the 'int main()' function.
@ABaumstumpf4 ай бұрын
@@nicholas_obert "But if you don't specify any return value, what does it return?" A compiler-Error.
@Brad_Script5 ай бұрын
that's why I enable 20+ warnings on top of -Wall and -Wextra in GCC
@sebastianfalcone50464 ай бұрын
This is the right solution to many of the issues
@armancheshmi77025 ай бұрын
I like headbanging though
@mr.hashundredsofprivatepla37115 ай бұрын
He isn’t talking about metal
@noxagonal5 ай бұрын
@@mr.hashundredsofprivatepla3711 Ngl, C++ is pretty metal.
@caiocouto34504 ай бұрын
I do too but not while debugging
@sledgex95 ай бұрын
Also I swear I have seen compilers WARN you about forgetting to return from a non-void function. And also about forgetting to initialize a variable. The latter probably requires an increased warning level. Moral of the story: Always use the biggest warning level you can and always tell the compiler to treat warnings as errors.
@lowlevelgamedev93305 ай бұрын
well in msvc those things will be reported as errors not warnings fortunatelly, I actually ignore warnings 😂
@sledgex95 ай бұрын
@@lowlevelgamedev9330 I suspect that you get a lot of dumb warnings that's why you have been conditioned to ignore them. Try explicitly turning off the the dumb ones. I suspect those are very few, but produce a lot of noise. Also search StackOverflow on how to disable warnings on MSVC coming from external includes.
@balijosu4 ай бұрын
I can never bring myself to ignore warnings. -Werror every time!
@anon_y_mousse5 ай бұрын
Some useful advice, since the order of static initialization of globals isn't defined, put them all in one source file and declare them extern in a header that every other module includes. If all of your globals are in one place and defined top-down, then you'll be fine. Just remember to minimize your usage of globals. Also, avoid codependent structs and classes. That's a sure way of getting weird bugs and compilation fails that you won't necessarily understand. I can't remember the last time I had a buffer overflow issue because for static arrays I have a macro I defined years ago that is in my "standard" header which simply does sizeof( array ) / sizeof( *array ) and I use a custom array type for dynamic arrays which includes a length member.
@vastabyss64964 ай бұрын
I love the Minecraft music in the background. Nice video!
@lowlevelgamedev93304 ай бұрын
thanks 💪
@AntonioNoack5 ай бұрын
Mmmh, you should have mentioned tools like Valgrind, which are runtime sanitizers.
Always use fsanitize address and fsanitize undefined behavior flags, Wpedantic also helps
@balijosu4 ай бұрын
Best to avoid static initialization entirely. It seems simple, but it can definitely cause you headaches down the line. Watchpoints are also worth mentioning. I.e. expressions the debugger watches for changes. Try to make them hardware watchpoints so they're fast.
@venilc4 ай бұрын
"Never assume you know what the problem is" I dont know why, but I feel that. I've been surprised so often at what the actual error happened to be. I rarely say im 100% sure about something related to c++ 😅
@noritesc50005 ай бұрын
very fun bug i once got with stack coreption a example: void SetTo10(int& Value) { Value = 10; } char A; SetTo10((int&)A); then i learned don't cast values if funcion is expecting a refrence
@RetroAndChill4 ай бұрын
As someone who programs Java for work and then uses C++ in my spare time, I do like how many more things the Java compiler outright forbids that C++ only lightly warns you about
@mito24535 ай бұрын
An advice for dealing with undefined behavior or errors coming from different file/source code that is not part of the project. You can use the call stack to trace where the code was called from and go back until you find code that is part of your project. At least for me this helped me a lot when I got a random error from stbi_image and didn’t know what caused it.
@RebelliousX4 ай бұрын
how can you forget to return a value to the function that asks for a return value? I believe the compiler will complain.
@artey66714 ай бұрын
Here's a strange thing I noticed: For ints a and b, the expression a + b + b ist not necessarily equivalent to a + 2 * b.
@skeleton_craftGaming5 ай бұрын
2:34 which is part of the reason that it is bad practice to use a c style array. if you were following beat practices you would've been useing std::array[or std::vector]::at which throws if you try to access a out of bounds value
@redpepper745 ай бұрын
Wait does indexing a std::array still let you read out of bounds?
@skeleton_craftGaming5 ай бұрын
@@redpepper74 I think it throws [If you have exceptions enabled of course]. I said at instead because it is granted to be a constant member function. Where as there are cases where the index overload isn't.
@Johnny-tw5pr4 ай бұрын
One of the worst errors I've dealt with is when I opened a very large and old project that I had completely forgotten its existence. I try to compile and I get very ambiguous errors in the xmemory file. Boy that took a few days to debug,
@MilkywayWarrior16184 ай бұрын
Forgetting to declare a destructor as virtual is one of my (least) favourite
@tuvshinbayarmandakh70354 ай бұрын
It has happened to me when doing CP. My code sometimes gave correct answers but sometimes it did not. I was like "What in the quantum is this?", the bug was indeed undefined behavior. I forgot to return the value from the function XD.
@GhostVlVin5 ай бұрын
In one of previous videos, you told us, that you allocate memory as less as possible. In game, you have game loop where you update and draw all your objects, and I sure that you couple them in one vector or list using some common interface, so I have a question, how are you using vector of Interface based objects without memory allocation in cpp, or if you are not, what other technics you are using to get this result
@Sebo.5 ай бұрын
the signature of std::vector is template class vector; this templated argument Allocator manages how memory is allocated for the vector, you can for example write your own allocator so that it manages a pool so that instead of calling new, malloc or something else to get more memory from system, you call it once but large enough to store everything and you manage it yourself, i.e. construct or destruct other objects, this provides a speedup because you don't have to ask system for memory every time or you can free everything when you exit (which you can also do with the default allocator); this is just one way and is in my opinion the cleanest because it can be made generic
@lowlevelgamedev93305 ай бұрын
yo so, you can't get away with not allocating memory ever. But the idea is to allocate only when needed. So for growing arrays, I use vectors because they are conviniend and they clear the memory when I exit the scope. To answer your question, you can watch that long one hour video about my minecraf clone, but basically I have ome vector for each entity type and I have some tricks to iterate through all of them. The idea is that if no entities are created, there is no memory allocated so the game moves fast.
@lowlevelgamedev93305 ай бұрын
so you can use the polimorfic allocator verison, for the normal vector you can indeed pass an allocator but the api for that is very wierd and has a very wierd limitation, so it is like it doesn'f even exist unofortunatelly :((( What I would do is I would add a temporary arena if needed. Like one that clears every frame
@insentia84244 ай бұрын
What I do in the game I'm writing from scratch: I allocate a chunk of virtual memory and then use that. The OS and hardware then map that memory as it's needed into physical memory. This means I could even request unreasonably huge amounts of memory for a game that just get more entities the longer you paly that can't get unloaded (automation/factory builders). This means manually taking care of that memory and using/reusing it while the game runs, but it also means the only real point of failure in regards to allocations is in the beginning. There might be slow downs related to the memory mapping I am not privy to yet. And it'd be an interesting thing to see if it's worse than memory allocations or not, but that's for future me to discover.
@dziuaftermidnight4 ай бұрын
to me, it was simpler to just switch to C entirely. yes, you may have to write more code sometimes, but at least you can truly master it. and also, no worries about implicit stuff that C++ does all the time.
@lassdasi5 ай бұрын
clang-tidy to the rescue!
@gtdcoder5 ай бұрын
Don't use raw pointers and manage resources with RAII.
@kritomasP4 ай бұрын
there's also -fsanitize=undefined for UB
@ltecheroffical5 ай бұрын
How do you orginize your games? I mean how are you orginizing your objects, scenes, dialog and more?
@jasiek13094 ай бұрын
Remember that a++ + a++ is undefined behavior
@sadscientisthououinkyouma18675 ай бұрын
Macros are not evil, like anything called "evil" in programming it is because some people don't know how to use them correctly and cause far worse errors in the process.
@ohwow20745 ай бұрын
They're evil. Sure they have some obscure use cases though.
@rubynaxela85245 ай бұрын
goto
@dogyX35 ай бұрын
@@rubynaxela8524 I think I've seen it used like a super-break in nested loops. Sometimes, its much neater than setting a flag, break, then checking that flag outside the inner loop
@balijosu4 ай бұрын
The X-macro technique is very useful.
@panjak3234 ай бұрын
Why couldn't you use constexpr in your case ?
@lowlevelgamedev93304 ай бұрын
long story, glm didn't let me for some reason, no idea why honestly, it gave me compiler errors
@panjak3234 ай бұрын
@@lowlevelgamedev9330 yeah right, it probably doesn't have constexpr constructors.
@ChrisCarlos645 ай бұрын
Macros are not evil but they are easily abused and can be obtuse on how to use them. I will only use them as a last resort in some cases and maybe to cut down on repetitive stuff. Otherwise, I try to avoid them when possible because I hate the pain of leaked macros.
@moonyl53415 ай бұрын
1:12 how is that done
@wowyomad5 ай бұрын
getData() returns a reference to a local variable. It's an undefined behaviour. Usually you would get segmentation fault here.
@AntonioNoack5 ай бұрын
You have to watch a bit further, where you then can see the getData function. int data = 10; is set inside the function getData(), but int data outside the function is using the return value of getData(), and that is undefined.
@wowyomad5 ай бұрын
@@AntonioNoack I replied here before your comment but the reply is gone😕. I'm too lazy to rewrite it but in short, it's not just because the variable was created inside the function but also because he returned a reference to it, not the value itself. And since value was created on the stack, it got erased before return was called.
@bananacraft695 ай бұрын
macros can definitely be useful. i'm working on a project, where i have to access a specific value a lot, but "this->memory[this->registers[registerIndexes::ri_ISPT]]" is a mouthful so i just used a macro to reduce it to smth smaller, then undefined it at the end of the file
@felps32135 ай бұрын
In such cases I like to define a separate function for accessing the value. Compiler will inline calls to one-line functions 99.9% of the time in release mode (-O). Or even defining a lambda locally in the function where said value is accessed often.
@bananacraft695 ай бұрын
@@felps3213 yeah but i think macros are just simpler in this usecase tbh
@elijahshadbolt73345 ай бұрын
Be aware of simple macro names, you don't want to accidentally undefine someone else's library macro.
@bananacraft695 ай бұрын
@@elijahshadbolt7334 dw i always check if a macro name is in use before i do smth like that TwT
@marcinwawrzkow73945 ай бұрын
It might be also good to define lambadas for accessing in single file or even method. If you’re using multiple object which share methods but don’t share interfaces the concept might be helpful. Even if the codebase gets a little bit bigger, you’re guaranteed to have Much cleaner error messages, than in macros, that are just copy and paste. At least imo
@benshulz41794 ай бұрын
0:58 none of these examples even compile, lol.
@waxlbloh64504 ай бұрын
#1 using cpp
@sledgex95 ай бұрын
You probably meant "buff[32]" instead of "buff[33]". Using 33 means it is 2 elements past the end. The valid range of your array is 0-31.
@lowlevelgamedev93305 ай бұрын
actually I meant 2 elements, it seems like the last element was uses as a guard or something in debug to check for this kind of overflows
@sledgex95 ай бұрын
@@lowlevelgamedev9330 oh, ok.
@sledgex95 ай бұрын
@@lowlevelgamedev9330 After you comment I did some digging on StackOverflow. It is legal C++ to point to one element past the end. Dereferencing it is undefined behavior. This can be used as a marker for the end. Think of it in the context of STL containers. All the end() iterators of stl containers are defined as "pointing to one element past the end". Hence "return array+arraysize;" is a valid implementation for and end() method.
@sledgex95 ай бұрын
@@lowlevelgamedev9330 The last bit I forgot. By allowing one-past-end element to be valid, that probably means that the next item on the stack (aka the next declared int variable) would be 2 elements past the end. So your buff[33] works on corrupting the other variable.
@zanagi5 ай бұрын
How do you have your c++ game published on steam? I thought they only allow unity or unreal unless you contact them i guess
@lowlevelgamedev93305 ай бұрын
what? no, also like half of the games on steam use custom engines, tho don't take my word for it, I am just speaking out of memory, there are statistics online, you can use whatever you want they don't care
@martiqmarty5 ай бұрын
i like your videos, but find it so hard to follow as a non native english speaker aswell. if you would talk a little slower i think it would be much easier to follow. The content is so good so i find it frustrating i cant understand sometimes
@Mohamed_Ahmed-2225 ай бұрын
Subtitles might help
@xenopholis475 ай бұрын
so when he throws arrows and the particles come out after arrows hit are constructors? and does he use destructors after it so that gets removed? am i right to understand that?
@lowlevelgamedev93305 ай бұрын
basically the colors are static variables. They are constructed once when the program starts, and they have that value for the whole program. I just use the color values. But they are wrong because they were initialized wrong when the program started
@xenopholis475 ай бұрын
@@lowlevelgamedev9330 Oooooohhhhh.... ok ok got it. I had to read it 5 times to get it LMAO
@atackhelikopter43035 ай бұрын
sometimes warnings are good, other times the compiler doesn't like your code and you get 20+ warnings, and it is unfixable (nothing i tried to to fix them worked and nothing i tried to make the code unstable worked) that's my experience with warnings lol
@lowlevelgamedev93305 ай бұрын
yes lol, that's why I ignore them 😂😂
@balijosu4 ай бұрын
Paste your problematic code here 😁
@atackhelikopter43034 ай бұрын
@@balijosu 600 lines of code, that's way to many bruv i would need to make a pastebin and search for the code because it is months old
@balijosu4 ай бұрын
@@atackhelikopter4303 I'm ready when you are 🙂
@Dizintegrator5 ай бұрын
Bro writes c99 and presents it as c++ problems bruh
@K9Megahertz5 ай бұрын
Yeah I was confused on this as well.
@billclinton49135 ай бұрын
the new keyword is pure evil
@benshulz41794 ай бұрын
tell me you've never coded without telling me you've never coded
@marks_shot5 ай бұрын
camaka
@arl-t8d5 ай бұрын
cameka
@discontinuity75264 ай бұрын
hey Low Level Game Dev, what do you think about using rust?
@lowlevelgamedev93304 ай бұрын
yo, its not really for me and I don't think it fits gamedev. I preffer to be more flexible while writing code, and the safety that rust gives I can get from the way I structure my code. So it isn't really for me. I don't even use const in my code for context lol 😂😂
@discontinuity75264 ай бұрын
@@lowlevelgamedev9330 lol makes sense, thanks for the reply 😂
@thepuppetqueen575 ай бұрын
I love c++ I really do but most libraries make me wanna die
@lowlevelgamedev93305 ай бұрын
well most cpp devs use their own libraries for this reason, so maybe give it a try to make your own library, you will like it 💪
@thepuppetqueen575 ай бұрын
@@lowlevelgamedev9330 nah the libraries I use are server libraries and I would rather die than make one of those all by myself
@downbad.5 ай бұрын
Reminder to keep knives and ropes away while using *C++*
@vycdev5 ай бұрын
first
@lowlevelgamedev93305 ай бұрын
lets goo 😂😂
@Mempler4 ай бұрын
Nr 1) C++
@dampfwatze5 ай бұрын
When you realize that Rust's main feature is to prevent undefined behavior... 🦀
@dampfwatze5 ай бұрын
@marcsfeh This not about the specific execution model of a CPU. While a computation platform is generally deterministic, the exact state of a system is usually not reoccurring. Most undefined behavior comes from access to where it shouldn't be/it was not expected, or race conditions. Race conditions depend on many factors in the OS, that are different every time. Rust eliminates undefined behavior mainly through the borrow checker, which ensures every possible access to data is known and can be handled. It also provides many well engineered constructs to deal with uncertain situations at runtime. All this stems from Rust's strict policy against undefined behavior. Memory safety is one part of this. Usually, Memory safety can be advertised better.
@user-tw2kr6hg4r5 ай бұрын
1. Choosing C++ over any other language.
@benshulz41794 ай бұрын
what's the problem with C++ in game engine? One of the best languages to use.
@EduardKaresli5 ай бұрын
Avoid all these problems by moving to Rust. 🤷 EDIT: since my answer to the guy below wasn't published, I have to assume that the author of this channel doesn't like when facts are not in his favor. Hence I will state here that: 1. there is an operating system written in Rust and is called Redox OS. 2. That Rust is used in the Linux kernel itself. 3. Rust is used in gamedev and there are game engines built with Rust, for example the Bevy game engine. 4. After programming for 20+ years (15+ in C++, 7+ in JS/TS) I can safely state that, for C++ developers, there are absolutely no excuses why NOT move to Rust.
@averdadeeumaso40035 ай бұрын
Operating Systems APIs don't use Rust, low level devs must use C and C++
@desnerger63465 ай бұрын
@@averdadeeumaso4003 safe != high-level. Just because someone uses e.g. winit/glutin instead of glfw, or even glium instead of raw opengl calls, that doesn't mean the person isn't a low-level game developer. The safe API wrappers are a pretty thin layer in the overall game engine architecture.
@EduardKaresli5 ай бұрын
@@averdadeeumaso4003 There is an operating system written in Rust called Redox OS. Also, Rust is used in the Linux kernel.
@linsoe-u4d5 ай бұрын
Tbh, rust makes game dev even more complicated and don't give advantages
@linsoe-u4d5 ай бұрын
@@averdadeeumaso4003 I don't exactly get what you mean, but those low level apis can be used from rust and also alot of windows componets are being rewrite in rust. rust is also now part of the linux kernel.