I started writing a simple interpreter in C++ with a class structure that I will describe below, but I quit and rewrote the thing in Java because headers were giving me a ha
Most header files should be wrapped in an include guard:
#ifndef MY_UNIQUE_INCLUDE_NAME_H
#define MY_UNIQUE_INCLUDE_NAME_H
// All content here.
#endif
This way, the compiler will only see the header's contents once per translation unit.
C/C++ compilation is divided in compilation/translation units to generate object files. (.o, .obj)
see here the definition of translation unit
An #include directive in a C/C++ file results in the direct equivalent of a simple recursive copy-paste in the same file. You can try it as an experiment.
So if the same translation unit includes the same header twice the compiler sees that some entities are being defined multiple times, as it would happen if you write them in the same file. The error output would be exactly the same.
There is no built-in protection in the language that prevents you to do multiple inclusions, so you have to resort to write the include guard or specific #pragma boilerplate for each C/C++ header.
Why can I #include a million times, even one after the other, but I can only include my header once?
It is probably because your header doesn't have an include guard.
// printer.h file
#ifndef PRINTER_H_
#define PRINTER_H_
// printer.h code goes here
#endif
Note that it is best practice to chose longer names for the include guard defines, to minimise the chance that two different headers might have the same one.