Usually, we do declare but not define a global variable in the header file. However, we define templates in it. Then the issue arises: is it possible to define a global variable template?
template <uint8_t PinCode>
uint8_t BitMask = digitalPinToBitMask(PinCode);
In reality, a global variable template:
After longer research, I finally found the most elegant solution. The definition style depends on whether you want to initialize the variable.
If the variable does not need initialization, you only need a header file:
template <int TimerCode>
int TCCRB;
Yes, it is and must be so simple. Don't add "static" or "extern" keywords like we usually do for variables in a header file. It will pass compilation and work as a global variable template among all CUs. Same instances will share the same actual variable, i.e., changes in one CU will affect other CUs, as long as they have the same template arguments.
As we know, if you define a variable with no necessary keywords like "static" or "extern" in a header file, it will cause a redefinition error if the header is included in more than one CUs. "static" tell the compiler to copy this variable for each CU as individual variables, so changes in one CU won't affect that in another CU. Instead, "extern" tells the compiler that this variable here is only a declaration. It's defined and owned in only one of the CUs, and other CUs should only keep a reference or symbol to it. So that changes in one CU WILL affect other CUs.
However, a variable template is neither a static definition nor an extern declaration. It's a special instruction to the compiler: find all cases of references to this template, combine those of the same template arguments, and generate one definition automatically for each unique instance! This time the compiler takes the responsibility to avoid redefinition: it scans all CUs and then generates unique definitions itself!
This automatic definition is so convenient if you don't want to give the variable an initial value, and if there are too many possible instances to be listed one by one. However, if you do want an initial value, you'll have to define it yourself, and you'll need an individual CU to own these variables to avoid redefinition:
//In the header file:
template <int TimerCode>
extern int TCCRA;
//In the CPP file:
template <>
int TCCRA<1> = 2;
template <>
int TCCRA<2> = 5;
//Naturally you have to provide initial values for all possible instances …
For this case, the "extern" keyword is necessary because you explicitly defined all valid instances in your specially provided CU, and other CUs must refer to this definition. The compiler shouldn't generate a random definition itself. This is very much like a normal global variable definition, only adding some template grammars. However, the user can only use instances provided in your CPP file. This is also very natural because only for known instances can you provide an initial value.
I found very few tutorials on this topic on the Internet. I hope my experience will help more people ~
The standard defines many global constants which are templatized such as the type traits type_trait_v constants.
They are defined as such in the header
template<class T>
inline constexpr some_type some_variable = ...;
In your example you could do something like
template<uint8_t Pin>
inline constexpr uint8_t BitMask = digitalPinToMask(Pin);
For this to work then digitalPinToMask must be invocable in a constexpr context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With