If the header file supplied by a vendor of something with whom one's code must interact is deficient in some way, in what cases is it better to:
- Work around the header's deficiencies in the main code
- Copy the header file to the local project and fix it
- Fix the header file in the spot where it's stored as a vendor-supplied tool
- Fix the header file in the central spot, but also make a local copy and try to always have the two match
- Do something else
As an example, the header file supplied by ST Micro for the STM320LF series contains the lines:
typedef struct
{
__IO uint32_t MODER;
__IO uint16_t OTYPER;
uint16_t RESERVED0;
....
__IO uint16_t BSRRL; /* BSRR register is split to 2 * 16-bit fields BSRRL */
__IO uint16_t BSRRH; /* BSRR register is split to 2 * 16-bit fields BSRRH */
....
} GPIO_TypeDef;
In the hardware, and in the hardware documentation, BSRR is described as a single 32-bit register. About 98% of the time one wants to write to BSRR, one will only be interested in writing the upper half or the lower half; it is thus convenient to be able to use BSSRH and BSSRL as a means of writing half the register. On the other hand, there are occasions when it is necessary that the entire 32-bit register be written as a single atomic operation. The "optimal" way to write it (setting aside white-spacing issues) would be:
typedef struct
{
__IO uint32_t MODER;
__IO uint16_t OTYPER;
uint16_t RESERVED0;
....
union // Allow BSRR access as 32-bit register or two 16-bit registers
{
__IO uint32_t BSRR; // 32-bit BSSR register as a whole
struct { __IO uint16_t BSRRL, BSRRH; };// Two 16-bit parts
};
....
} GPIO_TypeDef;
If the struct were defined that way, code could use BSRR when necessary to write all 32 bits, or BSRRH/BSRRL when writing 16 bits. Given that the header isn't that way, would better practice be to use the header as-is, but apply an icky typecast in the main code writing what would be idiomatically written as thePort->BSRR = 0x12345678;
as *((uint32_t)&(thePort->BSSRH)) = 0x12345678;
, or would be be better to use a patched header file? If the latter, where should the patched file me stored and how should it be managed?
Edit
In this vendor's data file, I/O device registers are defined by using structure definitions like the above with macro definitions like #define GPIOB (*((struct GPIO_TypeDef*)GPIO_BASE + 0x100))
. Because GPIOA
, GPIOB
etc. are defined as macros rather than identifiers, any macro substitution of GPIO_TypeDef
which goes into effect after the structure is defined would affect future uses of that type but also apply to GPIOA
, GPIOB
, etc. Thus, my present inclination would be to define my own project-wide header file which #include
s the vendor's file, and then defines my own structure and macro-substitutes my structure's name for the vendor's. That would allow code to be written to use the registers in the normal idiomatic fashion while using the vendor-supplied definitions for just about everything else, and while providing a clear patch-point.