.h and .cpp or .ino?

I’ve been looking through a lot of peoples’ source code for their games, and one thing I noticed people doing is having a bunch of .h and .cpp files, and their .ino file is practically empty. For some other people, I notice they don’t have a bunch of .h and .cpp files, but they have all .ino files. I currently write all my code in the .ino file, and use .h files to store images and sound, but I don’t really know what .cpp files are, not really .h files either, but I assume they’re used for storing data like I use them for? So why use a bunch of .ino files or a bunch of .h and .cpp files?

Basically, .h and .cpp files are used to create C++ classes.

In your games, you have created structs (which are basically classes) and these could have been defined in a .h file. You can have all your code in one file but it becomes unwieldy when you are creating a big game.

If you look at one of my games, GitHub - Press-Play-On-Tape/1nvader, you can see under the /src/entities directory I have a bunch of basic ‘entities’ in the game … player, enemy, bomb, etc. These are all structs and have a bunch of properties but they also wrap up some basic logic that can move them, reset them … whatever.

At the root directory, I have split the main parts of the game into separate .ino files just to make it easier to navigate rather than having one big file. Each of my game states has an init() function which gets called as I move into that state and a main function which gets called repeatedly.

My main .ino file just handles the states and has very little code in it.

Everyone structures things differently. I have a number of games where the states where actually classes in themselves. What I found though was that this took up a heap of memory for no real value so I stripped the game state classes back to basic functions in .ino files.

2 Likes

Make yourself a cup of coffee, this is going to be quite an info dump…


What Are .ino Files and Why Do They Exist?

Firstly, .ino files aren’t used in ‘normal’ C++, they’re an Arduino-only thing.

Regular C++ only uses .h (‘header’) files and .cpp (‘source’) files.
(Technically header files can have any extension, but .h is the ‘official’/‘good practice’ extension. Some people do use others, like .hpp, but that just muddies the waters.)

Some people (mainly Arduino themselves) like to claim that ‘Arduino’ is actually a different language to C++, but the truth is that it’s really just C++ with an extra processing step: The Arduino toolset/toolchain (whatever you want to call it) actually has an extra preprocessor that combines all your .ino files into a single large .ino.cpp file (which is effectively an ordinary .cpp file) before passing the generated files to an ordinary C++ compiler (GCC).

.ino files have fewer rules than .h and .cpp files because the Arduino preprocessor does some work that would normally be the programmer’s responsibility.

For example, in C++ you usually can’t use a function before it has been properly declared, so if you wrote a function a that used a function b, function b would have to be ‘declared’ (though not necessarily ‘defined’) before a.

E.g.

// Declare b without defining it
void b();

// Define a
void a()
{
	// Uses b
	b();
	b();	
}

// Define b
void b()
{
	arduboy.println(F("Testing..."));
}

(Of course, in this case you could actually define b first, but sometimes that might be impractical or impossible.)


Why Use Multiple .ino Files

The kind of people who use lots of .ino files tend to be:

  • People who were introduced to C++ through Arduino
  • People who don’t use much C++ outside of Arduino
  • People who struggle with the rules/restrictions of ordinary .h and .cpp files
    • Disclaimer: ‘Struggle’ in the sense of finding them annoying or cumbersome, not ‘struggle’ in the sense of being incapable of working with them through lack of knowledge/understanding
  • People who don’t understand the rules/restrictions of ordinary .h and .cpp files and/or how the compiler uses them

Why Use Only One .ino File

I’m the kind of person who prefers to have only the one .ino file (the Arduino toolchain requires at least one .ino file as an entry point) because I was doing C++ long before I knew Arduino existed and I know the rules well enough that using .h and .cpp files gives me more control over the code.

I don’t actually like .ino files because the process that the preprocessor puts them through is a bit ‘mysterious’ (i.e. it isn’t well documented or easy to reason about), and it has actually broken for me before. (There are certain constructs that the Arduino preprocessor can’t handle properly, e.g. template functions have to be written a particular way or the .ino file preprocessor breaks them.)

When writing C++ for desktop, you wouldn’t even have an .ino file, you would only have .h and .cpp files, hence for me it makes more sense to have an arragement where I can pretend that the .ino file doesn’t exist.

I see how .ino files can be useful for beginners, because not having to worry about certain rules (e.g. declarations and definitions) can make it easier to start programming, but anyone who wants to use C++ in other environments is going to have to learn the full set of rules anyway, so it’s ultimately just a stop-gap.


.ino and .h Files vs .h and .cpp Files

Even if you use mainly .ino files, .h files tend to be a good option for storing your images, and occasionally other data like strings. (And they can actually be kept in separate folders, which I don’t think works with .ino files - I think .ino files all have to be in the root, or possibly the src directory.)

Though more or less anything you put in an .ino file can either be put in just a .h file or spread across an .h and .cpp file, hence it’s possible to have just the one .ino file. structs/classes, functions, variables of any kind (instances of structs, arrays…) et cetera.

Sometimes there are a few extra rules to be aware of, (e.g. a function declared only in a header should be prefixed with inline, non-const/constexpr globals often have to be declared in the .h file and defined in a .cpp file, you have to manually import any header file you use - .ino files implicitly import Arduino.h without telling you), but otherwise it’s conceptually similar.

There are rare occasions where using both a .h and .cpp is mandatory - particularly when you end up with what’s called a ‘mutual dependency’ - thing a depends on thing b and thing b depends on thing a and the only way to break the cycle is to put the declarations in two .h files and the definitions in two .cpp files. In these scenarios, an .ino file would be of no use.


Organising Your Files

The question of how to actually organise your .h and .cpp files is a different matter. Different people have different arrangements.

The one that suits me best for Arduboy games is to:

  • Have my state machine divided up into several classes, with one .h and one .cpp file per class .
    • Having both a .h and .cpp is necessary because of a mutual dependency between the state classes and my Game class - the Game class has an instance (a variable) of each state class, and each state class makes use of a reference to Game in its functions, which forms a mutual dependency that must be broken by splitting the classes into .h and .cpp files.
  • Keep all my images in separate .h files stored in a separate Images folder, which are then all imported via a single .h file called Images.h. The image variables themselves are kept in a separate namespace so I don’t have to worry about their names clashing with anything other than other images. (E.g. I would do something like Sprites::drawOverwrite(x, y, Images::player, 0) - the player array is kept inside the Images namespace.)
  • Keep all my strings in one or more header files in a Strings folder. Like with images, I have a Strings namespace to avoid name clashes. However, I also have some extra pipelining to make localisation (i.e. translation into different languages) possible. That’s something I’ve only actually done once as an experiment to prove that it’s possible, but I keep the framework around because there’s no (runtime) overhead for doing so.
  • Have a Utils folder that contains a bunch of headers with handy utility functions. Particularly a function that allows me to extract the size of an array, and some functions that make it easier to work with strings stored in progmem (in particular, to make it possible to feed a const/constexpr array of char stored in progmem to the Arduboy’s print and println functions).

I prefer using classes (or structs - there’s only one tiny difference between them) to not using classes because classes keep relevant information together (something namespaces also do) and have access restrictions which prevents other code from accessing things it shouldn’t have access to (something namespaces don’t do, or at least not as easily).

The best game to use as a reference to examine how I structure things is actually the joke ‘game’ Love Tester (a reference to a certain Nintendo novelty) that I made for a game jam. Firstly because it’s very simple, and secondly because some of my other games don’t quite follow the structure properly because I developed it over time and didn’t get around to updating my old games with my newer discoveries.


The Short Answer

So the short answer is to start with .ino files, then start using .h files as your programs grow larger, and if you plan to ever do anything with C++ outside the realms of Arduino or you become advanced enough that you start encountering mutual dependency scenarios, then it’s time to learn more about .h and .cpp files and the rules that govern them, and then decide for yourself if you prefer that approach.

7 Likes

Fantastic overview. Thanks!

1 Like