I'm working with legacy embedded C code which defines the types uint8_t, uint16_t and uint32_t in a header file using the typedef keyword.
For discussion, let us say the file typedefs.h contains these definitions.
In my new C source module, I include stdint.h. I also include other header files which include typedefs.h somewhere in the hierarchy. As expected, the compiler complains about multiple defined symbols.
I would like to modify the legacy file typedefs.h so that it only declares the uint*_t types if either stdint.h is not included or better if the uint*_t types are not defined.
My understanding is that #ifndef cannot be used since typedef is not a preprocessor directive.
So how do I tell the compiler to not define the uint*_t if they already exist (or if the stdint.h is already included)?
Note: this would be easy if the C specification defined standard include guard definitions for the header files.
FWIW, I am using Green Hills compiler, 4.24, for an ARM9 processor.
I beleive that the stdint.h should also be defining a macro for the limits of the types that it defines. You should be able to test for those using a #ifdef and the like.
#ifndef UINT32_MAX
typdef ... uint32_t;
#define UINT32_MAX ...
...
#endif
Edit: Originally used UINT32_MIN, but as Jens Gustedt poited out this is the one combination of signed/unsigned and min/max that doesn't occur.
Just fix the legacy header to always include stdint.h to get these types, remove the duplicate definitions, and provide a drop-in file stdint.h for broken systems that lack it.
If you're on a UNIX system, then you should back-up a step and use a configuration package like autoconf(1) or automake(1). It's designed to handle problems like this.
Related
To better understand how libc works, I was reading the FreeBSD source code. And there I stumbled upon the definition of __uint8_t in the sys/_types.h file. I can understand definitions of types like u_int8_t for compatibility with old code, but a special _types.h file where all types start with __? Why are these types defined in the _types.h file that accompanies types.h and not just types.h altogheter with other types like standard uint8_t?
(I assume that these types with __ at the beginning are not defined to be compatible with the old code because it is not written in the comment)
Why does __uint8_t exist?
To provide access to the implementation's 8-bit unsigned type that does not collide with user code.
a special _types.h file where all types start with __
__ names are reserved by the implementation, so using that name will not collide with user code.
Why are these types defined in the _types.h file that accompanies types.h and not just types.h altogether with other types like standard uint8_t?
So the implementation can include its include its own files without including the various STD C .h files.
uint8_t is defiifned in <stdint.h>. Defining __xxx_t in there too would bring in definitions other definitions that may conflict with user code. Compliant user code is not required to include <stdint.h>.
For example, C11 dictates that size_t should be declared in the following header files:
stddef.h
stdio.h
stdlib.h
string.h
time.h
uchar.h
wchar.h
When reading C11, I found there are many other data types declared in more than one standard header files.
Questions
Let's say in the case of size_t. Why not just in stddef.h for simplicity?
Let's say a C compiler implements size_t in those header files. Are they guaranteed to have the same definition in those header files?
As an example of a function declared in stdio.h that requires size_t be predeclared, consider snprintf(). As it is, if you want to use it in your code, all you need to do is #include <stdio.h>. If size_t were declared only in stddef.h, you would have to
#include <stddef.h>
#include <stdio.h>
Not only that, but since stdio.h declares snprintf whether you use it or not, you would have to include both files every time you needed anything in stdio.h to avoid compiler errors; stdio.h would have an artificial dependency on stddef.h. That causes your source code to become longer and more brittle (notice that if you reverse the order of the two directives it would also break). Instead, we write header files so that they stand alone and do not depend on other headers, and this is what the C standardization committee decided on for the standard library.
Let's say in case of size_t. Why not just in stddef.h for simplicity?
The type is used in the declaration of functions in all those files. If it wasn't declared in <stdio.h> you would get a compilation error unless you first include <stddef.h>.
Let's say a C compiler implements size_t in those header files. Are they guaranteed to have the same definition in those header files?
Yes, they will have the same definition. Usually, the value is defined in a single place in a separate include file that is included by the others.
In some cases it may be possible to modify the definition with compiler options or defines, for example a compiler that allows 32/64 bit compilation may define size_t as a 32 or 64 bit unsigned entity depending on the target defined on the compiler command line.
There's a subtle difference between by and in - an implementation is completely free to define size_t in a single header, as long as it's defined when the specified headers are included. So, you have two options for that:
Define size_t in every single one and wrap each one in include guards
Define it in a single file, and wrap it in include guards
And yes, size_t must be defined as specified, which is (glibc):
typedef unsigned long size_t;
or
typedef unsigned int size_t
They don't say you have to be sane, they just say it needs to be defined at the time anyone includes one of those headers, because they depend on it being defined and can be used independently. Put simply, if you define something dependent upon size_t, then size_t must first (previously) be defined.
How (or rather, where) you do it is up to your implementation.
First of all, when one does a #include <stdio.h> there is no requirement that there actually exist a file anywhere called stdio.h, or that the compiler do anything with such a file. Rather, the requirement is that such a line must cause all identifiers which are specified as being associated with <stdio.h> to be defined according to specification. It would be perfectly legitimate for a compiler that saw #include <stdio.h> simply enable the use of certain identifiers that were hard-wired into the compiler. Because the easiest way for compiler vendors to make things behave as the spec requires is to have #include <stdio.h> directives run the text of some file stdio.h through the preprocessor, that's what many compilers do, but that's not required.
When the spec lists "files" where size_t should be declared, what it's really saying is that an #include directive that names any one of those files should create that identifier in global scope. That could be done by having files with all the listed names incorporate a definition of size_t, or by having size_t be built into the compiler but only enabling the built-in definition of the compiler sees a #include directive with one of the indicated names.
If a project has a header that includes a second header and both of them include a common header, is it good practice to drop the inclusion of the common header from the first header and rely on the indirect inclusion via the second header?
e.g.:
I know the stdint.h can be removed from temperature.h, but should it be?
In temperature.h:
#include <stdint.h> // *Should* this include be removed in this case.
#include "i2c.h"
extern uint16_t temperatureRead (i2cData_t x);
In i2c.h:
#include <stdint.h>
typedef struct i2cData_t {
uint16_t exampleMember
} i2cData_t;
Generally you want your modules to be self contained. If your module relies on something in a header then include it. Temperature.h may one day decide it doesn't need to include stdint.h any more and have it removed. Your code should have nothing to do with that decision, so safeguard it by including stdint.h, i.e. be self contained.
Use header guards (or C++ pragma once) to make sure your compilation speed doesn't degrade due to including a header multiple times.
IMO, no.
Always assume a module is standalone. In above case, temperature.h requires stuffs from both stdint.h and i2c.h so let it be. In case a refactoring happens and i2c.h no longer includes stdint.h, you can avoid compilation problem. It's simple to fix for a small project, but not quite for a big one.
I know the stdint.h can be removed from temperature.h, but should it be?
Each source file should include the headers that it explicitly requires. In the posted case, if the type of exampleMember changed to be int and ic2.h no longer includes stdint.h then the compliation of temperature.h fails, even though its source is unchanged. Or, if temperature.h is used by other source files then those other source file must include stdint.h in order for compliation succeed.
What's the best way to ensure that stdint.h is included for headers that use the types from that header?
The alternatives I can see are:
including stdint.h in the header itself (dangerous, is it not?)
flagging something to the compiler if it's not included when parsing the header (something like #ifndef _STDINT_H with an accompanying #error "stdint.h not included"
Educating the other developer in always including stdint.h before any of my headers that require it, either using comments, conversation, documentation or a mixture of all three (the last one being preferable).
If there are any other alternatives worth mentioning, feel free to do so - I'm just guessing at what I should do here.
If you use types from stdint.h in your header, you really should include stdint.h in that header.
It is generally preferred to include the least amount of files from a header to reduce compile time. Therefore, whenever possible, you should forward declare types in headers. Since the types from stdint.h cannot be forward declared (they're just typedefs of primitive types), there is no other option than including stdint.h in the header.
I my oppinion, including header files in header files is ok if you use include guards.
Something like
# ifndef HEADER
# define HEADER
# include header.h
# endif
What are some of the best ways to manage redundant typedefs used for platform independence from multiple middleware (operating systems, protocol stacks) vendors in the C programming language.
e.g.:
target.h
/* inclusion lock etc */
typedef char CHAR;
typedef unsigned char BYTE;
typedef unsigned short int WORD;
/* ... more of the same ... */
OS_types.h
/* inclusion lock etc */
typedef char CHAR;
typedef unsigned char BYTE;
typedef unsigned short int WORD;
/* ... more of the same ... */
At some point the compiler recognizes that it has two redundant typedef symbols and bails out with an error because this is simply not allowed by definition in C.
One possible way to do this without modifying the vendor's header would be to use the preprocessor with some header wrappers, e.g.
mytypes.h
#define BYTE VENDOR1_BYTE
#include <vendor1/types.h>
#undef BYTE
#define BYTE VENDOR2_BYTE
#include <vendor2/types.h>
#undef BYTE
typedef unsigned char BYTE;
This would result in the vendor's code generating different typedefs but hopefully mapped to the same actual type (unsigned char in the example). If the vendors are using different underlying types for the same type names then the method will likely not work.
That's a toughie. If I had to do something, I'd probably hold my nose and modify the third-party header-files -- possibly using macros to obtain conditional compilation of the offending typedefs.
Good luck.
If the vendor is responsive to feedback, you could beg them to move those generic type definitions into a separate file, e.g. types.h. If they're isolated in a separate file, it's much easier to manage. The solution could be as simple as removing their types.h and adding your own project-specific types.h which can do whatever it needs to do in your project.
Even better, beg them to use the standard C typedefs in stdint.h, i.e. uint16_t.
Otherwise, I'd suggest a modification to the vendor header files, done as cleanly as possible so it's easy to re-do when they next release code. Of course this all goes in your VCS so you can track exactly what changes you made!
One approach, although it could be a lot of work, is to build your own "wrapper" layers which provide only the functionality you need from each of the middleware vendors. If you keep each wrapper in its own compilation unit (.c file) that's the only place you'll need to refer to the vendor's header file. That gives you a way to prevent the conflicting types from "leaking" into your application, as you can use your own typedefs and translate them to the vendor-specific types in the wrapper.
As Steve suggested, modifying the header files might be the best solution, depending on how often the vendor ships new versions of their stuff. The overhead could get pretty high.
If you have the option to use C++ compilation for your own code (even if it is essentially C code) you could create namespace wrappers thus:
vendorA_target.h
namespace vendorA
{
extern "C"
{
#include <target.h>
}
}
vendorB_OS_types.h
namespace vendorB
{
extern "C"
{
#include <target.h>
}
}
Then in your own code. include these headers in place of the originals, and use scope-resolution, or if you are certain that types with the same name have identical or compatible definitions, simply us a using directive:
using vendorB::WORD
WORD timeout = 100 ;
vendorA::WORD x = 0xffff ;
Note that the extern "C" wrappers are not necessary if the headers already have them internally in __cplusplus macro conditionals - but it won't hurt.
Using C++ to compile C code imposes no overhead, but it does have stricter type comformaty checking, which while good for your code quality, may cause other headaches; especially if the third-party headers contain code that is invalid as C++. If the headers already have extern "C" declarations in __cplusplus macro conditionals, then they are already intended to be "C++-ready" and you may not have any such problems.
Unfortunately this method will not solve the problem of preprocessor macros with the same name. If you have that problem, you may have to #undef the macros from one header before including the other, or modify the headers.