senpie

Today was a bit different. Instead of reading the book I decided to do some of the exercises included. They were small, simple, and boring and wasted my time. However, one of the exercises was interesting. It asked me to find a real C++ production code and check how many casts they are using and which variants. One great candidate I found for that exercise is RBDOOM-3-BFG, which is a forked ( and improved version ) of the original Doom 3 BFG Edition source code published by id Software. I got a bit carried away and was reading the source and I was surprised at how clean it was ( expecting any production code to be disgusting ), at the same time a feeling of nausea came over me from the size of the codebase and thought of how much I have yet to learn. I tried to build the project on m1 macbook, although there was an issue with Vulkan SDK and I couldn't figure it out. However, I am used to not being able to build anything on mac xD On the other hand, later I tried to build it on Windows and it was a couple of clicks, which is sad, that only windows receives such great support and care. Finally, I finished chapter 16 covering the basics of classes in C++ and I really like the concrete classes. It's one of the few things I like about C++, the ability to define your own type for abstract ideas, that will be used frequently with almost zero overhead. Memory for them can be allocated on a stack and it's simple to move and copy them. That's it for today, see you!

Today, I have spent more time on this series than I planned, definitely more than an hour:) So “why?” you may ask. Because I tried to implement the calculator example from the book on my own, using the newly acquired knowledge on break coding into namespaces and separate files. Yet again, it confirmed my beliefs, that only reading or watching tutorials is not enough, and equivalent practical exercises should be done. It seems trivial on paper, but when I tried to implement it on my own I started to question what I had learned and remembered and had to constantly refer back to the book. However, now I am more confident in my skills to write a calculator language off the top of my head in C++. Also, I felt kinda wrong to only code without reading a bit, considering it was the beginning of “Part III: Abstraction Mechanisms.” Most of the chapter was familiar since I know OOP concepts. Although, there were some C++-specific stuff, that I did not expect to exist. First, it's the idea of the const method, which is a mechanism to guarantee, that method will not modify the member property of an object. However, seems that it does not work for pointers and you can freely modify an object member stored with a pointer. Although, C++ wouldn't be C++ without some weird features. What if you define method const, but still want to modify a property, you declare it to be mutable, absolutely makes sense. Another interesting and unnecessary thing was that if you define the method inside the class's body, then it will implicitly be defined as an inline function, so if you do not want it, then that method should be defined outside. Finally, did I mention how much I hate implicit conversions in C++? For sure I did. Well, it turns out that it's also true for constructor arguments and they even have a keyword for that, just put explicit in front of your constructor, and magic, now no implicit conversion will happen. That's it for today, see you tomorrow!

Finally, it covered linker and how to divide the program into separate headers and file units. Again coming back to my compiler design class, I can safely say that linker is an ass. There are so many cases when linking can go wrong and you will not even be notified until the program crashes somewhere silently. And the worst is that the author himself says, “Mostly the major linker implementations handle some of those cases”. Mostly? Are you serious? However, having a safe linker is contradictory on its own, since it is a core issue. Nothing to say much for this chapter, except that you should definitely break the code into several files and header files in case the codebase becomes large. You can even go further and split the header file into two header files, where one interface is for the user and another _impl.h, for the developer implementing the interface. Make sure to have only one definition in a program, use guards, but don't overdo with #includes. Also, while writing this I felt, that I am starting to type like the author of the book. Don't do this, remember to do that, pay attention to this, hopefully, this will not break, etc. I will probably switch to some other topic for a couple of days to give my brain a rest from this book and come back. See you!

Today was rather swift, but alas boring. It was about the namespace in C++. Since the book was written a while ago, it is so funny to me, that he introduces the namespaces as a facility to divide code into logical units as with modules. He says that by combining the namespaces with other facilities of the language we can achieve clean code as with modules. What's so funny, is that they have added modules in C++20. Pffff, it only took them three standards to add modules, showing what of a trash pile C++ is. Just do it properly and do it once dammit! I am kind of developing a pleasant reaction every time the author mentions the horrible C++ codebases he faced throughout his career. Makes me think, that he deserves it since if he did everything properly from the start, he wouldn't see that crap. Another fun fact, if you read my blog post and I didn't mention an error in a code example from the book, then assume that I did not type it out myself and took it for granted. Because, with a 100% rate, every time rewrite a book code example it fails to compile with the stupidest mistakes you can imagine. Gives you an idea of what we deal with. Finally, a tiny, code snippet ( from the book ) to help me get my point through. Bjarne has this proud tone every time he talks about a feature, like how easy it made the life of developers. However, he completely misses the fact that the illusion of “convenience” hides a dozen implicit rules, that programmers have to memorize and are easy to mess up. Quoting from the book:

namespace Chrono {
    class Date { /* ... */ }

    bool operator==(const Date&, const std::string&);

    std::string format(const Date&);
    // ...
}

void f(Chrono::Date d, std::string s)
{
    if (d == s) {
        // ...
    }
    else if (d == "August 4, 1914") {
        // ...
    }
}

We look for the function in the scope of the call ( as ever) and in the namespaces of every argument (including each argument's class and base classes) and do the usual overload resolution. So, such an innocent “==” operator has this grand set of rules working together, so you will not have an extra line or two, so fucking novel!!! Anyway, that's it for today. I should keep my cool, or else can't survive in the world of C++. Thank you, see you tomorrow!

It gets more and more intense. The first part of the chapter explained how cool are the exception-handling facilities provided by the language in comparison with “old” methods. Then it goes deeper explaining specific syntax and usage of try and catch in different parts of the system. What he does later is phenomenal! He tries to use the concepts discussed in this chapter to implement a super safe vector container. He writes the first example with try and catch, then proceeds to explain that it's not good and rewrites everything to use RAII ( Resource Acquisition Is Initialization ) and says that way iS mUCh ClEAnEr ThAN UsInG exceptions. Although, he says it's bad to directly work with memory since we are not ill-mannered C developers and should use high-level facilities, he still uses a bunch of weird ass low-level functions, without even explaining them and assuming we will probably understand from the name and the two-word comment next to it? Anyway, this was a rather heavy and confusing chapter for me and I am glad it's over. Maybe someday I will revisit it, and everything will click, but right I do not want to be spinning my wheels on this. That's it for today, see you!

100DaysOfGameDev: Day 12

Can we agree at this point that C++ is a joke thing? However, that is a rhetorical question, since most programmers already agree that it is, including senior C++ developers. The only people who consider it good language are C++ devs with inflated egos who do not want to face the truth, who spent years of their valuable time making projects in it and couldn't think of something better. As if the ballon encapsulating the meaning of their life would pop and cease to exist, opposite to the big bang that materialized it in the first place. I am studying C++ to make games, and I will not miss a chance to rant about it and or tell how bad it is. In practice it is solving the issues, that it created in the first place. Anyway, it got philosophical very quickly, and I wait for the day when there will be better game development facilities to make games without much risk. And returning back to the topic of the game series! Today's topics were exceptions and error handling in C++. It talked about how superior are the exception handling facilities in C++ with try and catch, compared to other means of handling exceptional cases in the program. It argued that everything else sucks and the world would have been a better place if everything was done this way in the beginning, at the same time ignoring the fact, that the world would have been a better place without C++ in the first place. Anyhow, it mentioned the “other error handling ways” mostly done in C, arguing that if are working with archaic(irony) software, that already has its own exception-handling mechanism, then we have to adapt. It showed three popular means of doing it that are used by most applications and talked about each of their advantages and disadvantages. That is it for today, this chapter is rather large, so I will also cover it tomorrow. See you by then!

Another day, another rant about the book, but before that I will list the topics. Today's topics were also about functions mostly about the default arguments ( actually nice ), complex rules of function overloading with all intricacies ( probably will forget in a couple of days ), thoughts on pre and post-conditions for functions, pointers to function ( the old C way ), macros, conditional compilation, predefined macros, and pragmas. The default argument facilities are a nice feature I missed in Java, since ofc I can write three overloaded versions of a function, but it's sometimes nice to just have one function with default values. The function overloading is so complicated, because of the billion conversion rules, that C++ has, which I hate. Bjarne also talked about the pre and post-conditions of function, which basically means the valid input we as programmers expect to receive for valid output and ways to handle it. You definitely would not want to receive negative numbers for a function calculating the area of a rectangle. Nothing to say about function pointers, besides how little shit the author gives about code examples in the book. Again I hope that it is the early chapters and later chapters will be better. The issue is that as you read through the book everything makes sense until you try to run that code and see that it fails. Fourth edition, btw. That same person then hates on macros calling them a sign of a poor programmer and program ( he should check his code examples ). Although I cannot disagree, that macros should be usually avoided, besides the usage of #ifdef, which cannot be avoided. That's it for today, see you tomorrow!

So many ways to write a function it's crazy. We have an inline function, we have constexpr function, we have a function where you don't modify the arguments, we have functions that take by reference, some take by value, and we have a special case for taking array for argument, don't forget about the std::array_initializer<T> and if you wanna have an unspecified number of arguments there's a badass option of int prinf(const char* ...) meaning a pointer to character and more arguements. The last one comes with three horsemen of the apocalypse va_list, va_start(), and va_end(), truly dreadful. That's it for today folks, tomorrow expect more content about functions. Gotta remember everything 'cause I value my feet. See you!

Another day, another chapter. The lambdas in JS and Java seemed much simpler, than in C++. One differentiating feature, that pops up to the eye is the capture list [], which in the minimal lambda syntax []{}; allows the user to access the variables in local scope with precise control. You can specify to either access the variables by reference or by value ( which will supply the copy to not modify the original value ). Another interesting thing about lambdas' is that they don't have a specifically defined type, rather they are called closure type and represent function objects, where you overwrite the void operator() const function. No two lambdas have the same type. The second topic discussed the chapter was casting. Both the C++ way using, staticcast, reinterpretcast, constcast, dynamiccast, the construction of value of type T from v T{v}, and the C way with good old (T)v. Almost always the C++ mechanisms are preferred over the C way when writing a C++ code xD However, it will be even better if the 'bad-behaving` casting is avoided altogether. That's it for today folks, see you tomorrow!

Today's topic covered logical operators, bitwise logical operators, condition expressions, increment decrement, free store and memory management, lists, and a bit of lambda expressions. The book once again went over the idea of logical operators such as && or ||, emphasizing that they are evaluated from left to right so you can use the idea of shorcircuting the expressions and execute second condtion, because first one holds. Not much interesing stuff for bitwase logical operators, although the comparision between !(not) and ~(complement) was a good reminder that they are not the same. !0(not zero) would mean true, however ~0(complement of zero) will flip the bit pattern, which in two's complement is the value -1. Nothing new for conditional expressions ?:, although should remember that they are a big deal in constant expressions. For increment and decrement, I found this program to copy c-style strings, quite amusing

void cpy(char* p, const char* q)
{
    while (*p++ = *q++);
}

For the free store, a.k.a. heap, a.k.a. dynamic memory, it was good to remember that you wouldn't want to call delete on user defined types twice since it may call their destructors twice and result in some crazy stuff. Also, the syntax for void* operator new (size_t sz, void* p) noexcept; was quite interesting. It basically allows you to supply the pointer, where you want to place the new object, which I never though about. Finally, initializer_list seems intricate topic, or I am just sleepy. Important thing to remember that members in initializer_list are imutable, so you cannot move operation and instead have to use copy operation. An example is:

void f()
{
    initializer_list<int> lst {1,2,3};
    cout << *lst.begin() << '\n';
    *lst.begin() = 2;   // error: lst is immutable
    cout << *lst.begin() << '\n';
}

That's it folks for today. Since I wasn't able to cover lamba expressions fully, I will include it as a part of next post. See you and have a great day/night/sleep!