senpie

The missing puzzle pieces were lectures. I went back to this awesome C++ series and oh God I understand everything much better now. I need to hear the intonation of the voice when learning about something because it helps me get into the context and make correct assumptions that allow me to grasp the topic better. In the case of a book, the author types out the voice in their head on paper, and information is lost. Maybe he had a sarcastic voice while writing that example, but since I didn't hear it, I may take that example for granted and then get confused about future topics. I also like that in these lectures, because I would get confused on the parts where the lecturer or the chat got confused too. It gives me confidence that my idea, that something was off is verified by other people, and since I do not have enough expertise to figure out a solution on the spot they give an explanation anyway, which allows me to quickly proceed to other topics, without wasting of much time. Although, I still prefer to read the book in the beginning and turn back to lectures, when confused, so I can get most information. As a final word, I will leave these two examples which are different, and try to explain the concept of a template of a template parameter.

#include <vector>

template <typename T, template <typename> class Container = std::vector>
class Stack {
	Container<T> c;
public:

};

int main()
{
	Stack<int, std::vector> s;
	return 0;
}

where you can specify what type of container to use as an underlying data structure for the stack.

#include <vector>

template <typename T, typename Container = std::vector<T>>
class Stack {
	Container c;
public:

};

int main()
{
	Stack<int, std::vector<int>> s;
	return 0;
}

Also, note that when I tried to run these examples, they seemed to work on compilers from version C++17. Although, it didn't run with my Macbook's compiler, so I have no clue what can be the problem. That's it for today see you tomorrow!

Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates. Templates.

This example made a bit more clear, how to think about one aspect of templates:

template<typename T>
struct S {
    template<typename U>
    void f(U u);
}

<template T>
<template U>
void S::f() { /* ... */ }

Therefore U is not part of a class template but is a separate template. Cannot formulate my ideas about templates yet. Need more research. That's it for today, see you tomorrow!

More templates! I cannot explain why, but it's a fundamentally harder concept for me to grasp and understand, than anything else in the programming world. You can achieve similar behavior to templates using char* and interpreting bytes, or using macros with less type safety. Or you can have a single base class to represent them all. Idk, someone may read this and be disgusted with what I am saying, because it is disgusting. However, those concepts are simpler and easier to understand what to expect from them. With templates ( generics ), however, I do not know which part is handled by the compiler, which part is not, or how the compiler processes all that template soup. There were examples, that used three template parameters and I do not understand how the compiler is enforcing correspondence between them, or otherwise, it does not at all.

/* Seems fine, we declare List with two template parameters T and Allocator. */
template<typename T, typename Allocator>
class List;

template<typename T>
class Link {
    /* Okay this is so weird. The concept of the link is to hold the value of a list, therefore, why the hell on earth does the List here have parameter U and not T. Doesn't that mean that we can have a case where a list of ints can have links that hold strings, why would we need that? */
    template<typename U, typename A>
        friend class List;
    T val;
    Link* succ;
    Link* prev;
};

templates<typename T, typename Allocator>
class List {
    // ...
};

So many questions with this example in this book. However, this is it for today, see you tomorrow!

or may I say

template<typename D, class Topic, typename V>
#DDaysOfTopic: Day V

Today, I started on the painful adventures of templates, or generics if you have a Java background. Usually, I have a rate of 20 pages per hour while reading this book; today it dropped to 8 pages. There can be two reasons: first I am tired and need sleep, second, this topic is absolute bottom/back/behind/insert the vulgar word here. It has always been one of the topics, that confused me the most. However, I have to give them credit that it sucks less, than the Java implementation, because the latter one was absolutely horrible and confusing. Yes, I am talking about erasure. On the other hand, I can see the way it can optimize generated code for higher performance and stuff. Fortunately ( or not ) I am not a AAA company yet, so I won't make heavy use of that. Nevertheless, I should quit ranting and talk about the content. Basically, templates are heavily used for the design of standard library facilities in C++, where it makes the most sense to apply. For readers, who are not familiar with its concept, it allows to reuse of the same piece of code for different types. That is if my soap can clean a face, then I can say soap and re-use the same soap to wash my insert the vulgar word here like so soap. Since there are many body parts, I do not want to define soap for each one like class FaceSoap, class TorsoSoap, class LegSoap, but I can have only one #template<typename T>class Soap; and just use that. It is also compile-time safe. That means no information will be lost if you decide to use certain soap only for your butt and then accidentally use it for your face and regret it later, the compiler will warn you in advance that you intended to only use it for your private ( or so private ) part. That's the simple concept behind it, however, I do not have much processing power to explain it further now, so thank you for passing by, and see you tomorrow!

With a better understanding of inheritance and polymorphism in C++, I dived deeper into the topic of RTTI (Run-Time Type Information). Basic hierarchy navigation is done with dynamic_cast or static_cast. It's also interesting to apply this knowledge to recover interface, when for example writing a deserializer, that would read object info from a file and then make objects based on that. I did a revision on double-dispatch, which I think is a neat little trick to achieve a type resolution based on two or more types. However, we are in this weird relationship with Bjarne of calling each other out, me calling him out in this blog series, and him calling me out in the book. Why? Because in the book it says “If you consider this(double-dispatch) elegant, you need to raise your standards.” Anyhow, he then showed the visitor pattern which I do agree looks quite cool and probably a better alternative, than double-dispatch in most cases. It also talked about the pitfalls of RTTI and when ( 90% of the time) it should be avoided. There's also the typeid(expr) function, which can be useful for diagnostic output, but should be avoided to make horrific switch statements. That's it for today, see you tomorrow!

Today I've definitely spent more time than I intended on this stuff. The thing is that the multiple inheritance in C++ is far more powerful than in Java, that is you can express more stuff, however, the downside is more complication and confusion. I felt like I should not just assume it works the same way as in Java and progress any further in topics until I fully understand virtual inheritance in C++. Therefore, I spent almost my whole day studying it. By virtual inheritance, I do not refer to virtual methods, but rather to virtual inheritance, that is when you write

class Grandma {
    int g = 1;
}
 
class Mom : public virtual Grandma {
    int m = 2;
}

class Dad : public virtual Grandma {
    int c = 3;
}

class Son : public Mom, public Dad {
    int s = 4;
}

The book did not go into much detail explaining how this stuff works, however, I cannot use it if I do not fully understand it. It would just tell that Mom and Dad share the same Grandma object in case of virtual inheritance, which made me think it is superior to the regular inheritance since it would take less space and solve the diamond problem. To my surprise sizeof(Son) in this case would be 40, whereas if the same code would have been written without virtual it would only take 20 bytes, and because virtual adds a level of indirection it is slower. Now, I understand a lot about multiple inheritance in C++, however, I am sure I will forget it soon, the same way I learned it. Hooray! If anyone would want to learn more about this stuff, here is a high-quality video, that explains it in detail ( sorry it's in Russian ).

Multiple inheritance in C++ can create crazy brain-draining graphs with little effort. The fact that OOP is marketed as a natural way for human thought, therefore empowering the programmer to create complex systems by breaking the program into small classes and setting up relationships between them is so untrue when you have such a big scale. After a period of development, the graph is not Shapes, Circles, and Trinagles anymore, but it's so complex that it's almost impossible to comprehend and picture it in your mind. Whereas it could have been solved much simpler if OOP was avoided overall. The Car does not inherit Wheels and Engine, it consists of them. Reminds me of one article, that I keep dear to my heart. It describes different types of programmers and defines a mental condition called “Brain OOP”, where Java, C#, and some C++ developers try to apply patterns to every part of their program and bloat it with abstraction. An intensive x86 assembly course is advised.

Today's topic covered different uses of inheritance to improve the design of the system. Not gonna go deep since it was standard concepts like multiple-interface inheritance, which is also present in Java. Then, it was the factory pattern example. The concepts were explained in the graphical user interface design. Since I have a long history with UI programming in games I was curious to see if there are other efficient ways of implementing stuff, that I haven't thought of, or ones that are C++ specific, however, it was pretty standard stuff. The only different thing was the use of a multiple-implementation inheritance, which is not allowed in Java and I believe is pretty clunky in C++. The author highlights that it can be confusing and hard to use it, however, he at the same time brought an example of a once-existing satellite program that was convenient to write using multiple-implementation inheritance. I would argue it would be better to use aggregation in those cases, although I am no C++ pro and cannot say for sure. That's it for today, see you tomorrow!

More C++ craziness. The access modifiers were kind of obvious in the beginning since it's similar to Java, however, it quickly got confusing when I learned that in C++ you can specify access to base class when deriving from it, such as class B : public A { /* ... */ }, class C : protected A { /* ... */ }, and class D : private A { /* ... */ }. It got even more confusing when I learned that you can combine the using keyword with all that shit. Nevertheless, it was still bearable until the author started talking about pointers to members and methods, which completely clogged my brain. I hope these are simple topics and the reason I cannot grasp them is because I slept ~4 hours in these two days and ate almost nothing. On the other hand, it can be confusing, because those concepts were non-existent in Java, so I do not see obvious application. Either way, I think I will revise this chapter later to be confident in it. That is it for today, see you tomorrow!

Who-hoo! Back again to the C++ book. Today's topic was “derived classes”. We looked into what simple derived classes can do, that is to simply add new data fields to the base type, and also when you add new functionality. Member functions were explained in great detail to explain when you would want to override them in the derived class. Two methods of overriding function behavior were presented. First clumsy ( fine for a small codebase with a single developer ) solution was to store a type field and slam all the logic implementation into a single function, where based on a type you execute certain code and use static_cast to access fields. Second way ( much neater ) was to use virtual function. Also, when using virtual functions you can use helper context keywords such as override, final to explicitly tell the user what they can and cannot do with a method, such as overriding, or prohibiting to overwrite the method in base. Furthermore, the usage of using keyword was discussed since classes are technically namespaces anyways and you can bring so functions from another class, or even inherit constructors. Finally, the return type relaxation a.k.a covariant return rule was explained, which I belive is same as in Java and is quite useful if you want to return derived type in a derived method. That's it for today, see you tomorrow!