Code Reviews are back! Hope you guys enjoyed this episode ❤ Don’t forget you can try everything Brilliant has to offer-free-for a full 30 days; visit brilliant.org/TheCherno. The first 200 of you will get 20% off Brilliant’s annual premium subscription!
i love that in c++, there isn't one single defined way of doing things. i mean, there are standards, but i love it that I can combine c-style code with object-oriented code without much of a problem. Its like using linux but in a programming language
although this is coming from me, someone who uses a heavily modified version of old-fashioned quake and doom's zone and hunk allocator with c++ objects
anyone who says non-inlined functions are bad and reduce performance should read quake3's source code. That thing doesn't inline almost anything yet it could run at 333 fps on 1997 computers with pretty decent (even for today) graphics.... (which is better than some games today)
I think the hate against goto is way overblown and uses like this help make the code more readable, not less. However this could also be solved with a scope guard
When I worked at a "triple-a" game studio, we had a build system that would effectively produce what you were calling "unity builds" when producing non-debug builds. I can't remember the exact numbers, but it'd take around 3500ish cpp files and compile them in something like 8 to 10 translation units. It *drastically* reduced the amount of time spent linking, but took heaps of memory to accomplish. Debug builds just relied on incredibuild and spare cycles on artist's machines to compile quickly, but linking could be a real pain sometimes! Incremental building with edit and continue was a godsend.
That's the main reason you'd use unity builds. I've implemented one for a company where the builds were starting to take so long it would impact the development process.
I thought the apart of the point of having multiple cpp/translation units is that when you need to recompile it will only recompile files that have been modified? There for you save compilation time by only having to recompile the necessary files, say for example with the 3500 source files split of 8 final translation units. If you needed to modify one of those sources files it will now have to recompile 3500/8 (430) source files in-order to compile that one change source file? Does combining 3500 sources files into 8 translation units and linking them out way compiling 3500 source files into 3500 translation units and linking them?
@@gmfCoding Compilation is faster than linking by a lot. I've played around with some C projects with hundreds of thousands of lines of code that compile in about 3 seconds and barely do any linking because they only use a single translation unit and include everything in the main.c file. In fact, one of my own projects used a single translation unit for a while, but as it grew, visual studio started to complain more and more. So I switched the whole thing to a bunch of separate translation units and it slowed the builds down by about 3 or 4 times, even if I had only made a change to one of the translation units.
@@gmfCoding That is the point yes, but for final release builds you tend to do a full clean build to ensure the compiler/linker can do all the optimizations and there's nothing can can possibly corrupt built files in the downtimes etc etc. Building less files makes a clean build faster so it's good for that final release build step, but yes incremental builds are better for small changes hence why they kept all the files seperate while working on it and only did the compress into less files for the release.
@@gmfCoding In the release builds most of the times you'll build the whole project just because the amount of changes is very big (and some header files when changes forces to rebuild a lot of object files), so unity builds in this case are more than preferred (cuz overall build would be much faster), but during the development when changes are small, non-unity builds are faster (it will take a little time after merges/pulls but in the rest of the time it'll build in seconds). But you know what? It depends. I think in some cases unity builds can be faster in both cases (but in the product where I used to work year ago it wasn't the case)
"one might argue functions calls are bad because of performance, which is ridiculous in this case" Thank you. Far too many people try to argue this as a reason for having the longest functions known to man.
There's a relatively new option in VS, called "Sticky scroll", that makes reading nested code like this much easier (for those of us who like functions to have a single exit point). Its description is "Group the current scopes within a scrollable region of the editor window" with options to set the number of scopes to track and whether to prioritise the inner or outer scopes. It basically means you always know what class, loop, if(), etc. you are looking at without having to scroll backwards (or hover over the curly braces, like you used to have to). Thanks for the videos. Stay safe out there.
That is amazing! I definitely didn't know about this. Even though I'm not particularly fond of messy logic or functions with load of crud in them, sometimes you just inevitably end up with messy code as a result of really complex logic that is hard to pull apart. I'll definitely have some use for this~
Neat, I guess. I use vim to edit code, so to keep something in mind like that, if I needed to, I would just create a split and scale it down to one line then scroll down. I suppose I could write a plugin to have it do so automatically and clean up as I leave a scope, but I don't tend to write 42 levels of nesting.
This might be me being a boomer (technically a millenial but whatever) but I prefer to write code that can be read with minimal intervention from the editor. (Syntax highlighting is ok, I guess).
@@CallousCoder I don't think it's so much the use of object orientation, but rather the overuse. Most game engines are giant bloated messes, but it doesn't happen in one go, it takes time for that cruft to build up.
@@CallousCoderI use OOP for state machines. The FSM I use uses Polymorphism to do state transitioning from one to another. Starts with a state base class, all necessary state classes derive from it. Then you have common virtual methods such as Enter, Exit, Update and Draw which are defined in base, then each derived class overrides those methods. OOP is EXTREMELY recommended for an FSM
@@demonking2526 It makes something that is so trivial unnecessarily complex and harder to test all paths properly. I am ditching the whole OOP mindset more and more, the older I get and going back to the old school procedural way. Which is cleaner, easier readably (maintainable) , faster and uses less memory. And most importantly for us, it's easy to prove something is correct. I usually deal with mission critical real time systems in energy/trading and medical. And OOP has been a pain to prove correctness in order to get things certified. Ditching OOP upped my (our) output significantly because it's trivial to prove correctness and timings. So screw OOP I will no longer use it unless it solves a real problem. Which usually it doesn't do, especially not in controlling hardware systems. But I like your thinking!
The goto usage at 17:15 is EXACTLY what a good goto is for: breaking out of nested loops. This removes the need for additional conditionals and probably outer loop variables.
I have never used SDL, but it can be condensed into: // any other more appropriate name bool PollEvents() { SDL_Event event{}; while ( SDL_PollEvent( &event ) ) { if ( event.type == SDL_QUIT ) { return false; } // handle any event HandleEvent( event ); } return true; } ... while ( PollEvents() ) { // game logic, rendering, etc... }
When you mentioned that whoever made this probably came from a different language like Java or C#, that made me chuckle. It really does look like they were trying to write C++ code in more or less exactly the same way as those other languages
OMG I would die for that next video. I've been so incredibly overwhelmed with the ways to organize a project that I have never made an actually big project. Love this channel
I do really enjoy the entire series of a section of code where it's not just "I think this is bad and for this reason" but you also get "and here is how I would do it along with why I would do it that way." I struggle with doing code reviews professionally because of the emotion people bring to my comments. I always explain the "why" and give examples of what they could do instead but it hasn't helped. For anybody having their code reviewed by others, either professionally or in this kind of setting, the reviewer isn't saying that YOU are bad. Your code is not an extension of you as a person. The thing that may be bad is the code. At the end of the day we all want well written code. We just disagree of what well written code is.
Once i was following a tutorial installing wine on my Ubuntu, I accidentally followed a tutorial written for Arch. At some moment i *sudo apt install pacman* in the terminal and the game poped up. Imagine my confusion as a Linux newbie!
I've been thinking about writing my own package manager, as I would like to implement my own OS, and I was considering calling it Tetris because the game involves packing misshapen blocks into tight spaces. Or would that be too evil?
Love the review, even though I'm not a C++ programmer in the slightest. Would love to see the state machine video that you suggest making. Maybe a refactoring series on your improvements/changes to a codebase as a game developer if you're up to it too?
Ever get a State Machine video? I love state machines way too much. Theyre just always so useful in so many different situations. Im a long time game programmer, from graphics to gameplay to networking, and in all those, I have ended up using, often multiple, state machines per project,
About all the code being in header files: it looks like all of this could be properly split into header/source files, but it just reminded me of working with templates. You were SUPPOSED to write the header with template references, then write a source file for every possible data type that would be handed in. My library was more about storage, efficient access, all that fun stuff, so I literally didn't CARE what the internal data was because I didn't touch it. I found out that you were allowed to put code inside the header file to make it uniform across all implementations. so I just put ALL the code in the header. The IDE yelled at me for not having a source file, so I gave it an empty source file. The library worked fantastically, and I went on to use it in quite a few of my projects because it simplified handling things like unicode, pointer arrays, stuff like that. Is it RIGHT to do it like that? No. Was I planning on going back and doing it right? Never.
I first stumbled on the idea of grouping C++ files together for build speed around 2001, as a way to speed up Warcraft III builds (heavily templated, compilers of that era took forever). I called it bundle files, and then used it later internally to distributed shared libraries in source form instead of DLLs. And then years later I find out other people were calling it unity builds, and eventually Unreal put it into their build system, and so did Meson, so here we are.
super interesting to see code formatted in such a way, as a learner id love to see a more proper conventional way. Looking forward to the state machine/refactor video!
I've been working on a game engine as a small hobby for some years and by accident, I included like this. I then stumbled upon weird linking errors and eventually dropped the project. It took me a year to understand c++ better and realize what I've done. It was such a pain in the ass to restructure the entire project. The Project is much better now and no more weird bugs, thank god
A git tutorial in increasing depth as the video progresses would be good. Starting with the basics like initialising a repo, configuring settings for the repo, adding files to the staging area, etc. Then moving up to branching making changes, merging back onto another branch, resolving conflicts, etc. That would make for a good video I think! There are videos like that already but they tend to drone on quite a bit. I think your style, presentation & way of explaining things in simple terms would make for a much more captivating and useful video for most people. :)
@@merlinwarage Lots of the documentation can be quite terse with regards to the technical jargon. I can't imagine a beginner understanding much of the terminology.
The code you review almost always looks so simple and then when you dive into a C++ code base it's like night and day in terms of how complex C++ can get
@@user-ge2vc3rl1n no, I’m saying complex is like memory/pointers. They’re complex but can be understood and have a rational purpose. Many C++ features are just pure bloat and have really poor syntax and a negative effect on performance
he's the king of no preemptive organization at all. keep the entire project structure in your head at once, understand all of it at once and at all times, don't make your life easier at any point :p
Edit: Please ignore this comment, the author was confused with a feature from another language. Hello, there is an alternative to the goto used at 17:18. You can put a label before a while (or any loop statement) and used the instruction "break LABEL_NAME;" from any nested while to break the NABEL_NAME while. It also work with "continue LABEL_NAME;" I don't remember wich c++ revision impltemented this features.
@@bamberghh1691 I've been looking for a source for 20 minutes now, and indeed, this feature ("named break/continue") is present in various languages but not in any version of c++. I was convinced I'd seen this feature in an article about c++20 or c++23, I apologise for the confusion.
Would love to see you work through this turning it into a state-machine etc, definitely something I feel would be useful to watch the process of along with the logical steps to move from this to a well structured program.
This is called an "amalgamation." It is what you historically did to get the benefits of link time optimizations - the compiler can "see" everything, everywhere, all at once, making it "know everything". The full benefit only kicks in if you make all the stuff static - it can be sure that nobody else needs it, so it will know that it can recklessly inline tons of stuff that is used once. The other way is to give a compiler option that tells it that it's all one source file `-fwhole-program`. To be clear, I agree with your criticisms, 100%.
Just a few minutes in, I just wanted to note for what it is worth "#pragma once" although it is widely supported it is not part of the official C++ standard and if you value the portability of your code or would prefer to strictly adhere the C++ standard not compiler implementation you should avoid using pragma and use the standards conformant "#ifndef, #define, #endif".
A state machine video will be great!, I will also love to see a "To OOP or nope, thats the question" video. Particularly when-yes/when-not (environment, kind of proyect, etc) kind of approach. BTW, I'm an embedded programmer (for hobby).
I have multiple colleagues who develop software for unmanned aerial systems which have specs that PCs had in the 80s and they are damn good at it, but their git repositories? Oh boy.
They are 2 unrelated skillsets. Sure, they sometimes go hand-in-hand but someone who's good at git can be terrible with code, and someone who's good at code can be terrible with git
If you code just for yourself, as a hobby, there is no real need to use Git (or similar tool). "use this maingame v2 backup fixed.cpp" is a perfectly valid filename.
I would say the lack of experience does show in the code (since everythings in header files lol), however this guy is truly great if he can navigate that code and write such a game. Just inexperienced for now...
I've cloned Pacman for multiple platforms, including various mobile phones, microcontrollers, and Javascript. There are a few gameplay things that are missed here that I noticed immediately: Key-presses/joystick input should set the direction Pacman should try to move in next frame. If it cannot move in that direction it will continue in its current direction. Also it should never stop moving unless it hits a wall (and has no pre-loaded next direction). The ghosts actually have the same logic. Ghosts never reverse direction, unless a power pill has just been eaten. The ghost's eyes should point in the direction they're moving Pacman should stop briefly after eating a ghost, while the sound effect plays - it just make it feel better :) In this game the pills are being eaten before Pacman is actually centred over them, which is not quite correct - he should be centred over the pill before it's eaten as it feels better again :) The ghost patterns are wrong, but honestly I don't care about that. In the real game each ghost has its own logic. Nice submission though, keep it up!
Even in the original, Pac-man doesn't eat the dot when centered. It's just eaten as soon as he enters the tile that contains a dot, similarly to how the ghost detection works. If you go frame by frame, you'll see it disappears from inside his mouth.
+1 for the Git/Github tutorial! Hahaha I'm a beginner and have been messing with it a little bit but could definetly use a "good practices" video on that :)
One thing I do that I wonder what you think of is I specifically put single-line if statements in (the existing style at 18:00) if and when I'm not going to put the brackets in. If the condition and code block is too long to go on a single line, it gets brackets. That way I never come along while debugging and go "hey, I'm just going to put a print statement here to see whats going on" and immediately get a compile error because I forgot the brackets. But one other thing, this is code after my own heart. Not that I would even begin to write code like this today, but I remember when I was but a tadpole who didn't even _begin_ to understand why you needed separate files for things, why you would static (not that static does anything if you only have one cpp file), or why nesting to infinity is bad. Back then, I would've written something that looks a lot like this, and I'm sure you'd have given it just as much shit, rightfully so.
"you KIND-OF just combine it all into LIKE a single file, a single KIND-OF c++ file, that then you send to the compiler as LIKE a single translation unit. so that is effectively KIND-OF of what we have here because these are the only KIND OF source code files" this is way too much 'kind-of' and 'like', so esp. for people who want to learn, it gets really hard to understand what the message is about - kind of :) files are files, if they are 'kind of' files a compiler will spit out lots of errors, or 'like kind-of' warnings kind-a-like-a
I have been programming for 30 years, and I still start my C programs in a similar way... I'll start out with one file main.c and then I'll add a header when I start to factor out chunks of code. But that header will contain the functions that I'm stripping... And then another file or two in a header... And then after about the fifth or sixth finalize break it all down into headers and c files. Absolutely drives one of my friends completely batty when he sees me doing this. It's like calm down most of my coat is a spike I don't care what happens to it next week :p
It took 13 minutes to get to the main function. I think these code reviews are super interesting. But often the discussion gets stuck at a trivial point and it get repeated for 10 min and then video is almost over. More interesting to move on to more interesting part of the code, after point was made.
Unity builds are actually pretty great; faster compile time and less files to manage. Personally though, I'm not a fan of using header files for unity builds, or for "single-header" libraries for that matter. I'm of the opinion that if you plan to include declarations along with definitions, then you should put that into a .c/.cpp, not .h file. That way, if someone else happens to take a look at the project, its abundantly clear that what you're doing is merging source into a single compilation unit. Even for just that embedded data (char array), I would have that as a separate .cpp file that gets included where necessary, but that's usually because I have a program I made that converts images and binary formats into C source that can be embedded into a project.
I think this very easy and understandable architecture, and if you write some simple small project like this one, you do not need to divide it into separate translation units i written one plugin once in the same way )
My note: You have to declare all of your header files into a different cpp program and then declare .hpp inside it to make use of it not only in a single cpp file but also in other files....also as cpp has a top down aproach, it becomes compulsory to write the cpp header in an order,so it is probably not good to write headers inside a single file
At 17:00, the dreaded 'goto' statement. A reminder to the computer science zealots, branch statements are used in assembly all over the place by the compiler, aka a 'goto' statement. I use them when it simplifies the code, like in drivers, etc. I won't cancel you. 😆
Just because assembly has the equivalent of goto, it doesn't mean your higher-level language should use them. If you want to use all the features of assembly, program in assembly.
@@merseyviking yes it’s far easier to exit a complex state environment with overly complicated if, then, else, flags to break out loops. I only use a goto statement when it’s deliberate, makes it easier to read the code. Your argument is rubbish. I’ll code the way I do, you do you . I don’t care.
@@glee21012 I'm asking would you want people working under you who do not know everything you might to use it. You had absolutely no idea what was meant? Even if it wasn't clear to you, it's plain English, how do you solve hard problems when you do not understand?
yes cherno can you please make a detailed video on how to make vs solution if you only have code files and can you also explain how the cmake works and to make a vs solution from cmake as well
True, though since gcc supports it I find it an acceptable method. Basically, any feature that gcc supports, since it's free and open source, I find acceptable because literally anyone can download and use it on nearly any platform, and there are cross compilers for gcc that can target some old and strange platforms which other compilers ignore.
Please name a compiler in industry use today that doesn't support pragma once. I'll wait. Pragma once is literally better supported than a lot of actual C++ standards.
It is a bad idea to put a method outside of the class but inside the header file. A header file should be able to include several times. Putting code outside of the class in the headerfile makes that header file essentially a code file and it can only be included once in your program. This is not a problem for this program since there is only one source file but normally we would rather have multtiple source files. Putting the code of the method in the class itself makes the method inlined and so it can be included in multiple files. You can achieve the same thing by writing inline RETURNTYPE CLASS::METHOD(ARGUMENTS) { CODE } I.e. have the keyword 'inline' in the declaration of the code but again it is cleaner to just put it in the class itself if it is a small method. Larger methods should always be placed in a separate source file with the same name as the class - and it is usually a good idea to have one headerfile for each class and one source file for each class where you place the methods that are not inlined. If a class have only inline functions and no static variables, then and only then can you have a headerfile without a sourcefile associated with it.
Not a programmer here (not a professional one at least). I'm a 2D artist and i feel like the talk about different languages in different situations is close to me too lol. How you implement all the basics and sctructure your work to become something new, interesting and unique, that works for the thing you're building is close to any creative endeavor.
i really like those reviews) You also sounded really angry after about 8:00 =) P.S. i would really love to see you talking about state machines more because i've never heard of them
I like the way they did their code with the header files its actually easier to read and find things rather than put a bunch of files everywhere and scramble to find what you are actually looking for. Edit: 8:46 That is the point of why it was also used for only 1 file not sure why that is such a bad thing when the project just works
@6:21 This is a compile time resource and should be an `inline constexpr std::string_view CharBoard{ ... maze here ... };`. Header files shout NOT have constant variables at all, only constexpr's
Not using C++ would be a way more effective compile time resource. Or at least using the least amount of C++ features you can. Most C++ features are arbitrary and the compiler doesn’t really know how to properly organize it. Which is why it takes so long for any C++ program to compile.
@@colleagueriley860 Can you come up with facts? Every language has its pro's and con's but I've seen C++ compile to efficient code in my last 30 years of using it. Like any tool it just requires knowledge on how and when to use it best :)
Hey, please do make a visual studio setting up/ configuration type video. I have always thought Visual studio was kinda complex but watching you use it I wanna use it but it would be great to watch how you have/set up a project.
I'm looking forward to the state machines video! Can you quickly mention all the ways to solve deeply nested code, as the one in Terraria? One common strategy I use is having guard statements.
Cherno, it would be nice to see you do a Git tutorial. I understand it enough to do some basic stuff but it gets real dicey real quick where I am unsure if I am going to screw something up real quick. Like I can do basic push/pull/commit.
Surprisingly this code is smart in it's use of simple approach. Kinda like it ngl. If I don't have plans for reusability or shared code base why not just stop jumbling with header implementation duality. It's heretic I know but I'm an engineer I am practical by nature. If you know better there's no problem if you broke the rules in my book. Btw if you use -O2 or higher optimization compiler might decide to break or goto implicitly by dead code ellimination optimization, so right usage but obviously dangerous so heresy. 😅
In general I'm not against header-only type projects, but the thing that makes me pale is the reliance on #include ordering. That's incredibly fragile.
Really great job from the dude that made that clone! It looked really, like the original... which was his goal i assume. Regarding header vs translation units... i fully disagree with your opinion about creating a translation unit for every .h or .hpp file, because this increases the compile time enormously, which is especially true for medium to large size projects. I always do one single translation unit for the entire application. It doesn´t mean that all the source codes are stored in one file, no. Its still separated by separate source files, but the declarations and implementations are guarded by preprocessor defines (Single-Header-File-Library). In very rare cases, where one single header translation file wont work - then i would use a separate translation unit for such cases. For example, Box2D does not like this idea, so i had to use one translation unit separately for that or when i want to give other header files access to a common shared library, then i would create a seperate translation unit as well. Yes i am aware that it will compile everything always - but this is still faster, than compiling hundreds of translation units. Look at unreal engine, it has thousands and thousands of translation units... hence require hours of compiling time, which is insane.
This is why you're supposed to use `make` or some similar build system. The initial build should be the only time that all units get compiled unless you make changes, and then only the units that you've changed will be recompiled. Just to be clear, `make` checks the timestamps of the files to see if the module has been edited since the last time it was compiled before it determines it should compile a given unit. So change one module and it'll compile just that one thing and it'll be faster than compiling everything at once. Maybe go watch Jacob Sorber's videos on how to write a Makefile as I think you could benefit from that.
@@anon_y_mousse I use primarly visual studio, so there is no make in there. Also I really hate build systems to the bottom of my hearth! The idea about build systems are great, but all the implementations - all of them are crap, period! And no, i used them all and still use some them unfortunatly (CMake and Make) - but only when i create opensource libraries/applications. In any other case, VStudio is just fine - it builts my stuff, even when it has ~500k lines of code - in one translation unit and it compiles in seconds, rather than minutes/hours.
@@anon_y_mousse There are unreliable, are over complicated, the syntax is always not intuitive and some are really slow, some built-systems are built on top of another built-system... O_o No just no... thanks for the advice, but i stick with my method and use no build-system at all or at max, the build-system i am forced to (CLion -> CMake, MSBuild -> C#).
hey, what software did you use to draw on the screen at 10:02 ?
Год назад+3
If you are going to write implementation code in your headers, put it into a #ifdef block and demand the user #define a macro before #including the header. That way, the user can #include the implementation in only one source file and still use the header in multiple compilation units.
Allocating on a heap runs some internal operating system allocation algorithm, so it's obvious that it's faster than allocating (or "pushing") on the stack, which is basically an std::vector of fixed capacity that every C++ programmer should know how to implement, but I presume you already know that. Now for the slower access: Memory in computers is split into several different levels, the higher the level the more memory it has, but also the slower it is for the processor to read and write that memory. Simplified, the levels go like this (from highest to lowest): RAM -> processor caches -> processor registers Processor registers are very limited, with only 16 64-bit general purpose registers on modern machines. But they're the only way to modify the memory in the RAM (e.g. add two numbers and write them back) In general all of your data _should_ reside in the RAM. However accessing your entire RAM all the time would be slow, and that's what the processor cache is for. Whenever you read or write memory from RAM to a register, the processor copies an N-byte (64-byte on modern machines) chunk where that memory you want to modify resides in from RAM to the cache. If the cache is full, the processor writes the chunk that has not been used for the longest time back to RAM and replaces it with the new chunk. Whenever your function needs more memory than the processor registers can contain (and every function called within that function adds up to its memory requirements, so it's pretty much unavoidable), it saves its local variables on the stack. Because you're using the stack pretty much all the time, it's very likely that the data that is on the stack will be in the cache (this is also why you don't allocate arrays on the stack and use the heap or declare them as 'static' or 'thread_local' in C and C++, so they're basically "preallocated on the heap" for the lifetime of the program if you use 'static' or thread if you use 'thread_local'). But technically, if you use the data on the heap just as often as you do local variables on the stack, you should get the same performance, no? There are two problems with this approach though: 1. you need to store a pointer to access the data on the heap, while if it's on the stack, it's offset from the stack pointer can be computed at compile-time and no additional pointer is needed 2. if the size of your data does not align with some multiple of the size of the chunk that the processor fetches, then you waste some memory, since the processor fetches some data into the cache that you might not ever use in your program Basically, you do not use the heap for anything that has constant size at runtime, for that you have either your local variables for the small things or the 'static' and 'thread_local' keywords for the big stuff.
Code Reviews are back! Hope you guys enjoyed this episode ❤
Don’t forget you can try everything Brilliant has to offer-free-for a full 30 days; visit brilliant.org/TheCherno. The first 200 of you will get 20% off Brilliant’s annual premium subscription!
i love that in c++, there isn't one single defined way of doing things. i mean, there are standards, but i love it that I can combine c-style code with object-oriented code without much of a problem. Its like using linux but in a programming language
People who cancel the goto are the same ones who cancel the raw c-style pointer, they're just scared to deal with the low level stuff.
although this is coming from me, someone who uses a heavily modified version of old-fashioned quake and doom's zone and hunk allocator with c++ objects
anyone who says non-inlined functions are bad and reduce performance should read quake3's source code. That thing doesn't inline almost anything yet it could run at 333 fps on 1997 computers with pretty decent (even for today) graphics.... (which is better than some games today)
out of all the horrid things EA has done, the EASTL is where they truly shine
Writes a goto - "I'm probably going to get canceled for this." LOL
I think the hate against goto is way overblown and uses like this help make the code more readable, not less. However this could also be solved with a scope guard
@@samanthaqiu3416 True, look at nginx. The code is littered with them at places for a reason.
If you go deep enough all loops are defined using goto.
@@skeleton_craftGaming
That's the "if you go deep enough, everything is just 0 and 1" argument. True, but not necessarily useful.
@@Asto508 It does at least prove that the end result should be the same
When I worked at a "triple-a" game studio, we had a build system that would effectively produce what you were calling "unity builds" when producing non-debug builds. I can't remember the exact numbers, but it'd take around 3500ish cpp files and compile them in something like 8 to 10 translation units. It *drastically* reduced the amount of time spent linking, but took heaps of memory to accomplish. Debug builds just relied on incredibuild and spare cycles on artist's machines to compile quickly, but linking could be a real pain sometimes! Incremental building with edit and continue was a godsend.
That's the main reason you'd use unity builds. I've implemented one for a company where the builds were starting to take so long it would impact the development process.
I thought the apart of the point of having multiple cpp/translation units is that when you need to recompile it will only recompile files that have been modified?
There for you save compilation time by only having to recompile the necessary files, say for example with the 3500 source files split of 8 final translation units.
If you needed to modify one of those sources files it will now have to recompile 3500/8 (430) source files in-order to compile that one change source file?
Does combining 3500 sources files into 8 translation units and linking them out way compiling 3500 source files into 3500 translation units and linking them?
@@gmfCoding Compilation is faster than linking by a lot. I've played around with some C projects with hundreds of thousands of lines of code that compile in about 3 seconds and barely do any linking because they only use a single translation unit and include everything in the main.c file. In fact, one of my own projects used a single translation unit for a while, but as it grew, visual studio started to complain more and more. So I switched the whole thing to a bunch of separate translation units and it slowed the builds down by about 3 or 4 times, even if I had only made a change to one of the translation units.
@@gmfCoding That is the point yes, but for final release builds you tend to do a full clean build to ensure the compiler/linker can do all the optimizations and there's nothing can can possibly corrupt built files in the downtimes etc etc. Building less files makes a clean build faster so it's good for that final release build step, but yes incremental builds are better for small changes hence why they kept all the files seperate while working on it and only did the compress into less files for the release.
@@gmfCoding In the release builds most of the times you'll build the whole project just because the amount of changes is very big (and some header files when changes forces to rebuild a lot of object files), so unity builds in this case are more than preferred (cuz overall build would be much faster), but during the development when changes are small, non-unity builds are faster (it will take a little time after merges/pulls but in the rest of the time it'll build in seconds). But you know what? It depends. I think in some cases unity builds can be faster in both cases (but in the product where I used to work year ago it wasn't the case)
"one might argue functions calls are bad because of performance, which is ridiculous in this case"
Thank you.
Far too many people try to argue this as a reason for having the longest functions known to man.
There's a relatively new option in VS, called "Sticky scroll", that makes reading nested code like this much easier (for those of us who like functions to have a single exit point). Its description is "Group the current scopes within a scrollable region of the editor window" with options to set the number of scopes to track and whether to prioritise the inner or outer scopes. It basically means you always know what class, loop, if(), etc. you are looking at without having to scroll backwards (or hover over the curly braces, like you used to have to). Thanks for the videos. Stay safe out there.
That is amazing! I definitely didn't know about this. Even though I'm not particularly fond of messy logic or functions with load of crud in them, sometimes you just inevitably end up with messy code as a result of really complex logic that is hard to pull apart. I'll definitely have some use for this~
Its in vscode too and its amazing so much better than the crumby breadcrumb bar
Neat, I guess. I use vim to edit code, so to keep something in mind like that, if I needed to, I would just create a split and scale it down to one line then scroll down. I suppose I could write a plugin to have it do so automatically and clean up as I leave a scope, but I don't tend to write 42 levels of nesting.
This might be me being a boomer (technically a millenial but whatever) but I prefer to write code that can be read with minimal intervention from the editor. (Syntax highlighting is ok, I guess).
This is usually called code context
Would definitely be interested to see your makeover of this clone and also a video on state machines in general. You got my vote for that.
aye. and my axe
+1 for state machines, always looked quite cumbersome in Oop patterns books so would be good to see it done here.
Yeah state machines shouldn’t be done in OOP
Most games therefor don’t need OOP.😂
@@CallousCoder I don't think it's so much the use of object orientation, but rather the overuse. Most game engines are giant bloated messes, but it doesn't happen in one go, it takes time for that cruft to build up.
@@anon_y_mousse absolutely agree.
@@CallousCoderI use OOP for state machines. The FSM I use uses Polymorphism to do state transitioning from one to another. Starts with a state base class, all necessary state classes derive from it. Then you have common virtual methods such as Enter, Exit, Update and Draw which are defined in base, then each derived class overrides those methods. OOP is EXTREMELY recommended for an FSM
@@demonking2526 It makes something that is so trivial unnecessarily complex and harder to test all paths properly.
I am ditching the whole OOP mindset more and more, the older I get and going back to the old school procedural way. Which is cleaner, easier readably (maintainable) , faster and uses less memory. And most importantly for us, it's easy to prove something is correct.
I usually deal with mission critical real time systems in energy/trading and medical. And OOP has been a pain to prove correctness in order to get things certified. Ditching OOP upped my (our) output significantly because it's trivial to prove correctness and timings. So screw OOP I will no longer use it unless it solves a real problem. Which usually it doesn't do, especially not in controlling hardware systems.
But I like your thinking!
The goto usage at 17:15 is EXACTLY what a good goto is for: breaking out of nested loops. This removes the need for additional conditionals and probably outer loop variables.
you're still a mad man if you use goto
DON'T YOU DARE OR KAISER WILHELM WILL BE MAD
Moving the SDL_PollEvents to it's own function that's a bool could do the trick too.
I have never used SDL, but it can be condensed into:
// any other more appropriate name
bool PollEvents() {
SDL_Event event{};
while ( SDL_PollEvent( &event ) ) {
if ( event.type == SDL_QUIT ) {
return false;
}
// handle any event
HandleEvent( event );
}
return true;
}
...
while ( PollEvents() ) {
// game logic, rendering, etc...
}
i was wondering in this case can't we just use return to exit the whole thing ?
@@TsukinouraNo because some code still has to be executed before returning
When you mentioned that whoever made this probably came from a different language like Java or C#, that made me chuckle. It really does look like they were trying to write C++ code in more or less exactly the same way as those other languages
Definite yes to both tutorials you mentioned, especially the project to Visual Studio conversion. Would love to see it.
OMG I would die for that next video. I've been so incredibly overwhelmed with the ways to organize a project that I have never made an actually big project. Love this channel
I do really enjoy the entire series of a section of code where it's not just "I think this is bad and for this reason" but you also get "and here is how I would do it along with why I would do it that way." I struggle with doing code reviews professionally because of the emotion people bring to my comments. I always explain the "why" and give examples of what they could do instead but it hasn't helped. For anybody having their code reviewed by others, either professionally or in this kind of setting, the reviewer isn't saying that YOU are bad. Your code is not an extension of you as a person. The thing that may be bad is the code. At the end of the day we all want well written code. We just disagree of what well written code is.
My mind has been so poisoned that I thought "pacman" referred to the Arch package manager.
Once i was following a tutorial installing wine on my Ubuntu, I accidentally followed a tutorial written for Arch. At some moment i *sudo apt install pacman* in the terminal and the game poped up. Imagine my confusion as a Linux newbie!
I've been thinking about writing my own package manager, as I would like to implement my own OS, and I was considering calling it Tetris because the game involves packing misshapen blocks into tight spaces. Or would that be too evil?
@@anon_y_mousse Go for it.
Same and I was disappointed
that's funny but i had to dislike it sorry
Love the review, even though I'm not a C++ programmer in the slightest.
Would love to see the state machine video that you suggest making. Maybe a refactoring series on your improvements/changes to a codebase as a game developer if you're up to it too?
Ever get a State Machine video? I love state machines way too much. Theyre just always so useful in so many different situations.
Im a long time game programmer, from graphics to gameplay to networking, and in all those, I have ended up using, often multiple, state machines per project,
About all the code being in header files:
it looks like all of this could be properly split into header/source files, but it just reminded me of working with templates. You were SUPPOSED to write the header with template references, then write a source file for every possible data type that would be handed in. My library was more about storage, efficient access, all that fun stuff, so I literally didn't CARE what the internal data was because I didn't touch it. I found out that you were allowed to put code inside the header file to make it uniform across all implementations. so I just put ALL the code in the header. The IDE yelled at me for not having a source file, so I gave it an empty source file. The library worked fantastically, and I went on to use it in quite a few of my projects because it simplified handling things like unicode, pointer arrays, stuff like that.
Is it RIGHT to do it like that? No. Was I planning on going back and doing it right? Never.
I first stumbled on the idea of grouping C++ files together for build speed around 2001, as a way to speed up Warcraft III builds (heavily templated, compilers of that era took forever). I called it bundle files, and then used it later internally to distributed shared libraries in source form instead of DLLs. And then years later I find out other people were calling it unity builds, and eventually Unreal put it into their build system, and so did Meson, so here we are.
super interesting to see code formatted in such a way, as a learner id love to see a more proper conventional way. Looking forward to the state machine/refactor video!
I've been working on a game engine as a small hobby for some years and by accident, I included like this. I then stumbled upon weird linking errors and eventually dropped the project. It took me a year to understand c++ better and realize what I've done. It was such a pain in the ass to restructure the entire project. The Project is much better now and no more weird bugs, thank god
A git tutorial in increasing depth as the video progresses would be good. Starting with the basics like initialising a repo, configuring settings for the repo, adding files to the staging area, etc. Then moving up to branching making changes, merging back onto another branch, resolving conflicts, etc. That would make for a good video I think!
There are videos like that already but they tend to drone on quite a bit. I think your style, presentation & way of explaining things in simple terms would make for a much more captivating and useful video for most people. :)
That really needs to include the awful pitfalls of things like "detached head" and other situations a beginner may struggle to resolve
I mean... git is pretty well documented with a lot of examples.
@@merlinwarage Lots of the documentation can be quite terse with regards to the technical jargon. I can't imagine a beginner understanding much of the terminology.
The code you review almost always looks so simple and then when you dive into a C++ code base it's like night and day in terms of how complex C++ can get
Not really complex, just abstract and dumb. A lot of C++ features cause both compile time and performance to worsen.
@@colleagueriley860 that's the same thing in different words.
@@user-ge2vc3rl1n no, I’m saying complex is like memory/pointers. They’re complex but can be understood and have a rational purpose. Many C++ features are just pure bloat and have really poor syntax and a negative effect on performance
Yes please. A video on the refactoring of this code to make it beautiful and using a finite-state machine would be excellent. Thanks.
That type of project setup is quite similar to what Casey Muratori does on Handmade Hero
he's the king of no preemptive organization at all. keep the entire project structure in your head at once, understand all of it at once and at all times, don't make your life easier at any point :p
@@ar_xiv What do you mean by that?
Love to see the state machine. Also, the creating project you asked us to mention
Edit: Please ignore this comment, the author was confused with a feature from another language.
Hello, there is an alternative to the goto used at 17:18.
You can put a label before a while (or any loop statement) and used the instruction "break LABEL_NAME;" from any nested while to break the NABEL_NAME while.
It also work with "continue LABEL_NAME;"
I don't remember wich c++ revision impltemented this features.
Oh, that's really good to know
I don't think that named breaks are in C++, maybe you're confusing it with the Java's named breaks?
@@bamberghh1691 I've been looking for a source for 20 minutes now, and indeed, this feature ("named break/continue") is present in various languages but not in any version of c++. I was convinced I'd seen this feature in an article about c++20 or c++23, I apologise for the confusion.
breaking to labels only works in java and javascript
@@Brad_Scriptand in Rust, and in Dart, and in just about every other reasonably modern language
Would love to see you work through this turning it into a state-machine etc, definitely something I feel would be useful to watch the process of along with the logical steps to move from this to a well structured program.
I loved this video so much I went in search of the follow up thinking this was an older video, so sad I have to wait.
y'know, when all is said and done, setting stuff up with gcc and make is much easier than setting up a project in visual studio
This is called an "amalgamation." It is what you historically did to get the benefits of link time optimizations - the compiler can "see" everything, everywhere, all at once, making it "know everything". The full benefit only kicks in if you make all the stuff static - it can be sure that nobody else needs it, so it will know that it can recklessly inline tons of stuff that is used once. The other way is to give a compiler option that tells it that it's all one source file `-fwhole-program`. To be clear, I agree with your criticisms, 100%.
Just a few minutes in, I just wanted to note for what it is worth "#pragma once" although it is widely supported it is not part of the official C++ standard and if you value the portability of your code or would prefer to strictly adhere the C++ standard not compiler implementation you should avoid using pragma and use the standards conformant "#ifndef, #define, #endif".
by that moment if your compiler does not support pragma once, you need to think about changing compiler
(unless shakled to exotic platform)
A state machine video will be great!, I will also love to see a "To OOP or nope, thats the question" video. Particularly when-yes/when-not (environment, kind of proyect, etc) kind of approach. BTW, I'm an embedded programmer (for hobby).
Sometimes I don't understand the world anymore... Somebody can code a game clone that looks that good, but then they don't know how to use Git???
Im gonna assume they were just trying to give cherno enough content for a lengthy video 😅
I have multiple colleagues who develop software for unmanned aerial systems which have specs that PCs had in the 80s and they are damn good at it, but their git repositories? Oh boy.
They are 2 unrelated skillsets. Sure, they sometimes go hand-in-hand but someone who's good at git can be terrible with code, and someone who's good at code can be terrible with git
If you code just for yourself, as a hobby, there is no real need to use Git (or similar tool).
"use this maingame v2 backup fixed.cpp" is a perfectly valid filename.
I would say the lack of experience does show in the code (since everythings in header files lol), however this guy is truly great if he can navigate that code and write such a game. Just inexperienced for now...
I've cloned Pacman for multiple platforms, including various mobile phones, microcontrollers, and Javascript. There are a few gameplay things that are missed here that I noticed immediately:
Key-presses/joystick input should set the direction Pacman should try to move in next frame. If it cannot move in that direction it will continue in its current direction. Also it should never stop moving unless it hits a wall (and has no pre-loaded next direction). The ghosts actually have the same logic. Ghosts never reverse direction, unless a power pill has just been eaten.
The ghost's eyes should point in the direction they're moving
Pacman should stop briefly after eating a ghost, while the sound effect plays - it just make it feel better :)
In this game the pills are being eaten before Pacman is actually centred over them, which is not quite correct - he should be centred over the pill before it's eaten as it feels better again :)
The ghost patterns are wrong, but honestly I don't care about that. In the real game each ghost has its own logic.
Nice submission though, keep it up!
Even in the original, Pac-man doesn't eat the dot when centered. It's just eaten as soon as he enters the tile that contains a dot, similarly to how the ghost detection works. If you go frame by frame, you'll see it disappears from inside his mouth.
+1 for the Git/Github tutorial! Hahaha
I'm a beginner and have been messing with it a little bit but could definetly use a "good practices" video on that :)
One thing I do that I wonder what you think of is I specifically put single-line if statements in (the existing style at 18:00) if and when I'm not going to put the brackets in. If the condition and code block is too long to go on a single line, it gets brackets. That way I never come along while debugging and go "hey, I'm just going to put a print statement here to see whats going on" and immediately get a compile error because I forgot the brackets.
But one other thing, this is code after my own heart. Not that I would even begin to write code like this today, but I remember when I was but a tadpole who didn't even _begin_ to understand why you needed separate files for things, why you would static (not that static does anything if you only have one cpp file), or why nesting to infinity is bad. Back then, I would've written something that looks a lot like this, and I'm sure you'd have given it just as much shit, rightfully so.
Another way to avoid the double break is to move the update loop into a function and use return.
also surprised me. return is the only right way in this code situation
23:00 better use guard if statements to reduce indention
"you KIND-OF just combine it all into LIKE a single file, a single KIND-OF c++ file,
that then you send to the compiler as LIKE a single translation unit.
so that is effectively KIND-OF of what we have here because these are the only KIND OF source code files"
this is way too much 'kind-of' and 'like', so esp. for people who want to learn, it gets really hard to understand what the message is about - kind of :)
files are files, if they are 'kind of' files a compiler will spit out lots of errors, or 'like kind-of' warnings
kind-a-like-a
Agreed. The guy just blabbers and blabbers and doesn't really teach anyone anything.
Love the review! ❤ I 'll wait with anxious the state machine
I have been programming for 30 years, and I still start my C programs in a similar way...
I'll start out with one file main.c and then I'll add a header when I start to factor out chunks of code. But that header will contain the functions that I'm stripping... And then another file or two in a header... And then after about the fifth or sixth finalize break it all down into headers and c files. Absolutely drives one of my friends completely batty when he sees me doing this. It's like calm down most of my coat is a spike I don't care what happens to it next week :p
C compiles so fast that you don’t even need a source files half the time. (Assuming you’re programming in an efficient way)
"I will not talk about the header file issue here, I put it in a separate video"
*Continues talking about header files*
It took 13 minutes to get to the main function.
I think these code reviews are super interesting. But often the discussion gets stuck at a trivial point and it get repeated for 10 min and then video is almost over. More interesting to move on to more interesting part of the code, after point was made.
can anyone tell me that what colour theme he is using pls?????
17:20 In Java you can label loops and break out of the outter loop using the label. (...basically a goto).
Does cpp support this?
No, and personally I'm glad it doesn't because it's a convoluted solution that basically just puts goto into the language anyway.
Unity builds are actually pretty great; faster compile time and less files to manage. Personally though, I'm not a fan of using header files for unity builds, or for "single-header" libraries for that matter. I'm of the opinion that if you plan to include declarations along with definitions, then you should put that into a .c/.cpp, not .h file. That way, if someone else happens to take a look at the project, its abundantly clear that what you're doing is merging source into a single compilation unit. Even for just that embedded data (char array), I would have that as a separate .cpp file that gets included where necessary, but that's usually because I have a program I made that converts images and binary formats into C source that can be embedded into a project.
i love single header libraries. this way I have single entry point into my library
14:18 enum is int sized (4 bytes), unsigned char is 1 byte
Great video! I'm excited for the state machine.
Yes that will be great too.
I think a video on creating a visual studio solution for a codebase would be very useful
I think this very easy and understandable architecture, and if you write some simple small project like this one, you do not need to divide it into separate translation units
i written one plugin once in the same way )
My note: You have to declare all of your header files into a different cpp program and then declare .hpp inside it to make use of it not only in a single cpp file but also in other files....also as cpp has a top down aproach, it becomes compulsory to write the cpp header in an order,so it is probably not good to write headers inside a single file
Thank you for such great content! Can't wait to see this exciting state machine approach!!
At 17:00, the dreaded 'goto' statement. A reminder to the computer science zealots, branch statements are used in assembly all over the place by the compiler, aka a 'goto' statement. I use them when it simplifies the code, like in drivers, etc.
I won't cancel you. 😆
Would you want somebody working on your own systems (not you yourself) to have this energy when they work on your codebase?
Just because assembly has the equivalent of goto, it doesn't mean your higher-level language should use them. If you want to use all the features of assembly, program in assembly.
@@merseyviking yes it’s far easier to exit a complex state environment with overly complicated if, then, else, flags to break out loops. I only use a goto statement when it’s deliberate, makes it easier to read the code. Your argument is rubbish. I’ll code the way I do, you do you . I don’t care.
@@dysfunc121 I have absolutely no idea what you are talking about.
@@glee21012 I'm asking would you want people working under you who do not know everything you might to use it. You had absolutely no idea what was meant? Even if it wasn't clear to you, it's plain English, how do you solve hard problems when you do not understand?
yes cherno can you please make a detailed video on how to make vs solution if you only have code files and can you also explain how the cmake works and to make a vs solution from cmake as well
Yes definitely interested in state machine and improvements to this project.
Doing a walk through our setting up a VS solution is a very good idea!
He did one already
I'd like that too. Since he asked for this in the video.
At 21:31 inside Hazel::Main why not use a unique pointer for Application?
Why did you decide to change from a string to char array ? 6:32
A detail, you really shouldn't prefer to use pragma once over ifndef. Ifndef is in the standard while pragma is compiler dependants.
True, though since gcc supports it I find it an acceptable method. Basically, any feature that gcc supports, since it's free and open source, I find acceptable because literally anyone can download and use it on nearly any platform, and there are cross compilers for gcc that can target some old and strange platforms which other compilers ignore.
Please name a compiler in industry use today that doesn't support pragma once. I'll wait.
Pragma once is literally better supported than a lot of actual C++ standards.
omg, that was actually the first time i ever saw a good reason to use goto!
It is a bad idea to put a method outside of the class but inside the header file. A header file should be able to include several times. Putting code outside of the class in the headerfile makes that header file essentially a code file and it can only be included once in your program. This is not a problem for this program since there is only one source file but normally we would rather have multtiple source files.
Putting the code of the method in the class itself makes the method inlined and so it can be included in multiple files. You can achieve the same thing by writing
inline RETURNTYPE CLASS::METHOD(ARGUMENTS) { CODE }
I.e. have the keyword 'inline' in the declaration of the code but again it is cleaner to just put it in the class itself if it is a small method. Larger methods should always be placed in a separate source file with the same name as the class - and it is usually a good idea to have one headerfile for each class and one source file for each class where you place the methods that are not inlined. If a class have only inline functions and no static variables, then and only then can you have a headerfile without a sourcefile associated with it.
24:55 - WE'RE DEAD! :D This is gold.
Very very interested in the state machine stuff!!! Please bring it on.
Finally the State Machine video.
I'm working on something same, so
Honestly I would love, you making this little project better;
Im looking forward to see your way of doing state machine. Please don't forget to do it :)
Wow this is just what I needed! I'm making a pacman clone myself.
Hi, Great content in the history of C++ as always. Just one thing, your volume needs ++ a bit.
Not a programmer here (not a professional one at least). I'm a 2D artist and i feel like the talk about different languages in different situations is close to me too lol. How you implement all the basics and sctructure your work to become something new, interesting and unique, that works for the thing you're building is close to any creative endeavor.
21:50 I don't get why you are allocating the Application pointer in the heap and deleting it in each frame
i really like those reviews)
You also sounded really angry after about 8:00 =)
P.S. i would really love to see you talking about state machines more because i've never heard of them
Nifty clone for sure & the possible ways to clean it up were well stated imo. & I'm Defo for the state machine vid.
Mans said "if it could be somehow optimized"
If the thumbnail is anything to go by then yes.
I’ve never programmed in C(++/#) but oh well looks interesting enough for me
When @TheCherno says "I will make a video about this" there is a 0.01% chance that its gonna happen
I'd love to see ECS architecture pacman
Great achievement without abstraction or scalability. Thanks for the video. This is very educational.
Yes is it absolutely worth doing a git tutorial, please and thank you
why const char[] instead of constexpr std::string?
Great! Did the State machine video got done?
State-Machine: absolutely!
The next dev on the job will thank you.
8:17 why shouldnt you use ifdef style include guards? i thought pragma once is non standard and ifdef include guards are the preferred way?
What do you mean heap is slower to access? Are you referring to cache locality?
"You know, you could technically use a GOTO here ... I know I'm going to get cancelled for this" 😆
I like the way they did their code with the header files its actually easier to read and find things rather than put a bunch of files everywhere and scramble to find what you are actually looking for.
Edit: 8:46 That is the point of why it was also used for only 1 file not sure why that is such a bad thing when the project just works
What are you using, where you explain certain things with drawings? Is it part of VS?
15:06 I can confirm that this style a corporate-compliance one. :)
@6:21 This is a compile time resource and should be an `inline constexpr std::string_view CharBoard{ ... maze here ... };`. Header files shout NOT have constant variables at all, only constexpr's
Not using C++ would be a way more effective compile time resource. Or at least using the least amount of C++ features you can.
Most C++ features are arbitrary and the compiler doesn’t really know how to properly organize it. Which is why it takes so long for any C++ program to compile.
@@colleagueriley860 Can you come up with facts? Every language has its pro's and con's but I've seen C++ compile to efficient code in my last 30 years of using it. Like any tool it just requires knowledge on how and when to use it best :)
Did you make a follow up video to this?
Hey, please do make a visual studio setting up/ configuration type video. I have always thought Visual studio was kinda complex but watching you use it I wanna use it but it would be great to watch how you have/set up a project.
I'm looking forward to the state machines video! Can you quickly mention all the ways to solve deeply nested code, as the one in Terraria? One common strategy I use is having guard statements.
Cherno, it would be nice to see you do a Git tutorial. I understand it enough to do some basic stuff but it gets real dicey real quick where I am unsure if I am going to screw something up real quick. Like I can do basic push/pull/commit.
Yeah, Git is a nightmare when used for anything more than you described. I've been struggling with it for two years since we switched from SVN.
Surprisingly this code is smart in it's use of simple approach. Kinda like it ngl. If I don't have plans for reusability or shared code base why not just stop jumbling with header implementation duality. It's heretic I know but I'm an engineer I am practical by nature. If you know better there's no problem if you broke the rules in my book. Btw if you use -O2 or higher optimization compiler might decide to break or goto implicitly by dead code ellimination optimization, so right usage but obviously dangerous so heresy. 😅
In general I'm not against header-only type projects, but the thing that makes me pale is the reliance on #include ordering. That's incredibly fragile.
Why put everything in headers when you can put everything in main
Really great job from the dude that made that clone! It looked really, like the original... which was his goal i assume.
Regarding header vs translation units... i fully disagree with your opinion about creating a translation unit for every .h or .hpp file, because this increases the compile time enormously, which is especially true for medium to large size projects.
I always do one single translation unit for the entire application. It doesn´t mean that all the source codes are stored in one file, no. Its still separated by separate source files, but the declarations and implementations are guarded by preprocessor defines (Single-Header-File-Library). In very rare cases, where one single header translation file wont work - then i would use a separate translation unit for such cases. For example, Box2D does not like this idea, so i had to use one translation unit separately for that or when i want to give other header files access to a common shared library, then i would create a seperate translation unit as well.
Yes i am aware that it will compile everything always - but this is still faster, than compiling hundreds of translation units. Look at unreal engine, it has thousands and thousands of translation units... hence require hours of compiling time, which is insane.
This is why you're supposed to use `make` or some similar build system. The initial build should be the only time that all units get compiled unless you make changes, and then only the units that you've changed will be recompiled. Just to be clear, `make` checks the timestamps of the files to see if the module has been edited since the last time it was compiled before it determines it should compile a given unit. So change one module and it'll compile just that one thing and it'll be faster than compiling everything at once. Maybe go watch Jacob Sorber's videos on how to write a Makefile as I think you could benefit from that.
@@anon_y_mousse I use primarly visual studio, so there is no make in there. Also I really hate build systems to the bottom of my hearth! The idea about build systems are great, but all the implementations - all of them are crap, period! And no, i used them all and still use some them unfortunatly (CMake and Make) - but only when i create opensource libraries/applications. In any other case, VStudio is just fine - it builts my stuff, even when it has ~500k lines of code - in one translation unit and it compiles in seconds, rather than minutes/hours.
@@F1nalspace Odd that you'd have that kind of experience with build systems. Maybe look into something like nobuild and give it a try.
@@anon_y_mousse There are unreliable, are over complicated, the syntax is always not intuitive and some are really slow, some built-systems are built on top of another built-system... O_o
No just no... thanks for the advice, but i stick with my method and use no build-system at all or at max, the build-system i am forced to (CLion -> CMake, MSBuild -> C#).
@@F1nalspace Still, I wish I knew how you had the problems you have, because I've had a significantly different experience.
Which font are you using for Visual Studio? Is it Cascadia Code or Cascadia Mono?
I want to start with java, but all the compilers are so odd, and strange, do you have a good suggesstion?
We definitely want to see you set up those files in visual studio
hey, what software did you use to draw on the screen at 10:02 ?
If you are going to write implementation code in your headers, put it into a #ifdef block and demand the user #define a macro before #including the header. That way, the user can #include the implementation in only one source file and still use the header in multiple compilation units.
I thought state machine too as soon as I saw the nested-ifs. Interested if you implement it via polymorphism or some other way.
You said Heap memory is slower to allocate and slower to access. Whats the reasoning by it being slower to access? I don't understand how thats true?
Allocating on a heap runs some internal operating system allocation algorithm, so it's obvious that it's faster than allocating (or "pushing") on the stack, which is basically an std::vector of fixed capacity that every C++ programmer should know how to implement, but I presume you already know that.
Now for the slower access:
Memory in computers is split into several different levels, the higher the level the more memory it has, but also the slower it is for the processor to read and write that memory. Simplified, the levels go like this (from highest to lowest):
RAM -> processor caches -> processor registers
Processor registers are very limited, with only 16 64-bit general purpose registers on modern machines. But they're the only way to modify the memory in the RAM (e.g. add two numbers and write them back)
In general all of your data _should_ reside in the RAM. However accessing your entire RAM all the time would be slow, and that's what the processor cache is for. Whenever you read or write memory from RAM to a register, the processor copies an N-byte (64-byte on modern machines) chunk where that memory you want to modify resides in from RAM to the cache. If the cache is full, the processor writes the chunk that has not been used for the longest time back to RAM and replaces it with the new chunk.
Whenever your function needs more memory than the processor registers can contain (and every function called within that function adds up to its memory requirements, so it's pretty much unavoidable), it saves its local variables on the stack. Because you're using the stack pretty much all the time, it's very likely that the data that is on the stack will be in the cache (this is also why you don't allocate arrays on the stack and use the heap or declare them as 'static' or 'thread_local' in C and C++, so they're basically "preallocated on the heap" for the lifetime of the program if you use 'static' or thread if you use 'thread_local').
But technically, if you use the data on the heap just as often as you do local variables on the stack, you should get the same performance, no? There are two problems with this approach though: 1. you need to store a pointer to access the data on the heap, while if it's on the stack, it's offset from the stack pointer can be computed at compile-time and no additional pointer is needed 2. if the size of your data does not align with some multiple of the size of the chunk that the processor fetches, then you waste some memory, since the processor fetches some data into the cache that you might not ever use in your program
Basically, you do not use the heap for anything that has constant size at runtime, for that you have either your local variables for the small things or the 'static' and 'thread_local' keywords for the big stuff.
Hm, instead goto you could just make function with all closing activities in there and call it with return statement after or I'm missing something?
RUclips preshowed me the first sentence of this video with the subtitles: "Hi guys, my name is China ..."