Usage of macros inside class header files

Hi, I was wondering if it is a good practice to have #define macros in a class .h file. For example, I would have a class that manages user buttons. The class can be used in different applications, I'd like to think of it as a black box, as the developer only use its high level functions to interface with the buttons.

so a macro could be used to set which hardware implementation is in use in the current project, which would in turn change how the class is built:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define SHIFT_REGISTER_USED 1

class tact {
public: 
  tact() {};
  void read();
private:
  uint8_t _pin;
}

void tact::read()
{
uint8_t input;
#if SHIFT_REGISTER_USED 
        input = (1 << _pin)) >> _pin;
#else
        input = _pin;
#endif
}


Would that be a good practice? since it breaks the idea of the black box, the header file cannot simply be reimplemented as-is in different applications and the user needs to get in the .h file and setup some macros.
Is this a good practice? The only alternative I can think of is setting the macro as a constructor argument, but it will be redundant for every instance

Thanks!!
Well you wouldn't write #define SHIFT_REGISTER_USED 1 anywhere in the code.

The place to put those would be in the IDE project / makefile.
It's a build-time decision chosen by the project builder.
Eg, in code::blocks https://imgur.com/a/nK6njqJ

The #if's are fine, although you might want to go with
#ifdef SHIFT_REGISTER_USED
Some tools can be a bit funny seeing #if VAR when VAR hasn't been defined at all.

If you're planning on creating lots of them, make sure they have a consistent naming convention so people know where to look and how to define them.
As @salem suggests, more frequently one sees
1
2
3
4
#if ! defined SHIFT_REGISTER_USED
#  define SHIFT_REGISTER_USED 1 // default behavior: use the shift register
#endif
// ... 

This gives the user the ability to override the default by
a.) defining the macro before including the header; or
b.) defining the macro on the command line (for example, with option -DUSE_SHR=1).
It could be that the default behavior is to raise an error via #error, informing the user that they must define it to something.

Alternatively, instead of #ifs, there may be a #include "config.h" at the top of the header, where config.h is a header file generated by a build tool like autoconf or cmake.
https://cmake.org/cmake/help/latest/command/configure_file.html
These tools are probably overkill for most projects.

You certainly want to limit the amount of conditional compilation. Try to provide a few big switches, not a million tiny knobs. You're on the hook to support every possible combination of choices.

If there are several distinct options, consider writing entirely separate implementation files, and compiling just one of them into the executable.
Last edited on
Ok thank you!

for now I will go with different implementations, it seems very reasonable for the size of the class, and I am also limited as I use platformIO for compilation and I do not have the freedom to write makefiles for the embedded environments I am using. I have not thought of a critical reason to use the #ifs, so they will easily be converted into either different functions or classes

Thanks again
Topic archived. No new replies allowed.