3

Say I'm working on a library^1 to which I want to add support for SD.h^2 but knowing for a fact that many microcotrollers don't support SD.h (therefore they result in compilation errors [eg. Attiny85]) I don't want to just #include <SD.h> (or even #ifndef __SD_H__ then #include <SD.h>) in Mylibrary.h and make it totally unusable for some microcontrollers. So I was wondering, is it considered ok to just add the functionality like...

// Mylibrary.h

#ifdef SD_H // Save functionality for those including SD.h to their sketch.ino void Mylibrary::save(String filename){ SD.open(filename, FILE_WRITE) // ... } #endif

...without ever including the SD.h in Mylibrary.h and hence force the user whenever he\she wants to use some SD functionallity, to just include SD.h in his\her sketch.ino? +(it sound exhausting to do something like this for every MCU, as an alternative way)

[1] (in my case this library)
[2] (in my case something related to this)

PS. The reason I'm asking is because I don't remember seeing it anywhere being used that way and I'm slightly confused as to if this is the right or wrong thing to do so

Giorgos Xou
  • 141
  • 8
  • Chat-GPT says it's reasonable, but I really want to know your thoughts about how would you approach such an issue – Giorgos Xou Nov 05 '23 at 20:10
  • 9
    Do not trust ChatGPT, always assume it is hallucinating. – the busybee Nov 06 '23 at 06:29
  • It is bad and I have seen it :) – Carsten S Nov 06 '23 at 10:36
  • 2
    Note that identifiers that start with two underscores (or one underscore and a capital letter) are reserved: "All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use." – Andrew Henle Nov 06 '23 at 14:04
  • Since you asked ChatGPT, I asked it as well: "in C++ is it good practice to put all of your code in a .h file?" And the answer was: "No, it is not considered good practice to put all of your code in a .h (header) file in C++. In C++, code is typically organized into header files and source files, and there are specific conventions for what should go in each type of file." So no, putting all your code (declarations and definitions, that is, function prototypes and implementations) is not considered good practice. You can search other parts of the Internet for other opinions on that. – Nick Gammon Nov 07 '23 at 10:54
  • @NickGammon, When I started building this library, there wasn't ChatGPT available. Based on my research (and sure also empirically) that's what I found out that it was working. Also, I once had them seperate I later decided that I had to merge them into the .h for the sake of better MACRO optimization – Giorgos Xou Nov 07 '23 at 15:59

2 Answers2

7

Yes, it is in general a bad practice to use include guard define to detect if the header file is included. It will work only in a header file in a compilation unit which includes the detected header file.

In Arduino all ino files of the sketch are one compilation unit. But every C or C++ file is a separate compilations unit. They would not 'see' the include guard from an include in the ino file(s).

If you want to use the include guard define, don't relay on it exclusively. Use your own define and set it if you detect the include guard. This way user can set it if theirs SD library has a different name for the include guard.

#ifdef __SD_H__
#define XY_USE_SD_LIB
#endif

#ifdef XY_USE_SD_LIB // Save functionality for those including SD.h to their sketch.ino void Mylibrary::save(String filename){ SD.open(filename, FILE_WRITE) // ... } #endif

I use this in my popular ArduinoOTA library here.

There is other way to detect includes and it works across compilation units, but it doesn't work on all Arduino platforms. It is the __has_include 'directive'. I use it here.

Juraj
  • 18,037
  • 4
  • 29
  • 49
2

I don't think this will work, however you could try it and see. Just set the board type to something that doesn't support SD.h.

My problem would be that your library is a separate compilation unit (ie. file) that would be compiled against the things you put as header files.

If your library user uses SD.h in the main .ino file, then your library (which is compiled separately) would not know about that. In other words, it wouldn't know whether or not to implement the Mylibrary::save function.

I think a fairly simple solution would be to put a define in the include file for your library, eg.

#define USE_FILE_SAVING false

Then check that in your code. You don't need a whole list of MCUs, just inform the user that if they want file saving implemented to change that one line.


For more information see How the IDE organizes things

Nick Gammon
  • 38,184
  • 13
  • 65
  • 124
  • Thanks! it's really interesting though, I also just realised that it depened if it works or not based on which board i select (eg. on arduino uno works, with espressif's ESP32 it doesn't consider that SD.h is already defined) – Giorgos Xou Nov 05 '23 at 20:52
  • Wait It's so weird, it copiles just fine on both boards but with ESP32 the #ifdef __SD_H__ #pragma message("YES SD.h") #else #pragma message("NO SD.h") #endif says "No ..." – Giorgos Xou Nov 05 '23 at 20:57
  • 1
    Adding SD.h to your main project is not going to affect how your library compiles. That is a separate compilation unit and does not look at what includes you have in your .ino file. See: https://arduino.stackexchange.com/a/13182/10794 – Nick Gammon Nov 06 '23 at 06:14
  • @GiorgosXou This isn't a library, though, right? Of course it affects a compilation unit. You are showing in that GIF that putting the include inside a file in a compilation unit (that is, the file we are looking at) will of course affect how that file is compiled. But if you put that include in file A it won't affect how file B (your library) is compiled. – Nick Gammon Nov 07 '23 at 04:51
  • Your library is compiled separately (in another compilation) from your .INO file. The library cannot know what includes you may or may not have put in your .INO file. – Nick Gammon Nov 07 '23 at 04:53
  • My whole library is in the .h file. There are no seperate cpp files, I just organise each class implementation with #pragma region file.cpp inside the .h (for the exact reason of extreme macro optimization) – Giorgos Xou Nov 07 '23 at 08:39
  • "The library cannot know what includes you may or may not have put in your .INO file." in many cases like #ifdef __SD_H__ it knows, in other cases like @juraj said you have to use __has_include like #if defined __has_include #if __has_include(<SD.h>) #include <SD.h> .... But from what I understand, you shouldn't 100% only depend on those – Giorgos Xou Nov 07 '23 at 09:21
  • OK, so the whole library is in a .h file? Then rather than asking whether it will work or not (which you can find out for yourself empirically) how about telling us whether or not it works, and if not, ask us why not? – Nick Gammon Nov 07 '23 at 10:49
  • I thought I made it clear, I even had a comment // Mylibrary.h on my code snippet. I didn't ask "whether it will work or not", I asked "how would you approach such an issue" because empirically, I know I make mistakes... – Giorgos Xou Nov 07 '23 at 15:51