and I would like them to share a global variable to save memory and avoid reallocation of local variables.
It works if I define timeinfo in the main.cpp and then do extern in the respective headers.
If I instead declare it in the header of libA and include this header in libB, I get linking errors, I assume because separate variables are created for the compiled output of libA.cpp and libB.cpp.
Is there also any way to keep the main.cpp unaware of timeinfo?
Thanks! That makes sense and works. It also nicely untangles the two libs. Until now I could live with libB depending on libA, because libA is strictly time, and libB just uses time in some functions, so it makes sense that there is a dependency, but I noticed that actually, no functions from libA are called there.
One question though:
The precompiler makes the quoted part to
extern struct tm timeinfo;
struct tm timeinfo;
What does that actually mean and (why) do I have to do it? Isn't this saying
"Don't worry about timeinfo, it will be declared in another translation unit." immediately followed by
"Please declare timeinfo!"?
I tried to instead just include <time.h> and it seems to work (though I currently cannot test on hardware).
Is there any more reason in this that the fact that the external linkage followed by the actual declaration is useless but works and it is cleaner if the cpp-file actually gets the same header as the compilation units that use it?
Even if I would want to accept the considerable increase in compile time associated with this, I cannot even completely do this because I have circular dependencies in objects (e.g. observer patter in in my MQTT wrapper) and AFAIK that is not possible with header-only files. On the other hand, I also have template classes where it was necessary.
ElCaron:
What does that actually mean and (why) do I have to do it? Isn't this saying
"Don't worry about timeinfo, it will be declared defined in another translation unit." immediately followed by
"Please declare define timeinfo!"?
My only "answer" is that's how I've seen in done in the vast majority of cases by people who write code (for a living) in industry working on large software projects. It has worked for me, so I never worried about it too much.
Even if I would want to accept the considerable increase in compile time associated with this [breakout into cpp files],
Not sure I understand this. I was working on a project with 11,000+ lines of code, but spread out over 19 ccp and h files. When I compiled the project for the first time in the morning, it would take almost a minute to compile it...and that's on a pretty fast machine. Then, as is often the case, I would work on a single file that I might be testing/debugging and then recompile. The recompile time was virtually instantaneous. Evidently, the GCC compiler does incremental compiles, only recompiling those files that have a dirty flag set.
Also, breaking the cpp files into logical components (e.g., sensorRead.cpp, displayMgt.cpp, etc.) makes it easier to locate code in large projects.
Not sure I understand you. We seem to say the same.
Put defintions (thanks gfvalvo) into separate cpp files -> good, because then only the changed cpp files will be compiled.
Put definitions hpp files -> everything needs to be compiled each and every time, because everything is stitched together to a single file by the precompiler.
Edit: oh, now i do. Not
this [breakout into cpp files]
but
this [writing header-only libraries]
Also, breaking the cpp files into logical components (e.g., sensorRead.cpp, displayMgt.cpp, etc.) makes it easier to locate code in large projects.
That, again, would practically work the same with breaking them into separate hpp files.
Putting everything into one file is ludicrous, out of question. The very poor handling of multiple files by the Arduino IDE was the main (but far from the only) reason that drove me to PlatformIO (on Visual Code).
I never looked back. It is SO much better in any regard. I can't believe why I put up with that for so long.
Put definitions hpp files -> everything needs to be compiled each and every time, because everything is stitched together to a single file by the precompiler.
No, the hpp (or h) files contain data declarations, not data definitions. So an array that is used in multiple files (e.g., myArray[]) would be written as:
extern int myArray[];
in the header file, but in the INO file it might appear as:
int myArray[] = {1, 2, 3};
Indeed, if you try to put an initializer list in the header file with the extern keyword, you'll draw a redefinition error. Header files should contain declarations, not definitions. This allows you to work with any given data item across multiple source files. The compiler can then allow you to work with that data item and the linker is left to sort things out as to the actual memory address for the data item.
econjack:
No, the hpp (or h) files contain data declarations, not data definitions.
No, please read what I write. Robin2 and I where explicitly talking about header-only libraries, where all definitions are placed in the header file.
This can be done with the stated disadvantages in compile time (and some advantages, because the compile can inline functions automatically where deemed advantages),
it HAS to be done when dealing with templates,
and it CANNOT be done with circular dependencies in objects (to my knowledge, I could not get it working and search suggested that separation was necessary).
Due to the requirement with templates, it is actually done in some quite prominent libraries like Boost.
BTW, I just learned this: [dcl.inline]
So if one writes header only libraries, one should still separate definition of the class and definition of the methods, because inline methods are in general not what one wants.
Just saying because that may not be clear to everyone, especially someone coming from other languages like Java.
ElCaron:
So if one writes header only libraries, one should still separate definition of the class and definition of the methods, because inline methods are in general not what one wants.
Even if you separate them, they will still have to be inline. There's no other way if you're using a header-only library. If you don't make them inline, you'll get multiple definition errors.