Barr Code
Comment
Weatherhead
I totally disagree with the last rule "DON'T expose the internal format of any ...
FillG
Okay Michael, now what about documentation guidelines for include files. I ...
What belongs in a header file
Michael Barr
5/11/2011 6:44 PM EDT
What sorts of things should you (or should you not) put in a C language .h header file? When should you create a header file? And why?
When I talk to embedded programmers about writing device drivers or using real-time operating systems or my Embedded C Coding Standard book, I often come to see that many lack basic skills and information about the C programming language. This is probably because we are mostly a gang of electrical engineers who turned to programming and are self-taught in C (and C++ and the myriad other programming languages we make use of).
Dos and don'ts
In the interest of promoting the general welfare, I'd like to use this month's column to discuss one of those basic skills that is too often lacking: building proper header files. Here's my list of Do's and Don'ts for embedded C programmers to follow when creating .h header files.
DO create one .h header file for each "module" of the system. A module may comprise one or more compilation units (e.g., .c or .asm source code files). But it should implement just one aspect of the system. Examples of well-chosen modules are: a device driver for an A/D converter; a communication protocol, such as FTP; and an alarm manager that is solely responsible for logging error conditions and alerting the user of the active errors.
DO include in the header file all of the function prototypes for the public interface of the module it describes. For example a header file adc.h might contain function prototypes for adc_init(), adc_select_input(), and adc_read().
DON'T include in the header file any other function or macro that may lie inside the module source code. It is desirable to hide these internal "helper" functions inside the implementation .c file. If it's not called from any other module, hide it! (If your module spans several compilation units that need to share a helper function, then create a separate header file just for this purpose.) Module A should only call Module B through the public interface defined in moduleb.h.
DON'T include any executable lines of code in a header file, including variable declarations. But note it is necessary to make an exception for the bodies of some inline functions, about which more below.
DON'T expose any variable in a header file, as is too often done by way of the extern keyword. Proper encapsulation of a module requires data hiding: any and all internal state data should only be in private variables inside the .c source code files. Whenever possible these variables should also be declared with keyword static, which will enlist the linker's help in hiding them.
DON'T expose the internal format of any module-specific data structure passed to or returned from one or more of the module's interface functions. That is to say there should be no "struct { … } foo;" code in any header file. If you do have a type you need to pass in and out of your module, so client modules can create instances of it, you can simply "typedef struct foo moduleb_type" in the header file. Client modules should never know, and this way cannot know, the internal format of the struct.
Inline functions
On a related note, I recently received this question from an engineer in Brazil:
This is a good question, as it seems at first to be about a conflict between my bug-killing rules and something else I refer to as "Generally Accepted Programming Principles" (i.e., GAPP).
The inline keyword is a part of the C++ programming language that was added late to C (in C99). In C++, most programs are built out of classes–with GAPP dictating one header file per class definition. Any C++ function may be declared inline. However, if the inline function is a public member function (a.k.a., "public method") of the class it is necessary to place the code for the inline function in the header file. (This is so that all of the other modules that use the class can see the code they need to have placed inline by the compiler.)
Of course, placing the body of any function inside a header file conflicts with GAPP for the C programming language. Here's a set of rules to help you decide where to put the code for inline functions in C.
IF the inline function is a "helper" function that's only used inside one module, THEN put it in that .c file only and don't mention it in the header file. This is consistent with Rule 4.2.c of my Embedded C Coding Standard, which says that "The header file shall identify only the [functions] … about which it is strictly necessary for other modules to know."
IF, however, the inline function operates on the abstract data type defined in the header file and must be visible to two or more modules, THEN put the body of the inline function inside the header file. There is no rule in the Embedded C Coding Standard that strictly prohibits this, so there is no conflict after all.
Though not really specific to embedded software development, I hope this advice on good C programming practices is useful to you. If it is please let me know and I will provide more C advice like this in future columns.
Michael Barr is the author of three books and over sixty articles about embedded systems design, as well as a former editor-in-chief of this magazine. Michael is also a popular speaker at the Embedded Systems Conference, a former adjunct professor at the University of Maryland, and the president of Netrino. He has assisted in the design and implementation of products ranging from safety-critical medical devices to satellite TV receivers. You can reach him via e-mail at mbarr@netrino.com or read more of what he has to say at his blog (www.embeddedgurus.net/barr-code).
When I talk to embedded programmers about writing device drivers or using real-time operating systems or my Embedded C Coding Standard book, I often come to see that many lack basic skills and information about the C programming language. This is probably because we are mostly a gang of electrical engineers who turned to programming and are self-taught in C (and C++ and the myriad other programming languages we make use of).
Dos and don'ts
In the interest of promoting the general welfare, I'd like to use this month's column to discuss one of those basic skills that is too often lacking: building proper header files. Here's my list of Do's and Don'ts for embedded C programmers to follow when creating .h header files.
DO create one .h header file for each "module" of the system. A module may comprise one or more compilation units (e.g., .c or .asm source code files). But it should implement just one aspect of the system. Examples of well-chosen modules are: a device driver for an A/D converter; a communication protocol, such as FTP; and an alarm manager that is solely responsible for logging error conditions and alerting the user of the active errors.
DO include in the header file all of the function prototypes for the public interface of the module it describes. For example a header file adc.h might contain function prototypes for adc_init(), adc_select_input(), and adc_read().
DON'T include in the header file any other function or macro that may lie inside the module source code. It is desirable to hide these internal "helper" functions inside the implementation .c file. If it's not called from any other module, hide it! (If your module spans several compilation units that need to share a helper function, then create a separate header file just for this purpose.) Module A should only call Module B through the public interface defined in moduleb.h.
DON'T include any executable lines of code in a header file, including variable declarations. But note it is necessary to make an exception for the bodies of some inline functions, about which more below.
DON'T expose any variable in a header file, as is too often done by way of the extern keyword. Proper encapsulation of a module requires data hiding: any and all internal state data should only be in private variables inside the .c source code files. Whenever possible these variables should also be declared with keyword static, which will enlist the linker's help in hiding them.
DON'T expose the internal format of any module-specific data structure passed to or returned from one or more of the module's interface functions. That is to say there should be no "struct { … } foo;" code in any header file. If you do have a type you need to pass in and out of your module, so client modules can create instances of it, you can simply "typedef struct foo moduleb_type" in the header file. Client modules should never know, and this way cannot know, the internal format of the struct.
Inline functions
On a related note, I recently received this question from an engineer in Brazil:
"I am trying to conform to the rules in your Embedded C Coding Standard book and I just ran into what may be a problem with Rule 6.3.a. Instead of using function-like macros, I'm using inline functions, as you recommend. However, my compiler (avr-gcc) gives an error when I declare a function to be inline at both header and source file. If I put both the inline declaration and function body inside the header file it works fine. This fixes my compiler problem, but isn't it a bad practice to place code inside the header file?"
This is a good question, as it seems at first to be about a conflict between my bug-killing rules and something else I refer to as "Generally Accepted Programming Principles" (i.e., GAPP).
The inline keyword is a part of the C++ programming language that was added late to C (in C99). In C++, most programs are built out of classes–with GAPP dictating one header file per class definition. Any C++ function may be declared inline. However, if the inline function is a public member function (a.k.a., "public method") of the class it is necessary to place the code for the inline function in the header file. (This is so that all of the other modules that use the class can see the code they need to have placed inline by the compiler.)
Of course, placing the body of any function inside a header file conflicts with GAPP for the C programming language. Here's a set of rules to help you decide where to put the code for inline functions in C.
IF the inline function is a "helper" function that's only used inside one module, THEN put it in that .c file only and don't mention it in the header file. This is consistent with Rule 4.2.c of my Embedded C Coding Standard, which says that "The header file shall identify only the [functions] … about which it is strictly necessary for other modules to know."
IF, however, the inline function operates on the abstract data type defined in the header file and must be visible to two or more modules, THEN put the body of the inline function inside the header file. There is no rule in the Embedded C Coding Standard that strictly prohibits this, so there is no conflict after all.
Though not really specific to embedded software development, I hope this advice on good C programming practices is useful to you. If it is please let me know and I will provide more C advice like this in future columns.
Michael Barr is the author of three books and over sixty articles about embedded systems design, as well as a former editor-in-chief of this magazine. Michael is also a popular speaker at the Embedded Systems Conference, a former adjunct professor at the University of Maryland, and the president of Netrino. He has assisted in the design and implementation of products ranging from safety-critical medical devices to satellite TV receivers. You can reach him via e-mail at mbarr@netrino.com or read more of what he has to say at his blog (www.embeddedgurus.net/barr-code).
Navigate to related information



cdhmanning
5/11/2011 7:48 PM EDT
In general good advice, but a few holes IMHO.
"DO create one .h header file for each "module" of the system" No. Headers should define interfaces. Sometimes one module will have many interfaces. Sometimes one interface is shared by many modules.
Headers are also often a good way to define system configurations etc, which have no relationship to modules.
"DO include in the header file all of the function prototypes for the public interface of the module it describes" No. Split up headers according to logical interfaces. Often a module has multiple interfaces and needs multiple headers. Test interfaces should be separated form "normal usage" interfaces.
"That is to say there should be no "struct { … } foo;" code in any header file." Except of course when the structure is part of the interface. For example, If you have an interface to a GPS receiver or whatever that gives current position, then "current position" is probably a struct
Structures are also useful in interfaces for setting up function pointer tables and other useful abstractions.
Sign in to Reply
thinkski
5/12/2011 7:45 AM EDT
Good tip about omitting the details of data structures in header files. Made me realize that I've been doing that for no good reason.
Sign in to Reply
digitaljetset
5/12/2011 10:46 AM EDT
I think that it's much better to have one header file for each module, it is less confusing. Some people that want to do "object oriented" programming in C, likes to have multiple .c and .h files for each module, this is so confusing, hard to understand and hard to mantain.
Sign in to Reply
antiquus
5/12/2011 2:09 PM EDT
One situation that was hard to solve involved a single code base that covered over 30 products, using 3 CPU's (x86, AVR and Arm), at least 4 FPGA implementations, and many other hardware quirks including different peripheral bus implementations. Of course, the hardware guys simply mixed-n-matched like tinker toys, to give a zillion possible combinations for the new products.
For this I ended up with (1) a compile-time constant that defined the system, and (2) a special header file that then selected the appropriate sub-header-files according to the constant. The single header was included in most (but amazingly not all) modules. This is a cross between Frankenstein and a twisty maze of little passages.
This worked only because I used the Understand editor from www.scitools.com. This editor interprets the header files and allows direct jumping (sort of hyperlinking) to declarations and references.
Sign in to Reply
xlax
5/12/2011 3:09 PM EDT
I am confused by the statement "If you do have a type you need to pass in and out of your module, so client modules can create instances of it, you can simply "typedef struct foo moduleb_type" in the header file. Client modules should never know, and this way cannot know, the internal format of the struct."
If the compiler doesn't know the internals of the structure, how can it create an instance of it. I can see how the compiler could create a pointer to the moduleb_type. Is that what you mean by "creating an instance of it"?
Sign in to Reply
maryl
5/12/2011 4:13 PM EDT
I am confused by this as well. Why would client modules create instances to something they have no visibility to the contents? Why are they creating the instance if they don’t need to use it and hence need to know the internal structure? Is this a case of creating data that is then passed around to functions that do know the contents? Perhaps this is my inexperience (coming from the Pascal world) showing, but I find this very confusing.
Sign in to Reply
cdhmanning
5/12/2011 6:26 PM EDT
Consider something like a file handle. That holds a whole lot of context. The client code does not need to know (and should not know) what is in the handle and should only access the file through some well defined functions.
When the client code calls fopen() it gets back a pointer to a file structure that it must then use when making an fread() call or similar. It needs the pointer, which should be typed so the compiler can check for misuse, but it does not need to know what is inside.
Sign in to Reply
David Brown
5/13/2011 4:00 AM EDT
The file handle is an abstract type - it is not a struct, but a pointer to a struct. You can handle these without knowing the contents.
If all you have is an empty "struct" declaration, then you can only work with pointers to them, but not the type itself.
Sign in to Reply
maryl
5/13/2011 2:21 PM EDT
Thank you. That is an extremely helpful example.
Sign in to Reply
willc2010
5/12/2011 10:45 PM EDT
You're right. As written in the article, this doesn't work. In order to create instances in client modules the compiler needs to know the definition of the structure. Otherwise it doesn't even know how big it is. It would only work if the typedef declared a pointer instead, but that's not what it says.
This is just a basic limitation of the include file mechanism. What is needed is the concept of a 'private section' as in an Ada package, or some other properly designed module system, but I won't go there because not many people care about this stuff any more and it's like trying to sink rubber ducks.
Sign in to Reply
cdhmanning
5/13/2011 1:04 AM EDT
The C mechanism is perfectly fine for handling many kinds of "private stuff".
Have a look at this example http://pastebin.com/YH5N7ZCJ
There is no need to know how big the structure is and any reliance on that really breaks modularity.
All that should be made private in a case like this is that there is a struct of type x but you don't get to know what's inside it.
This is way, way better than using C++ private since there is no exposure of the internals at all.
As you say this only works via a pointer interface.
Sign in to Reply
willc2010
5/13/2011 4:08 AM EDT
Well if I just want to export an abstract data type and have users be able to declare variables or record fields or arrays of that type, why should I have to dynamically allocate structures and go through pointers? An Ada compiler (for example) needs to know how big the type is, but its definition (and hence also its size) doesn't have to be formally visible to other modules, so it is still private and there's no reliance on it whatsoever. But with include files you simply can't express the concept. That's because there is no connection in C between a module's interface (the include file or files) and its implementation. It is a mere convention.
If you want to export a type but hide the details in C, as in the example you cite, you have to use a cumbersome and expensive roundabout mechanism, and then you have to deal with the fact that the lifetime of the variable that the user actually wants (represented by the pointer) is now disconnected from the lifetime of the structure, which means that it requires explicit cleanup (which can go wrong, e.g. memory leaks) or a garbage collector - in either case, an expensive solution to a problem that wouldn't exist in the first place if the language wasn't so weak.
Sign in to Reply
MoeMoeTheMoe
5/12/2011 3:30 PM EDT
What is the general feeling about header files including other header files. Personally, I don't do it but I have worked with others that think that it is quite OK.
Sign in to Reply
cdhmanning
5/12/2011 6:29 PM EDT
I think this is fine. Often one interface depends on another. It is better to sort this out in the header files (and get it right) than expect the client code to set up the wrong.
Sign in to Reply
David Brown
5/13/2011 4:04 AM EDT
If one header file needs another header file, it should include it. Generally you should try to keep modules independent when that is possible. But if one header needs a type defined in another header, for example, then it should include it. Never rely on the application code having the extra headers or headers in a particular order.
Sign in to Reply
David Brown
5/13/2011 4:12 AM EDT
I agree with most of this article, but I think it goes a bit far with the "hide everything" aim. It is a worthy idea, but the unfortunate reality is that C (and C++) will not let you hide data and details without a cost.
We are talking about embedded programming here - wasted code space costs money, and wasted run time costs power.
And while layers of abstraction and data hiding help code quality and testing in some ways, they detract in other ways - they can make debugging much harder, and it can be difficult to follow the flow of information.
It is common to slavishly follow the ideas that "all global data is bad", and that implementation details must be hidden at all times. The reality is that global data is sometimes the clearest and most efficient way to pass data around. And implementation details end up in header files if you want your code to be efficient. The simple answer here is to use comments or sectioning of the header file to make usage clear.
Sign in to Reply
cdhmanning
5/13/2011 6:27 PM EDT
I absolutely agree with you that it is far to easy to fall into slavishly following rules. Often "purity" has to be sacrificed for some reason. Engineering is the art of compromise.
The use of abstract interfaces (and pointers) does not necessitate the use of malloc().
For example consider an abstract interface to code for one or more serial ports. The port structures can be static in the implementing code.
The actual private port data does not need to be exposed and can be identified via a pointer or even a port number.
As for code overheads... well this will very much depend on the architecture. ARM, for instance, often handles pointers to structured data better than direct access to globals.
Sign in to Reply
Jeffrey Nonken
6/7/2011 7:10 PM EDT
When I can't afford the overhead of accessor functions, I'll make the variables public and use accessor macros instead (defined in the relevant headers, of course). A gentlemen's agreement is then in place that external code use the macros and not access the variables directly.
Of course it's easy enough to break that agreement, but then there's nothing to stop future coders from making any or all variables public and accessing them directly anyway. The macros give an alternative method that allows a modicum of control but does not add run-time overhead.
Sign in to Reply
parkgate
5/13/2011 4:22 AM EDT
"DON'T expose the internal format of any module-specific data structure passed to or returned from one or more of the module's interface"
I believe that rule only works if your coding standards don’t prohibit the use of malloc() or you think malloc() is a bad idea.
I would like to suggest an alternative rule:
DO run a static analysis followed by a compile of all the code if any change is made to a header file.
Terry
Sign in to Reply
JMUNN
5/13/2011 1:42 PM EDT
Please give us the same article for C++ header files with C++ details!
Sign in to Reply
Luis Sanchez
5/13/2011 5:25 PM EDT
As a newcomer I think this article is of great advice. I imagine a system described in block diagrams and that each block will have a header file. However, a point made by one of the readers is good too. Header files which relate to the different interfaces seem reasonable. I think we can have one single header file as long as the different interfaces or modules are well identified inside it.
Sign in to Reply
cdhmanning
5/13/2011 6:17 PM EDT
Luis. If you do choose to have a single header file, then partition it according to interfaces and logical groupings. In the end you will probably want to break the big header into smaller headers so you might as well start off at least part way down the right path.
Sign in to Reply
willc2010
5/16/2011 5:01 AM EDT
Another thing that probably should be mentioned: try to avoid using the same name to define data types in different header files, because there is no cross-check that they are used consistently or kept separate. For example, if one module gets a definition of a structure called S from one header file (directly or indirectly), and another module gets a definition of S from another file, and they do not happen to be structurally identical (e.g. one has an int where the other has a char), then if a function in one module passes an S or a pointer to an S to a function in the other module, you are likely to get a machine-specific and unpredictable bug that will manifest itself at some time in the future.
There is nothing in C itself to prevent this from happening. It's an example of the inadequacy of a module system that works simply by pasting source files together. In a small project you can generally avoid it with some care. It's more likely to occur in a larger project with many contributors.
Sign in to Reply
MCDK
7/7/2011 11:28 PM EDT
In large projects, they just don't declare 's' as you assume, the s is prefixed with the module name. So the probability of your predition coming true is 0 unless the coding guidelines are not in place or the project is not handled by experieced.
Sign in to Reply
parkgate
5/16/2011 8:19 AM EDT
“The use of abstract interfaces (and pointers) does not necessitate the use of malloc(). For example consider an abstract interface to code for one or more serial ports. The port structures can be static in the implementing code. The actual private port data does not need to be exposed and can be identified via a pointer or even a port number.”
What you say is true but if you accept that hiding the internal workings of a module is a good thing then using the port number will be better than using the pointer.
If you accept the use of malloc() is a bad thing and you are not going to use it either through choose or because your coding standards disallowed it then I don’t believe you can use the last rule.
Terry
Sign in to Reply
cdhmanning
5/17/2011 9:50 PM EDT
Yes, using a port number is good in that it lets you do simpler validation for valid arguments and lets you capture things like trying to access freed objects.
The benefit of using a pointer is that this does not force the implementation to work in any particular way (as numbers would). ie. If you use a number then you have to also have a way to access the items by number. That's trivial for an array, but not easy for other data types.
You can expose private data via a pointer without using malloc with something like:
http://pastebin.com/R6rGHmUA
And yes, I do agree that in general extensive use of malloc is a bad thing in embedded systems.
Sign in to Reply
parkgate
5/16/2011 8:33 AM EDT
“Another thing that probably should be mentioned: try to avoid using the same name to define data types in different header files, because there is no cross-check that they are used consistently or kept separate. “
This should be picked up by your static checker. If you are not using one try splint which is free. I used it to get the following:
Run the command:
splint *.c
This gives the following output:
Splint 3.0.1.6 --- 11 Feb 2002
tmp2.h(9,3): Structure DUPLICATED_S redeclared with fields { int a; int b; },
previously declared with fields { char a; char b; }
A struct, union or enum type is redefined with inconsistent fields or
members. (Use -matchfields to inhibit warning)
tmp1.h(9,3): Previous declaration of DUPLICATED_S
tmp2.h(6,9): Field a redeclared as char, previously declared as int
tmp1.h(6,11): Previous declaration of a
Finished checking --- 1 code warning
For the following source code:
/////////////////////////////////////
// file tmp1.c
#include "tmp1.h"
void func1 (DUPLICATED_S *pStruct)
{
pStruct = pStruct;
}
/////////////////////////////////////
// file tmp1.h
typedef struct {
char a;
char b;
} DUPLICATED_S;
/////////////////////////////////////
// file tmp2.c
#include "tmp2.h"
void func2 (DUPLICATED_S *pStruct)
{
pStruct = pStruct;
}
/////////////////////////////////////
// file tmp2.h
typedef struct {
int a;
int b;
} DUPLICATED_S;
Sign in to Reply
parkgate
5/19/2011 6:59 AM EDT
“You can expose private data via a pointer without using malloc with something like:
http://pastebin.com/R6rGHmUA”
If you run this example code through splint you will get a warning about possible NULL pointers and possible memory leaks. What I think has happened is that you have started to write your own version of malloc () and I’m quite impressed that splint has spotted this fact.
Consider the following code fragment. It uses a set of functions from a module called timer0.c to measure time and it breaks the last rule.
////////////////////////////////////////////////////////////////////////////
#include “timer0.h”
void delay (unsigned int time_in_ms)
{
TIMER0_CTRL timer;
timer0_set (&timer, time_in_ms);
do {
SetWatchDog ();
} while (timer0_expired (&timer) == false);
}
////////////////////////////////////////////////////////////////////////////
The compiler allocates the memory for the timer variable and the calling function doesn’t need to worry about NULL pointers or memory leaks.
It is true that the calling function could mess with the contents of the timer structure and it would be nice if C allowed the structure to be protected but all that is needed is a bit of discipline on the part of the programmer.
I believe this is a simple and elegant solution and any attempt to apply the last rule would make it more complicated and messy.
Terry
Sign in to Reply
cdhmanning
5/19/2011 11:55 PM EDT
I was leaving the null pointer handling as an exercise to the reader. It was not a fully implemented solution.
The problem with the code you show is that it has limited application and requires that the specific of TIMER0_CTRL are exposed (otherwise it cannot be allocated by the caller). This breaks modularity.
While that code is simpler, and OK for a trivial example, it does not work well in general.
Sign in to Reply
parkgate
5/20/2011 5:53 AM EDT
“The problem with the code you show is that it has limited application and requires that the specific of TIMER0_CTRL are exposed (otherwise it cannot be allocated by the caller). This breaks modularity.”
I have to disagree with your comment that “it breaks modularity”.
The timer0.c module is used as part of an embedded project with several state machines that need to keep track of time. Each state machine module has its own private timer control structure which is passed to the timer0.c module. The timer0.c module has no knowledge of the total number of timers nor does it care.
New private timers can be added to the project using something like:
static TIMER0_CTRL my_new_timer;
Allocation of memory is done by the complier and timers can be added without affect timer0.c.
Your solution would require you to check you don’t run out of timers every time a timer is added and you might need to update timer0.c to add timers when you do run out. This breaks the modularity and reusability of timer0.c.
If you look at the “stdio.h” you will see that the FILE structure is exposed. I’m not aware of this causing any problems even though there must be billions of lines of code that use this structure.
So the last rule seems to be trying to fix a problem that doesn’t exist while possible introducing real problems.
My main objection to the last rule is that if I applied it to timer0.c I would end up with a more complicated and messy solution and that would break the most important golden rule of Engineering “Thou shall strive to produce simple and elegant designs”.
Terry
Sign in to Reply
Matt S.
5/20/2011 1:01 PM EDT
STSARCES (Standards for Safety Related Complex Electronic Systems) Annex 7 and UL 1998 are great references to embedded coding standards and prohibit the use of heap management functions (e.g. malloc(), calloc(), free()) and instead require pre-allocated RAM regions.
My biggest peeve with "C" is when header files require others to be loaded first; it should not matter what order the header files are listed in the top of a "C" source file.
Great article.
Sign in to Reply
cdhmanning
5/22/2011 4:58 PM EDT
Includes should always include any headers that they need.
When client code includes a header all dependencies are then resolved.
Sign in to Reply
DAH2136
6/2/2011 1:01 PM EDT
"DON'T expose any variable in a header file, as is too often done by way of the extern keyword."
Except that you often need a global instance of something
(eg an instance of a struct with the system's state, timer-counter, ISR flags)
Sign in to Reply
parkgate
6/3/2011 3:46 AM EDT
"Except that you often need a global instance of something (eg an instance of a struct with the system's state, timer-counter, ISR flags)"
It is better to do this via function calls. e.g. create a file called timer.c which access the system's timer registers and create a timer.h file with a list of the timer functions.
If more than one module can access the same resource directly you are creating a risk of conflicts and making a rod for your own back.
Terry
Sign in to Reply
cdhmanning
6/21/2011 12:12 AM EDT
You can generally use function calls, but if you really, really can't then use C macros that look like function calls and abstract away the global data.
At least then the code is written properly and does not have direct ties to globals.
This is also important to aid code portability. One day you'll want to port that PIC (or whatever) code to an ARM (or whatever).
Sign in to Reply
FillG
7/26/2011 10:03 PM EDT
Okay Michael, now what about documentation guidelines for include files. I vote zero. Documentation in .h files is a legacy concept based upon the dissemination of the interface to other parties. I prefer all documentation in the .c file. Keep it where you need it. You don't need it in .h files, you can always write a script to extract documentation from the .c file. Engineers picking up someone else's code, don't want to open two files and have to hop back and forth.
Sign in to Reply
Weatherhead
12/20/2011 9:12 PM EST
I totally disagree with the last rule "DON'T expose the internal format of any module-specific data structure passed to or returned from one or more of the module's interface functions." This is just not a reasonable thing to do in C while maintaining the ability to statically allocate structures outside of the module and I agree with others that it amounts to a solution looking for a problem. If you REALLY WANT TO HIDE the internal data structure don't even pass the struct pointers, rather use integer handles that have to be resolved to pointers internal to the functional module. That will prevent code outside the module from being able to even copy the data without using "accessor" functions in the module.
Sign in to Reply