Friday, November 02, 2012

An introduction to vectors in C++

For several years, your uncle Bob has been keeping track of his finances in the following format:
4
potatoes       1800
carrots        1300
beer           2500
tomato_sauce    500
The first line always has the number of items, and every next line has a single-word product, some spaces, and then the amount it costs. However, being a little eccentric, uncle Bob decided that he'd like to sort all his files, and that's where you come in.

You've been given the task of writing a program that reads a file, sorts the contents, and then writes them back in a similar format. For simplicity, we'll use std::cin and std::cout for reading and writing, and we won't worry about how many spaces there are between the item name and the cost. We'll start by reading the number of items and printing it right back out:
#include <iostream>

int main() {
int num_items;
std::cin >> num_items;
std::cout << num_items << '\n';
}
Nothing new here. We'd also like to have a data structure to store the item name and cost in:
struct Item {
std::string name;
int cost;
};
We now know our program will look roughly like:
#include <iostream>
#include <string>

struct Item {
std::string name;
int cost;
};

int main() {
int num_items;
std::cin >> num_items;
// A: Where do we put the items?
for (int i = 0; i < num_items; ++i) {
Item item;
std::cin >> item.name;
std::cin >> item.cost;
// B: How do we store the item?
}
// C: How do we sort the items?
}

Dynamically allocated arrays in C++

For several years, your uncle Bob has been keeping track of his finances in the following format:
4
potatoes       1800
carrots        1300
beer           2500
tomato_sauce    500
The first line always has the number of items, and every next line has a single-word product, some spaces, and then the amount it costs. However, being a little eccentric, uncle Bob decided that he'd like to sort all his files, and that's where you come in.

You've been given the task of writing a program that reads a file, sorts the contents, and then writes them back in a similar format. For simplicity, we'll use std::cin and std::cout for reading and writing, and we won't worry about how many spaces there are between the item name and the cost.
We'll start by reading the number of items and printing it right back out:
#include <iostream>

int main() {
int num_items;
std::cin >> num_items;
std::cout << num_items << '\n';
}
Nothing new here. We'd also like to have a data structure to store the item name and cost in:
struct Item {
std::string name;
int cost;
};
We now know our program will look roughly like:
#include <iostream>
#include <string>

struct Item {
std::string name;
int cost;
};

int main() {
int num_items;
std::cin >> num_items;
// A: Where do we put the items?
for (int i = 0; i < num_items; ++i) {
Item item;
std::cin >> item.name;
std::cin >> item.cost;
// B: How do we store the item?
}
// C: How do we sort the items?
}

Tuesday, August 21, 2012

My go at component-based design

I recently started working on a game.  Having read a bunch of stuff about how object-oriented programming isn't very suitable for modelling game worlds, I decided that a component-based design would be better.  However, while there were many articles talking about the general ideas of component-based design, I did not come across all that many resources that presented code for a framework in which to create entities, components, and systems in.  Needless to say, I decided to write my own.

The overall structure I decided to follow was one similar to that described in T=Machine's series of blog posts on this matter.  Everything in the game world would be an entity, and an entity by itself would have no data.  All data related to the entity would be stored in components, which were to be treatable as free-form bundle-of-data classes.  Finally, there would by systems, which would go through components of a given type and update them as necessary.  An extra constraint that I had was that I wanted all of this to be type-safe with minimal runtime type-checking cost; I wasn't sure that was actually possible, but decided to give it a  try anyway.

At the moment, I have some code that does all this.  In order to keep things together I added a world class to the mix which would have the role of managing the set-up of all this, and then call the systems as necessary.     The ownership and awareness can thus be described as follows:

• The world owns all entities and systems, and is also aware of all components.
• The systems are aware of the world.
• The components may be aware of the entities that they are part of.
• The entities own their components.
This can be visualised as follows, where a normal line means ownership and a dotted line means awareness:

The lifetime of a game goes as follows:
• A world is created.
• All entities and components are created.  Entities must be created before their components are, but for the rest things can happen in any order.
• The systems are set up.
• The world is set to execute, each step invoking all systems.
• When the game has been completed, the world is destroyed, which in turn destroys all entities, components and systems.
At the moment, every step must be individually requested.  It is therefore possible to access parts of the world from outside between steps, in order to (for example) save or load the game.  I'm not sure if I prefer this way or having everything done through systems, but it's definitely convenient to keep it this way for testing purposes.

The code can be found on Github, with this post talking about the state of affairs after this commit.  I'll probably do another post that will focus on how this system is implemented.

Tuesday, July 10, 2012

What's wrong with RTFM's C++ tutorial?

There has been too much ink spilt on this already, as even the author admits, so I don't feel particularly eager to point it all out again.  However, to preserve yet another reddit reply of mine:

• The tutorial has some technical mistakes. For example, describing sizeof as a function is very wrong. (He also abuses "keyword" for just about everything.)
• There is no context. This is my single biggest gripes with basically all tutorials: they fail to explain features in the context that they are used in. Functions are not just places you put code in so that it isn't all in one place: they are abstractions. Same with classes.
• Further about absence of context: pointers are (as is painfully common) not explained with proper context. The way the tutorial shows them does not represent how they're actually used, and makes them look useless.
• Important concepts for practical coding (such as const correctness and RAII) are not mentioned enough. Oh, let's not forget exception safety. When he does mention using const as much as possible, he for some reason decides to announce it as an object oriented programming thing (and goes on an utter tangent).
• As far as I can tell, he doesn't introduce using standard containers.

So, to summarize: he says things that are false, he fails to say things that are true, and he says things in the wrong order.  Just say no.

Monday, July 09, 2012

What's wrong with The New Boston's C++ tutorial.

(I originally posted this on Reddit, but decided I'd like to keep it around for the next time someone asked.)

He does not have context. Error no. 1 of all C++ tutorials online, and one that kills learning immediately. Someone who is starting to learn C++ should not have the syntax presented to them without explaining the purpose of the syntax; a lot of newbies don't understand this, and therefore consider that the tutorial is good because it is "simple".

Cases where he does this:

• Functions are introduced this way. Notice that in videos 9-11, he does not show a single function that is actually useful. Having watched that, a newbie will not have a feeling for whether a function is appropriate or not.
• Videos 12-15 are similar, and are also horribly out of place. He introduces a bunch of syntax, doesn't use it for anything meaningful, then forgets about them for roughly 25 videos. Someone coming from Java may think "Oh, right, classes", and then (wrongly!) use them for everything. Someone with no programming background will once again go "Why would I want this?", and be entirely right.
• All this remains true for the introduction of if, for, while, and switch. There are a few examples, but they do not adequately cover the introduced material.
• Unary scope resolution operator? Seriously? Apart from the unary version being used extremely rarely, the sane advice to give is "don't shadow if you don't know what you're doing". Beginners do not need another bit of syntax shoved down their throats.
• Arrays. Oh hey, again useless toy examples. I'm noticing a pattern. Oh, and he fails to explain what happens when you pass an array to a function.
• Pointers, again, done horribly wrong. No use-cases, no demonstration of why indirection would be beneficial. Also, done far too early: he hasn't even covered std::vector yet.
• One last example, and probably the most egregious in the whole tutorial: he introduces inheritance, and then shows a daughter inheriting from a mother. No, I did not swap the two around. I'm not sure how he wrote it without realising that "every daughter is a mother" makes no sense.

I'll leave the rest of such cases for you to find, there's plenty more in there. There are some factual errors as well (see arrays), but I don't have the time to track those down right now. Comment away if you know of any.

So, once you've watched all his videos and have been taught a bunch of syntax, where do you stand? Turns out the C++ world has a reasonably large group of "misguided newbies" that, just like you, learned C++ from some 'tut' and now can write code that compiles as C++, but don't understand the core principles of the language. The code is bad (usually in the sense of buggy and hard to maintain), and is unpleasant to work with even for the original author. Most common omissions are:

• RAII. You know, that thing that makes C++ manageable. If you don't know about it, you are not good at C++. If a tutorial does not introduce it, it does not cover C++ well.
• In a similar vein, the rule of three.
• const-correctness.
• Exceptions, and more importantly, exception safety.
• What the standard library offers.
• That's not to mention things that are useful but not essential to use, such as templates, operator overloading (except operator=), lambdas, functors, and iterators.

So, no "Bucky isn't a bad C++ teacher" is simply not true. He is in any case a bad teacher, and the omissions he makes lead me to suspect he's not any good at C++, either. If he'd take down his videos, the internet would be a better place.

Tuesday, June 26, 2012

Updates on ports (CSTW, Rain-Slick 3)

As I've written before, I have ported Cthulhu Saves the World to Linux.  The testing went a little bumpy, but I've ironed out the remaining issues and the patch is ready for release at any moment.  I've gotten a very similar patch ready for Penny Arcade's on the Rain-Slick Precipice of Darkness 3, although I do need to update it for the latest patch.

Unfortunately, I haven't heard from Zeboyd Games since they forwarded a few testers my way.  I'm not sure if they're just busy with Rain-Slick 3 or are for some reason ignoring me, but I hope for the best.

I've also taken a brief look at Dwarfs!?.  The game uses XNA 3 and a lot of Steam interaction, which will make it a pain to port.  I may end up putting it off in favour of language development work.

Speaking of which, I have a really cool (and blatantly stolen) idea for a type system.  Will post more as I flesh it out.

Wednesday, June 20, 2012

A very brief explanation of const member functions

When designing classes in C++, one should keep in mind that there will be cases when your class is const, such as when it has been passed by const reference or simply when a const instance of it has been created.  It is therefore important to make sure that those operations which should be possible to perform on a const instance can indeed be performed.

As the C++ compiler does not know whether a member function modifies the members of the instance it is called on, member functions not specifically marked const may not be called on a const instance of the class.  The following, for example, is not allowed:
private:
int x_;
public:

int GetX() {
return this->x_;
}
};

void f() {
std::cout << p.GetX(); // Compiler error: calling member function on const instance
}
In order to state that a mumber function does not modify the object it is called on, we can mark it const by appending const to the signature.  The result for this class is:
class PointGood {
private:
int x_;
public:
PointGood(int x) : x_(x) {}

int GetX() const {
return this->x_;
}
};

void g() {
PointGood const p(5);
std::cout << p.GetX(); // works
}
As non-const member functions can do everything const member functions can do and more, one should keep all his member functions const unless they have a good reason to modify the instance.  This should be familiar from the way member functions should be private unless they are explicitly meant to be protected or public.

The C++ compiler makes sure that member functions marked const really don't modify the instance they are called on.  The following is illegal:
struct PointBroken {
private:
int x_;
public:
PointBroken(int x) : x_(x) {}

int GetX() const {
this->x_ = 7; // Compiler error: not allowed to modify this->x_.
return this->x_;
}
};
This protection can be seen as the implicit 'this' pointer being of type PointBroken const* const instead of just PointBroken* const.  This justifies another restriction: calling a non-const member function on 'this' is forbidden, as we cannot be sure it does not have side effects.  For example, consider the following code:
struct PointAgainBroken {
private:
int x_;
void SetXToSeven() {
this->x_ = 7;
}
public:
PointAgainBroken(int x) : x_(x) {}

int GetX() const {
this->SetXToSeven(); // Compiler error: not allowed to call non-const member function on this.
return this->x_;
}
};
Clearly, allowing a call to SetXToSeven would allow behaviour identical to the previous example, which is what we do not want to allow.

The compiler errors generated by these mistakes can be somewhat cryptic; fortunately, they are fairly distinct, and can be seen below.  Comment them out one by one to see later ones.