Some questions I have about C++

I’ve been getting back into the Arduboy, which mostly involved reading code. I’ve got a few inquiries about C++ that I’ve been wondering about. Most of this stuff is from Pharap’s 2d platformer demo (I think it’s good material on a lot of different concepts) but the questions are sort of general.

  1. why does using #pragma or an include guard matter?
    I see this at the top of every header file-and on other peoples code, usually some include guards too. I know exactly what these do; they include a header file only once in compilation. My main inquiry about this is why? What difference does it make? From my understanding, if you include a header file twice, it will get compiled twice, right?

For example:

 //header.h
    void function();

//header.cpp
#include "header.h"
    void function()
{
}

//main.cpp
#include "header.h"

Here, header.h has been included twice. So if you put for example #pragma once at the top, header.h would only be compiled once. But why is this still used for things that are only included once?

I’ve decided to put this in here as well since I think it fits in here, especially because of my inquiry about compiling, but would it be possible to solely use .h files for an Arduboy game? Usually .h and .cpp files are bundles together, but if you’re just declaring in the .h file, and defining in the .cpp file, won’t it make everything more complex? I think it’s possible to define something in a .h file too. I’m just confused about the whole thing, but I’m certain I want to move away from .ino files (thanks @pharap).

  1. What is this->? I’m pretty sure it’s a pointer, but what is it used for? If you go to game.h in the 2d platform demo, there are a lot of these. I’ve seen them too a lot in other peoples’ code as well (from my time browsing all the GitHub repos in the game section).

I know these questions are sort of tailored toward the 2d platformer demo (or are at least related to it) but I think these are general C++ questions as well.
Thanks in advance!

1 Like

Just to point it out: the enemies branch is the most updated one. (I probably ought to update the master branch at some point.)

I might be biased, but I completely agree!

Specifically #pragma once. There are actually other pragmas that do other things, and they’re technically compiler specific, but #pragma once has become common enough that practically every compiler supports it.

(If you’re curious, this is the documentation for GCC’s pragmas and this is the documentation for Microsoft’s C++ compiler’s pragmas.)

#pragma once/include guards are needed because of the way C++'s preprocessor works.

Basically, #include "filename"/#include <filename> effectively copies the entire contents of filename and pastes those contents into the point where the #include line is. So if you had these three files:

// A.h
struct A {};
// B.h
struct B {};
// main.cpp
#include "A.h"
#include "B.h"

The preprocessor would actually translate that into a single block of text, so what the compiler ends up seeing is something like this:

// main.cpp
// A.h
struct A {};
// B.h
struct B {};

Thus if you were to attempt using #include without using #pragma once or include guards you’d very likely end up pasting the same file contents twice, which aside from being inefficient can actually lead to errors.

Specifically if you end up with two copies of a definition (e.g. a struct definition) then you’ll violate the one definition rule (i.e. the rule that says you’re only allowed to define something once, otherwise the compiler complains, even if the definitions happen to be the same).

So if main.cpp were changed to:

// main.cpp
#include "A.h"
#include "A.h"
#include "B.h"

Then the compiler would end up seeing this:

// main.cpp
// A.h
struct A {};
// A.h
struct A {};
// B.h
struct B {};

Hence you now have a multiple definition error because A is defined twice.

If you instead had:

// A.h
#pragma once

struct A {};
// B.h
#pragma once

struct B {};
// main.cpp
#include "A.h"
#include "A.h"
#include "B.h"

Then the fact you’ve included A.h twice is no longer a problem because the preprocessor will only read it once, thus the compiler sees:

// main.cpp
// A.h
struct A {};
// B.h
struct B {};

Regardless of whether you #include "A.h" once, twice, or hundreds of times.

Of course, it is technically possible to have a file without #pragma once or include guards that doesn’t cause an error, as is the case with your example and my previous example, but that’s uncommon enough that it’s better to just always do it and then you don’t have to worry about any problems.

Most other languages don’t have this problem because they don’t have old-fashioned dumb copy-and-paste preprocessors, the file inclusion is handled by the compiler, but C++ is stuck with it.

There’s a lot more that could be said about the preprocessor and how compiling works, but I’ll leave it there for now.

Because one day you might need to include it twice. It’s much easier to be in the habit of always adding a #pragma once than to have to go back and add one if something breaks down later.

In the example you gave there wouldn’t be an error because you’re only declaring function in the header, but if you’d actually defined the function in the header or had defined something else, e.g. a struct, then you’d break the aforementioned one definition rule and get a compiler error that you’d then have to track down. (And if you didn’t know what I just told you about how #include works then you’d probably be scratching your head wondering why something that’s only in one header is being defined twice.)

You would still require at least one .ino file because of how Arduino works, but everything else could be implemented in .h files provided that you don’t have any circular references.

As proof, have a look at my ‘Physix’ demo. It has one .ino file and multiple .h files, but no .cpp file.

The rule isn’t so different in non-Arduino environments: you need at least one .cpp file, but you’re free to have everything else be .h files.

(Arduino actually converts .ino files to a .cpp files behind the scenes. It’s possible to access those files if you want, though it’s a lot easier with a VSCode/VSCodium setup.)

Not necessarily. I regularly use vastly more .h files than .cpp files.

There are certain scenarios where .cpp files are mandatory, but most of the time they aren’t.

Sometimes splitting code across a .h and .cpp file can make it easier to keep track of the content, and sometimes there are other benefits (some of which don’t really apply to Arduboy development), but often I find it results in duplicated effort because you have to keep the declarations and definitions synchronised.

Yes, it’s a pointer. (Though not all things to the left of a -> are pointers.)

this is only valid inside member functions, so you’ll see it only in definitions for functions that are members of classes or structs. Specifically it’s an implicit function argument that points to the object that the function is being called on.

For example, if you had the following struct:

struct Rectangle
{
	int x;
	int y;
	int width;
	int height;
	
	int getArea()
	{
		return (this->width * this->height);
	}
};

getArea is a member function that returns int. It calculates the area of the Rectangle by multiplying the rectangle’s width and height together.

In the body of the function this is a pointer to the rectangle the function is being called on. (I.e. it’s a Rectangle *.)

So if you had a variable Rectangle rectangle and you called rectangle.getArea() then when the computer executes the getArea function the implicit this argument will be pointing to rectangle.

For comparison, C doesn’t have member functions, so in C you’d have:

struct Rectangle
{
	int x;
	int y;
	int width;
	int height;
};

// This is legal in C because C doesn't have a 'this' keyword,
// but naming a parameter 'this' in C++ would produce a compiler error.
// I.e. in C++ you'd have to pick a different name.
int getArea(Rectangle * this)
{
	return (this->width * this->height);
}

And you’d have to do getArea(&rectangle) instead.

That sort of thing is really common in C, so when C++ was created the developer(s) decided to introduce member functions to make the process easier and less error prone.

The compiler generates almost the same kind of code as if you were doing it the C way, so there’s no extra cost, but it’s a lot safer. (By the rules of the language this can never be null. If it somehow does become null then you’ve somehow broken something else.)


That all said, I’ll get people complaining if I don’t explain one other thing: technically this-> isn’t actually necessary most of the time…

If you were to do:

struct Rectangle
{
	int x;
	int y;
	int width;
	int height;
	
	int getArea()
	{
		return (width * height);
	}
};

Then it would compile and work exactly the same as before.

However, I personally prefer to always use this-> because it makes it easier to understand a function if it’s taken out of context.

For example, if someone showed you a member function on its own, like this:

void applyGravity()
{
	y += gravity;
}

Then you don’t have enough context to know if y and gravity are member variables or global variables. But if it were written as:

void applyGravity()
{
	this->y += gravity;
}

Then you could infer from the context that y is a member variable and gravity is probably a global.

3 Likes

Thanks! This clears up quite a bit of stuff. Surprisingly, the pointer stuff actually made sense to me!

What would an example of a circular reference be?

What would be a situation where one is needed?

I’m just trying to iron out all the details. Thanks!

1 Like

Pointers are actually easy to understand once you understand how RAM works and how structs are laid out in memory.

The only scary bits about pointers are the kind of bugs they can cause and arguably the syntax.
(Personally I have no issues with the syntax, but I’ve known it to upset/confuse quite a few people.)

The circular reference scenario is the most obvious one.

Technically you need at least one .cpp file for any C++ code to compile, so that counts.

Also if you’re compiling static libraries I think those have to have corresponding .cpp files, but it’s been a while since I’ve done that so I’m not entirely sure. That’s not something you’d be doing in Arduboy code, but it’s something that people sometimes do in desktop code.

A circular reference is when two things depend upon each other.

To give an abstract example, say you had a class A with a function use that accepts a reference to B as an argument, but B also has a use function that accepts a reference to A as an argument. Something like so:

// A.h
#pragma once

#include "B.h"

class A
{
	void use(B &)
	{
	}
};
// B.h
#pragma once

#include "A.h"

class B
{
	void use(A &)
	{
	}
};

This setup wouldn’t work because A needs B to be defined before it can define its use function and B needs A to be defind before it can define its use function, creating a stalemate.

The preferred way to solve this is to introduce some .cpp files:

// A.h
#pragma once

// Declare B without defining it
class B;

class A
{
	void use(B &);
};
// A.cpp
#include "A.h"
#include "B.h"

// Both A and B are defined by the above includes
void A::use(B & object)
{

}
// B.h
#pragma once

// Declare A without defining it
class A;

class B
{
	void use(A &);
};
// B.cpp
#include "B.h"
#include "A.h"

// Both A and B are defined by the above includes
void B::use(A & object)
{

}

By deferring the definition of the function until the .cpp file, it becomes possible to define both use functions in a context where both A and B will be suitably defined to allow the code to work.

Note that this trick only works for pointer or reference parameters for technical reasons: to declare a function the compiler needs to know the size of all the arguments, and it can’t know the size of a type until the type has been properly defined. However, pointers are always the same size, so it’s possible to declare a function that accepts pointers to undefined types if those types have been declared, as in the case of class A; and class B; above.

You might think that circular references are useless, but actually there are certain situations where they can be useful.

A relevant real-world example:
In my non-demo C++ games I like to have a main Game class and several game state classes. The game state classes have functions that accept a reference to the Game class so that the Game class can expose some common state and utility functions. Meanwhile the Game class actually contains instances of the game state classes. Thus the game states depend on Game and Game depends on the game states, so it’s important to be able to break the cycle by implementing the game states’ functions in .cpp files.

If you want to see a real example of this, the most organised published example I have is Love Tester. You can see Game’s game state member variables and if you peek through the game states you’ll see that they predeclare Game with class Game; and have void update(Game &) and void render(Game &) functions which are declared in the .h files and defined in the .cpp files.

To truly understand it all in full you’d ideally need a good idea of how the compilation process actually works and what the rules are regarding declarations and definitions. If you want I can link you to some technical articles that explain it all in excruciating detail, but they’re not for the faint of heart and they provide far more detail than you really need to remember.

Alternatively, I’ve written a very rough handwavy abridged version of how compilation works here.
(I wrote it a few years ago to help someone else.)

1 Like

I thought of a few more things this morning, though I’m not sure if you’ll be interested or not. They don’t necessarily affect Arduboy development much, but if you also write code for desktop or you’re planning to move on to desktop programming then they should come in handy.

Firstly, it is technically possible to resolve a cyclic dependency* without using .cpp files by putting all the definitions in the same header, but stuffing lots of code into a single file is generally considered to be messy and disorganised.

(* Technically I should have said ‘dependency’ instead of ‘reference’ in the first place because those terms are typically used to refer to different problems, but I don’t always make the distinction when I’m being informal. Usually the distinction is that a ‘circular/cyclic dependency’ is to do with one class or file depending on another for its definition and is thus a compile-time issue, whereas ‘circular/cyclic reference’ is something that happens at runtime involving objects referencing each other with pointers.)

Something I neglected to mention before:

There’s actually a technical reason for having .cpp files in the first place. Each translation unit (i.e. each .cpp file and the headers it includes) is typically compiled to a separate .o (object code) file, and sometimes the compiler can avoid having to recompile an .o file because it can prove it doesn’t need to. That means that having code spread across several translation units (i.e. having separate .cpp files) can sometimes make compilation faster. However, these days that’s less important and only really matters for large codebases, hence I neglected to mention it because Arduboy code is unlikely to ever get large enough for the difference to be noticeable (unless you’re compiling on a really old computer).

There’s a lot more to know than that, e.g. the pros and cons of header-only libraries and how static and dynamic libraries come into the frame, but that sort of stuff isn’t really relevant for Arduboy projects. If you’re interested in knowing more then let me know, otherwise I’ll leave it there for now.

As ever, if you have any other C++-related questions then feel free to ask.

(This made a nice change for me because I haven’t actually done anything programming related for at least a month or two.)

1 Like

Okay, I’ve got some more questions.

  1. Is it possible to instantiate stuff?
    Say I have an enemy.cpp and enemy.h, with a class, a complete AI, and whatnot. Would it be possible to instantiate/clone the enemies and scatter them throughout the map? The way I did it last time was by pretty much copy-pasting the exact code for the AI, with different variables. It was a terrible idea since it trippled the AI’s share of the progmem, and you could only have three enemies on the map.

  2. What’s the deal with definitions and declarations?
    I understand how #include works, how .h files work, how .cpp files work, but there’s one thing I don’t understand/see working out. So for most variables, you’re probably going to be assigning and changing values, so it’s great if you use a .h file. But for other variables, which probably are not going to be changed or whatever, would you define them in a .h file? Or assign them a value at the start of the .cpp file?

  3. When should you use a define over a variable?
    I’m quite interested in this because it seems most people use quite a bit of defines, whereas I use none.

  4. Is it possible to use the (if I remember correctly) two channels to display separate sounds?
    So one channel could play “background music” and the other could play the “actual” sounds.

  5. What’s the difference between const and constexpr?
    From what I’ve read, constexpr is a compile time const, and const can wait until later. I don’t understand what that means though, is it just compile time or something?

  6. When should you use an int function (or whatever) and return to get a value?
    Another thing I see is people using an int function, then using return to get back a value. Maybe it’s just because I don’t know the benefits/reason for this, but I’m really just not sure why you would so something like this.

I also apologize in advance for the super blocky and tight text.

1 Like

First up, the fact your code is split into enemy.h and enemy.cpp is irrelevant for this question. The important part is that you have an Enemy class.

The fact you’re trying to ‘copy-paste’ your code suggests you’ve likely missed the point of classes…

A class is a blueprint for an object. It specifies the members of that object, both the member variables and the member functions. After defining a class you can create as many different objects (or ‘instances’ if you prefer) of that class as you want.

For example, given the definition:

class Point
{
public:
	int x;
	int y;
};

You can do:

Point pointA;
Point pointB;

And both pointA and pointB will have their own x and y members and be independent from each other. They are separate objects that happen to have the same type (Point) and thus the same layout. They have the same structure but occupy different regions of memory. You can even have an entire array of them, e.g. Point points[4];.

Thus, in the case of having multiple enemies, you can simply do Enemy enemies[10];. No need to make multiple classes if all enemies share the same AI. (Even if you want enemies to have different AI you don’t necessarily need multiple classes, but that would be a different question.)

Weirdly the body of your question doesn’t really match the title.

Variables that don’t change are commonly called ‘constants’. The proper way to make constants in C++ is to use either a const or constexpr variable. (I’ll explain the difference later since that was one of your other questions.)

As for where to keep them, you can keep them more or less anywhere you would a normal variable, but typically there are certain places that make more sense than others.

For global constants, you’re best off keeping those in in your .h files so they’re accessible to any file that includes the header.

For constants that are relevant to a particular class, you’re best off defining those in the class’s definition, and crucially you’re best off making them static member variables. E.g.

struct Player
{
	// If your player can only move at a fixed speed,
	// it might be wise to make that speed
	// a named constant.
	static constexpr movementSpeed = 1;
};

A question you didn’t ask but I think I should answer anyway is “When should I use a constant?”.

The answer to that is any time you find yourself introducing a number that has a specific meaning. If you find yourself writing a numerical constant with a significant meaning (e.g. you’re not just adding 1 to something or doubling something) then you should consider creating a named constant for that value.

For single-use values using a named constant merely increases readability (which is a noble goal in itself and is thus deserving of your time), but for values that are being used in multiple places, using a named constant gives another benefit: because the value is only defined in one place, you only have to change it in that one place to automatically update the value at every point in the code in which it is used.

See also:

Almost never. Use const or constexpr variables instead.

There’s a lot to say about this issue, but I’ll try to focus on the major points…

First, to explain what macros actually are:

#define is a preprocessor directive that associates an identifier (called a macro) with a block of text. When the preprocessor encounters such an identifier in the source text it replaces it with the block of text that was associated with it. Hopefully you can see there’s a similarity in that behaviour to how #include works. I.e. that the preprocessor replaces a symbol in the source text with more text.

That behaviour is why I regularly refer to the preprocessor as a glorified copy-and-paste machine - because aside from a few directives 90% of what it does is to copy blocks of text and paste them elsewhere.

Given the following input:

#define WIDTH 128
#define HEIGHT 64

int calculatePixels()
{
	return WIDTH * HEIGHT;
}

The preprocessor would transform it into:

int calculatePixels()
{
	return 128 * 64;
}

The preprocessor runs before the compiler, so that processed block is actually what the compiler ends up seeing.

By contrast, if you use actual variables then the compiler sees the actual variables, not a block of pasted text.

// Here you are seeing the same thing
// that the compiler will 'see'.
constexpr int width = 128;
constexpr int height = 64;

int calculatePixels()
{
	return width * height;
}

Historically this used to mean worse error messages because the compiler is usually what reports the errors. You might end up with something like “error: blah blah blah ‘128’” instead of “error: blah blah blah ‘width’”. Fortunately these days compilers are smart enough to be able to remember the macro name, so that doesn’t really apply anymore, but it’s worth remembering.

Now for the actual serious issue, which is also why macros are conventionally written in all uppercase (commonly termed ‘macro case’ by C++ programmers). Macros have no concept of scope.

If you were to write a function like so using constexpr (or const) variables:

int calculatePixels()
{
	constexpr int width = 128;
	constexpr int height = 64;

	return width * height;
}

Those variables, width and height, are local to the function and only exist within the scope of the function. Which is to say that nothing outside of the function can see them. That’s a good thing. That allows you to reuse variable names and thus avoid name clashes.

To give a slightly contrived example to prove a point:

constexpr int width = 128;
constexpr int height = 64;
	
int calculateSomethingElse()
{
	// Local variables - no code outside
	// the function will see these.
	constexpr int width = 12;
	constexpr int height = 10;

	return width * height;
}

int calculatePixels()
{
	// Uses the global 'width' and 'height'
	return width * height;
}

If you were to use macros instead however…

#define WIDTH 128
#define HEIGHT 64
	
int calculateSomethingElse()
{
	// This actually redefines the above macros because
	// the preprocessor has no concept of 'scope'.
	#define WIDTH 12
	#define HEIGHT 10

	return WIDTH * HEIGHT;
}

int calculatePixels()
{
	// This should have been 128 * 64,
	// but thanks to the above function it's now 12 * 10
	// and now the code has a silent runtime bug.
	// Thanks macros(!)
	return WIDTH * HEIGHT;
}

Admittedly this kind of bug isn’t going to happen very often, and the compiler will actually warn you about the fact you’re redefining a macro to prevent this kind of bug, but it only does that in the first place precisely because that kind of bug is possible.

Why take the risk in the first place when const/constexpr variables solve the problem?

Besides which, the fact const/constexpr variables can be local opens up a lot of useful possibilities in terms of organisation. Sometimes it just makes sense to tuck a constant away inside a class, function, or namespace.

There is a legitimate use for macros, which I’d be happy to explain if you want to know more, but I’m going to end there because this has been quite a long answer already.

For one last example of the trouble macros can cause, consider this:

#define width 10

int something()
{
	int width = 12;
}

That will give you a compiler error telling you that 12 cannot be assigned to 10, or words to that effect, because what the compiler actually sees (after the preprocessor is done butchering the source text) is this:

int something()
{
	int 10 = 12;
}

That’s why it’s recommended to keep macros in all capitals - to avoid clashing with legitimate names.

That one I actually don’t know the answer to because I’ve never really played with sound.

I can scrape together some sprites, but I have next to no musical talent and can’t really test sounds without disturbing other people, hence all of my games thus far are silent.

@MLXXXp might be a more suitable person to answer that one. I’m not sure who else has ever played around with the Arduboy’s sound hardware.

const means ‘read only’.

constexpr means both ‘read only’ and ‘can be computed at compile time’.

The key difference is that a const variable may be assigned a value that can only be computed at runtime, whereas a constexpr variable can only be assigned a value that can be computed at compile time.

One particularly notable implication of this difference is that if you wish to assign the result of a function to a constexpr value, that function must be a constexpr function (i.e. a function marked constexpr whose result can be computed at compile time and whose body has various restrictions placed on it).

As a general rule you should prefer constexpr to const because constexpr has the stricter rules and the majority of your constants will be constexpr. If you stick to that rule then the occasions when a value can’t (or shouldn’t) be computed at compile time become more obvious.

Personally I like to think of constexpr as meaning ‘constant’/‘immutable’ and const as only meaning ‘read only’. That’s not exactly what they mean, but their usage is quite close to those ideas.

(Useless C++ trivia: once upon a time in the early days of C++ const actually was readonly and had a corresponding writeonly.)

I’m not entirely sure what you mean by this.

Do you mean “When should functions be used?” or “When should functions without any parameters/arguments be used?”.

To give some generic advice and info that may or may not answer your question:

A function is basically a sequence of instructions that compute some value or perform some actions. The int part is the return type - the type of the value the function returns as an output. It can be any type, and the keyword void symbolises that a function doesn’t return a value (as an output).

For example, you could have a function:

int square(int value)
{
	return (value * value);
}

Which squares the number passed to it. Thus int squareOfFive = square(5); would assign a value of 25 to the variable squareOfFive.

When a function is called the computer (in this case the Arduboy, or the Arduboy’s CPU) effectively ‘jumps’ into the body of the function (the code between the brackets), carries out the actions specified by the statements in the body of the function, and then when either a return or the end of the function is reached it returns to the position it was at previously and carries on as it was before.

A function should ideally be used whenever either:

  • You have a sequence of actions that are likely to be used in multiple parts of the code.
    • E.g. resetGame, changeGameState
  • You have a sequence of actions that form a meaningful unit and should thus be given a name specifying what that those actions as a whole do.
    • E.g. updateGameplayState, drawPlayer, printHUD
  • You have a sequence of actions that compute a value and together that computation does something specific that ought to be given a specific name.
    • E.g. a square function for squaring a number, an absolute function for calulating the ‘absolute’ value of a number (i.e. if it’s negative, make it positive, and if it’s positive leave it as it is).

I have no clue what you mean by this.


Recommended reading and other references:

1 Like

Thanks! For some reason, when you say something it just makes sense.

Oh! I thought you just used that once so that you can access members of classes/structs.

The truth is, I don’t really know what my question is exactly.

Ah, I think there was a bit of a misunderstanding. Let’s say I had an int lives; variable. Whenever I take damage, I want to decrement it by one.

//.h file
int lives;
void takeDamage();
//.cpp file
#include ".h file"

void takeDamage()
{
    --lives;
}

So here, you’re not actually defining it (which is what I meant when I said you wouldn’t change the value of it, which I apologize for, it could have been clearer).

Would you define something like this in the .h file, or at the top or the setup() in the .cpp file? I would assume it would be the latter, since the former would break the one definition rule (or maybe not because you’re only including it once -granted you’re using #pragma once or an include guard- in the compiled mega-file)?

One thing I have seen people do though, is creating a local variable then assigning that value to the “actual” variable. For example:

//.h file
int lives;
void takeDamage();
//.cpp file
#include ".h file"

void takeDamage()
{
    int LIVES = 3;
    --LIVES;

    lives = LIVES;
}

What I meant is, why would you get a value like this from a function, when you could very easily just get one from a variable?

Ah, I just meant my style of writing (barely any formatting, just words)

Thanks again!

1 Like

Well, it wouldn’t be very useful if I just spouted nonsense.

Again, I’m not sure what you mean by this.

You can still access the members:

Point pointA { 10, 5 };
Point pointB { 12, 6 };

pointA.x += 5;
pointA.y += 2;
pointB.x += 8;
pointB.y += 3;

The same applies to arrays:

// You could individually define the values
// of all these points if you really wanted.
// Using {} here will just zero-initialise them all.
// I.e. set all the fields to 0.
Point points[10] {};

point[3].x += 5;
point[4].y += 6;

for(size_t index = 0; index < 10; ++index)
{
	points[index].x += 1;
	points[index].y += 2;
}

Again, the class/struct is the blueprint for an object, and the variables are the objects. (And an array variable holds multiple objects.)

(This makes a lot more sense when you understand how memory works and how objects are laid out in memory, but that’s worthy of an essay of its own.)

By ‘defining’ I think you mean assigning it a value.

int lives; is a valid variable definition, even without a value. It would actually end up with a value of 0.

Unlike local variables (i.e. those you define in a function) or member variables (i.e. those you define as part of a class definition), global variables are zero-initialised by default (unless the type has a default constructor, but I don’t think you’ve learnt about default constructors yet).

Ideally you should avoid global variables by using classes, but that might not be practical for you yet since you’re only just getting used to using classes.

With that in mind, there’s some rules for declaring and defining global variables that you should be aware of when using .h and .cpp files.

If only one translation unit sees the lives variable (i.e. only one .cpp file includes the header in which it’s defined) then you can get away with just defining it normally in the header.

However, if you need more than one translation unit (i.e. .cpp file) to be able to access it then you have to declare it as extern in the header and put the definition in the .cpp file, like you would do for a (non-inline) function.

E.g.

// Something.h

// Declare the variable as 'extern'
extern int lives;

void decreaseLives();
// Something.cpp
#include "Something.h"

// Define the variable
int lives = 5;

void decreaseLives()
{
	--lives;
}

If you don’t do that you get a ‘linker error’ which complains about symbols being defined multiple times, which sounds like an ODR violation but it’s actually a different problem.

Needing to use extern probably seems a bit daft because frankly it is a bit daft. Modern languages don’t have to do this sort of thing, but C++ does because of the way the old-fashioned compiler design works.

C++17 added a way to avoid this by marking a variable inline, thus allowing you to define it in a header, but Arduino is stuck on C++11 so you can’t do that for Arduboy games.

Fortunately you don’t need extern for const or constexpr variables.

I’m not sure whether or not I should explain why the extern is needed. I’m happy to explain if you want to know, but the answer is quite long and boring. (The short version is that it’s to do with a thing called the linker, which is a program that runs after compilation to tie together all the object files into a single executable.)

It wouldn’t break the one definition rule because of the #pragma once/include guards.

It still causes a problem because you need the extern as explained above, but the problem wouldn’t be an ODR violation, it would be a linker error (albeit one that’s suspiciously similar to an ODR violation because it involves the same ‘symbol’ being defined more than once).

Either you’ve misunderstood what you’ve seen or these people are being silly.

That takeDamage function would set lives to 2 every time:

  • LIVES = 3;
  • --LIVES decrements LIVES, thus making it 2
  • thus lives = LIVES; sets lives to 2.

When the function ends the variable LIVES ceases to exist, and when it’s called again LIVES will be reinitialised with a value of 3; it won’t retain its previous value.


I’m not sure I understand your question, but my instinct is that you’re asking about ‘getter’ functions.

E.g.

struct Point
{
	int x;
	int y;
	
	int getX()
	{
		return x;
	}
	
	int getY()
	{
		return y;
	}
};

Is that what you mean? Functions like getX and getY above?

1 Like

Yes. That’s what the ArduboyPlaytune library is intended for. See the library’s README.md file for more info.

2 Likes

Thanks! I completely understand the .cpp / .h relations now (or at least I don’t have any questions about them at the moment).

:eyes::flushed:
I’ve seen it done in your platformer demo when updating the players position (the xVelocity). But maybe that’s a different case since it’s velocity.

Pretty much yeah.

If you mean this:

That’s different from what the example you gave is doing.

The example you gave using lives didn’t have any if statements so it does the same thing every time, unconditionally, and always produces the same result because it’s applying the same arithmetic operation to the same constant value.

The code from my platformer demo is conditional - it changes the velocity depending on which buttons the player is or isn’t pressing:

  • If the player presses nothing, the velocity is 0.
  • If the player only presses left, the velocity becomes -movementSpeed.
  • If the player only presses right, the velocity becomes movementSpeed.
  • If the player presses left and right, the -movementSpeed and movementSpeed cancel each other out, thus causing the velocity to become 0.

I could have done away with the local variable, like so:

this->playerEntity.xVelocity = 0;

if(this->arduboy.pressed(LEFT_BUTTON))
{
	this->playerEntity.xVelocity -= movementSpeed;
}

if(this->arduboy.pressed(RIGHT_BUTTON))
{
	this->playerEntity.xVelocity += movementSpeed;
}

That would have the same resulting effect, but the version that uses the local variable is likely to be very slightly more efficient for technical reasons.

(I can go into the technical reasons if you want. It’s to do with how the computer uses memory.)

The moral of the story is that if you want to ask what a piece of code is doing and/or why it’s doing it, it’s best to use the original piece of code, otherwise you risk losing important context and/or changing the meaning of the code, and thus getting a different answer from the one you wanted.

They’re called ‘getter functions’ or ‘getters’, and the equivalent for assigning variables are called ‘setter functions’ or ‘setters’.

When writing classes it’s considered a good idea to keep as much data (i.e. as many member variables) private as possible, and for the class to interact with the outside world primarily through public functions.

The idea behind this is that by providing a public interface that is separate from the private implementation it should become possible for the private implementation details to change without having to change the public facing interface.

For example if you had:

class Entity
{
private:
	int x;
	int y;
	
public:
	int getX() const
	{
		return this->x;
	}
	
	int getY() const
	{
		return this->y;
	}
};

void printPosition(const Entity & entity)
{
	arduboy.print('(');
	arduboy.print(entity.getX());
	arduboy.print(F(", "));
	arduboy.print(entity.getY());
	arduboy.print(')');
}

And then you introduced a Point type and wanted to replace your int x; and int y; in Entity with a Point, you can do that without having to upset any code that’s using getX() or getY():

class Entity
{
private:
	Point position;
	
public:
	int getX() const
	{
		return this->position.x;
	}
	
	int getY() const
	{
		return this->position.y;
	}
};

void printPosition(const Entity & entity)
{
	arduboy.print('(');
	arduboy.print(entity.getX());
	arduboy.print(F(", "));
	arduboy.print(entity.getY());
	arduboy.print(')');
}

Notice the contents of the printPosition function didn’t have to change.

If you had instead been using:

class Entity
{
public:
	int x;
	int y;
};

void printPosition(const Entity & entity)
{
	arduboy.print('(');
	arduboy.print(entity.x);
	arduboy.print(F(", "));
	arduboy.print(entity.y);
	arduboy.print(')');
}

And then you wanted to make the same change (i.e. to replace x and y with a Point marking the entity’s position), you would have to go around your code replacing all the relevant .xs and .ys with .position.xs and .position.ys.

Modern tools can automate that sort of thing, but it’s still more time consuming and error prone than having a stable function-based interface.

Admittedly this is less of an issue for Arduboy games than it would be for most desktop software since you’re probably the only one who is going to be touching the code, but even so you can see how the approach can be useful.

Another important aspect of getter and setter functions is that they allow you to do ‘sanity checking’/‘data validation’.

For example, if you had:

class Player
{
private:
	const char * name;
	
public:
	void setName(const char * name)
	{
		this->name = name;
	}
	
	const char * getName() const
	{
		return this->name;
	}
};

And you decided that you wanted to make it impossible for the player’s name to be nullptr then you could use setName to prevent that by adding some validation logic:

void setName(const char * name)
{
	if (name == nullptr)
		return;

	this->name = name;
}

Ideally you’d actually report an error instead of just ignoring the bad input, but options for error reporting on the Arduboy are quite limited.

You could do something like this:

void setName(const char * name)
{
	if (name == nullptr)
	{
		arduboy.clear();
		arduboy.print(F("Error: attempt to set player name to nullptr"));
		arduboy.display();
		while(true) {}
	}

	this->name = name;
}

But that’s quite expensive, and you then need the arduboy object to be easily accessible.

On desktop you could just throw an exception, but most Arduino targets have exceptions disabled because they’re too expensive for most microcontrollers.


Looking at my platformer demo reminded me of another use of getters. If you provide only getters and no setters then you can prevent a variable from being modified outside of the class, or strictly control how it may be modified.

For example, here’s a version of Entity that strictly controls how its health may be modified:

class Entity
{
private:
	uint8_t health;
	uint8_t maxHealth = 100;

public
	uint8_t getHealth() const
	{
		return this->health;
	}

	uint8_t getMaxHealth() const
	{
		return this->maxHealth;
	}

	void damage(uint8_t amount)
	{
		if(amount < this->health)
			this->health -= amount;
		else
			this->health = 0;
	}

	void heal(uint8_t amount)
	{
		uint8_t healthDifference = (this->maxHealth - this->health);
		
		if(amount < healthDifference)
			this->health += amount;
		else
			this->health = maxHealth;
	}
};

The heal function prevents health from exceeding maxHealth, and both damage and heal guard against integer overflow.

If damage had been implemented the naive way, as:

void damage(uint8_t amount)
{
	health -= amount;
}

And health were 10, then entity.damage(20) would cause the value to go past 0 and wrap around to 245.

Hence how careful use of functions, logic, and keeping your member variables private can help to keep your code readable and bug free.

1 Like

Is there a way you could automatically create these? Let’s say, you have a “missile” class, for a shmup or something, could you automatically do this every time a new missile is fired? And how would you access the members as one “whole?” Like, if any missile goes out of the screen or hits an enemy, destroy it?

Also, is there an official way to erase text? I know you can use state machines/switch statements, or arduboy.setTextColor(), but those are pretty inefficient and big if you ask me.

Technically there’s a way to do it in the way you’re thinking (i.e. to just magic up a new object as necessary), but that particular technique isn’t a good thing to do on Arduboy because of the Arduboy’s limited memory.

Your best bet is to create an array that can hold the maximum number of objects that you’ll need and to use only as much of the array as you need at any given time.

There’s two ways to do that:

  • The easier way is to give each object some kind of ‘active’/‘inactive’ flag (i.e. a bool), or ‘alive’/‘dead’ if suitable, and then only update and draw the objects that are actually active. You’ll have an easier time figuring out the logic for this, but it’s likely to be a bit more memory hungry unless you do some clever bit packing.
  • The slightly harder way is to maintain an integer (e.g. size_t count;) that tracks the number of objects in the array that are actually in use, and to regularly move the objects in the array around in such a way that all the active objects occupy the first count elements and the remaining elements are considered inactive and thus not touched. This is much cheaper, but requires more clever logic to implement properly.

Yes, by either:

  • Finding an inactive missile in the array of missiles and activating it, then using that newly activated missile for launch.
  • By ‘appending’ a missile to the end of the active list, increasing count in the process.

Depending on which of the aforementioned techniques you’re using.

No clue what you mean by ‘whole’. Hopefully this answers your question:

  • For implementation A:
    for(uint8_t index = 0; index < arraySize; ++index)
    	if(missiles[index].isActive())
    		missiles[index].x += 1;
    
  • For implementation B:
    for(uint8_t index = 0; index < count; ++index)
    	missiles[index].x += 1;
    
  • Implementation A: missile[index].deactivate(); (which may just set some bool active to false, or may do something more clever)
  • Implementation B: I kind of feel like I’m helping you cheat by showing you this because you’ll miss out on the wonderful frustration of having to work it out for yourself (and the sheer joy of actually solving it and understanding the solution). Nethertheless, here’s the answer:
    // Decrease the number of active missiles
    --count;
    
    // Remove the specified missile by moving all the entries down
    for(uint8_t index = targetIndex; index < count; ++index)
    	missiles[index] = missiles[index + 1];
    

I think I actually have a List class somewhere in one of my GitHub repos that I could lend that would make it a fair bit easier, but I’m a bit reluctant because I think you’d learn a lot by trying to solve this problem yourself.

It depends how/why you want to erase it, and how much you want to erase.

The easy way is to draw a black rectangle over it.

The cleverer and more efficient solution is to avoid writing the text in the first place.

I’m not sure what exactly you’re thinking of with this.

I feel like I’m missing some important context again, so I can only provide general commentary.

What makes you think they’re inefficient?

switches aren’t especially inefficient, though they can be made inefficient through misuse.

They might look big, but the size of source code doesn’t necessarily correspond to the size of the generated machine code. The size of the source code is less important than the nature of the operations that are being carried out.

As for using setTextColor() and then printing on top of the existing text, that likely would be less efficient than simply drawing a rectangle over the text.

Again, the most efficient thing to do is actually to avoid drawing the text in the first place. If you draw text and then erase it, that’s time and effort wasted that could have been avoided. (The computer’s time and effort.)

// Write
arduboy.print(F("Something");

// Then erase
if(someCondition())
	arduboy.fillRect(0, 0, 100, 8);

Versus

// Avoid writing in the first place
if(!someCondition())
	arduboy.print(F("Something");

As a general rule it’s often more efficient to avoid doing something in the first place than to do something and then try to undo it.

1 Like

What I mean is, rather than creating an if-statement for every single member of the array for something, I meant to access the entire array, rather than an element of the index. Like, if you had an array of 10 missiles, you would have 10 if-statements (or maybe that’s just going to be me) to check for the simplest things, like checking to see if they’re out of the screen’s width.

You could use a switch statement (I think, though I’ve never tried it directly for text) to change the “state” of the text (i.e. enabled/disabled).

What I meant by inefficient, is big in code. A whole switch statement for a simple “enemies approaching” text seems like overkill to me (or maybe that’s just because I use switch statements for big things, like state machines).

No you wouldn’t, that’s what for loops are for:

for(uint8_t index = 0; index < arraySize; ++index)
{
	// I'm assigning this to a variable for the sake of readability.
	bool offscreen =	
		(missiles[index].x >= arduboy.width()) ||
		(missiles[index].x + missileWidth < 0) ||
		(missiles[index].y >= arduboy.height()) ||
		(missiles[index].y + missileHeight < 0);
		
	if(offscreen)
	{
		// Handle missile being offscreen
	}
}

A single if, run once for each missile.

In fact, you can go one better and use a ranged for loop, which iterates over every item in a given array:

// 'auto' infers the type of 'Missile'
// '&' signifies that 'missile' is a reference,
// which means that when you attempt to modify it
// it will modify the original version stored in the array too.
for(auto & missile : missiles)
{
	// I'm assigning this to a variable for the sake of readability.
	bool offscreen =	
		(missile.x >= arduboy.width()) ||
		(missile.x + missileWidth < 0) ||
		(missile.y >= arduboy.height()) ||
		(missile.y + missileHeight < 0);
		
	if(offscreen)
	{
		// Handle missile being offscreen
	}
}

I’m still not quite sure what you’re imagining you’d do with the switch.

If the state can only be one of two things (enabled or disabled) then a plain old if should suffice. A switch is only really needed when you have several different states to choose between, or when you know you’re going to end up with more than two.

Even then you could use ifs if you wanted, but you’d end up with a long chain of them, and that would potentially produce less efficient code depending on whether you remember all the necessary elses and whether the compiler is smart enough and able to optimise the if chain.

Again, a large block of code doesn’t necessarily equate to an inefficient program.

Don’t forget that a lot of the switch is curly braces and whitespace, so it looks bigger than it really is.

Again, I’m not sure what you think you’d be feeding to the switch.

I’m imagining that you’re thinking you’d be writing something like this:

switch(areEnemiesApproaching())
{
	case true:
		arduboy.println(F("Enemies approaching!");
		break;
}

Which is only marginally larger than the equivalent if:

if(areEnemiesApproaching())
{
	arduboy.println(F("Enemies approaching!");
}

That’s not the sort of thing you’d use a switch for anyway though. It’s almost unheard of for someone to switch on a bool expression because if is more suitable for bool. switch is better suited for things that can have many different values. (Except maybe integers.)

Bear in mind that if you didn’t use switch for state machines you’d be writing stuff like this:

if(this->state == State::Preview)
{
	this->previewLogic();
}
else if(this->state == State::Simulation)
{
	this->simulationLogic();
}

Which gets quite tedious after a while.

With a switch you don’t have to keep writing this->state == . Granted you need to include break;s, but that’s not so bad, and it’s not that much larger:

switch (this->state)
{
	case State::Preview:
		this->previewLogic();
		break;

	case State::Simulation:
		this->simulationLogic();
		break;  
}

Most importantly, it’s more readable. Readability is king.

1 Like

I’ve got a few more today.

  1. What is static, and why should you use it?
    @Pharap , yesterday you told me quite a bit about non-static members and functions. I have read a bit about it, that it doesn’t associate the variable or thing with the class? Normally (or in my case) you can’t have a constexpr variable in a class, but when you declare it static, it can be in the class. The this-> pointer won’t work as well. Are there really any use cases for static?

  2. What is static_cast<>()?
    I’ve read that this supposedly forces a variable into a type, but why not just use auto? And, if you’re going to convert it eventually, why not just make it that type from the start?

  3. What is NULL?
    Yesterday when I was trying to make a particle system, I was thinking about the spawning of them. Since by default, the x and y are going to be 0, and visible, rather than going about my usual method, of putting them at -100 or something, NULL popped into my mind. I think I remember that NULL means 0, but it doesn’t actually exist?

Thanks!

1 Like

They keyword static has many different meanings depending on the context.

What you’re asking about is static member variables.

Static member variables are like global variables, but they are specifically associated with a particular class. (I.e. you must do ClassName::constant to refer to them from outside the class.)

That’s because normal member variables are part of the object layout of the class - i.e. they define the ‘blueprint’ for instances (objects) of a class, where each instance has its own copy of every member variable.

Making those constexpr (which means ‘can be calculated at compile time’) wouldn’t make sense because A) (in most cases) the object doesn’t exist until runtime and B) if they did represent a constant value that could be computed at compile time, it would be a horrible waste to have every instance keep its own copy - you’d be better off having a single global copy.

(Note that, unlike with constexpr, having a const (i.e. ‘read only’) member variable does make sense because the value would be computed at runtime and thus each instance might hold a different value.)

Static member variables on the other hand are associated with the class but are not part of the object layout. They behave like globals, and thus there would be a single copy shared between every instance of the class.

It will.

In most languages you wouldn’t be able to access a static member variable (or whatever that language calls it) via this (or the equivalent), but C++ has a specific rule that states static members may still be accessed as if they were non-static members via . or ->.

Hence why you can access some static member variables and functions on an arduboy object via arduboy. instead of having to do Arduboy2::. (Though doing Arduboy2:: is handy when you don’t want to be having to pass an instance around just to access utility functions.)

Casting is the process of coverting one type to another. There are several different types of casts with different capabilities and purposes.

static_cast is the safest and most basic of the different kinds of cast. It performs a safe type conversion between types when permitted. For example, enumerations can be converted to integer types and vice versa, integer types can be converted between each other, floats can be converted to integers, et cetera.

Classes and structs may define custom conversion operators which static_cast will opt to use for conversion if applicable.

E.g.

struct Int
{
	int value;

	// An implicit conversion operator
	// activates automatically as required.
	operator int()
	{
		return this->value;
	}

	// An explicit conversion operator
	// requires an explicit cast to work.
	explicit operator unsigned int()
	{
		return this->value;
	}
};

void demonstrate()
{
	Int example { 10 };

	// Using the implicit converstion operator
	// for 'int'
	int value1 = example;

	// Using the explicit conversion operator
	// for 'unsigned int'
	auto value2 = static_cast<unsigned int>(example);
}

(Note: it’s almost always better to make a conversion explicit, and especially when there may be a loss of data. Unfortunately C++'s core types all have implicit conversions to each other because it inherited those rules from C and the creators of C didn’t appreciate the value of a strong type system. Modern languages like C# are much better at having good type conversion rules.)

auto infers the type of a variable from the type of the expression on the right hand side of the assignment operator (=). It does not perform any kind of type conversion, all it does is automatically determine what type a variable should be based on the value being assigned to it.

Because:

  • Different types have different characteristics and interfaces.
  • Having distinct types for distinct purposes helps keep the code type-safe, which protects against certain kinds of errors.

For example, most of the time when you have an enumeration you specifically want to hide the fact it’s internally represented as an integer because you want to disable arithmetic operations, because those operations are meaningless or error prone.

However, you might want to provide some operations that can either only be implemented by treating an enumeration as its underlying integer type, or can be more efficiently implemented that way than they would have to be implemented if you couldn’t treat an enumeration as an integer.

For example:

enum class Weekday : uint8_t
{
	Monday,
	Tuesday,
	// ...
	Saturday,
	Sunday,
};

// Efficiently move to the next day
// by secretly converting the day to
// an integer and then back again.
Weekday nextDay(Weekday day)
{
	if(day == Sunday)
		return Monday;

	auto next = (static_cast<uint8_t>(day) + 1);
	return static_cast<Weekday>(next);
}

A lot of the time you aren’t going to need a conversion operator. They’re more likely to be needed for libraries that are creating very general types that either wrap other types or act as e.g. an arithmetic type.

For example, my fixed point arithmetic library gives the fixed point types conversion operators for integer and floating point types because it makes sense that you’d want to be able to convert a numeric type to another numeric type.

In a word: nullptr.

nullptr didn’t exist until C++11. Prior to that there was NULL, an ugly macro inherited from C.

Originally NULL was defined in C++ as 0, and some special rules were put in place to allow 0 to be assigned to any pointer and represent the null pointer.

In C++11 the committee created nullptr, which is both better for being a meaningful keyword and better because it actually has its own type (nullptr_t on Arduino, std::nullptr_t in other C++ environments), which allows better optimisation tricks and resolves certain ambiguities. NULL was thenceforth edefined as nullptr.

In regards to ambiguities: given a function a with two overloads a(int) and a(const char *):

  • When NULL is defined as 0, a(NULL) would call a(int) , which likely isn’t what the caller intended.
  • When NULL is defined as nullptr, a(NULL) instead calls a(const char *), which likely is what the caller intended.

As for what nullptr is actually used for, it’s used to explicitly mark that a pointer is invalid (i.e. doesn’t point to anything). There are certain scenarios where that’s useful, but most of those scenarios aren’t useful for Arduboy.

(By the way, in C NULL isn’t 0 or nullptr, it’s actually (void*)0 - a definition that wouldn’t work in C++ because of rule differences.)

This is essentially the same problem as spawning enemies.

For now, stick to giving them a bool active; member variable, and when you need more RAM or you’re at least a bit further along I’ll either talk you through how to manage an array such that it tracks active and inactive objects or give you a class that does that for you.

1 Like