Souce code in H file?

Status
Not open for further replies.

Noggin

Member
So I've inherited a project from another company which has one .c file and a few dozen .h files. All of the .h files are included in the .c file. All of the .h files contain significant amounts of code in them. The .c file has only an int main function.

I can't think of any defensible reason for writing about 10,000 lines of code in header files. Is there any reason to do that? The closest I can come to doing that is a #define macro... but I don't technically consider that "code" as it doesn't actually take up any flash or RAM as it sits in the header file. The last time I had code in a .h file was when I was in college and didn't know any better and even then it was only variable definitions.

If it matters, it was originally written with the CCS compiler.
 
They probably didn't know that they could have just included .c files instead of including .h files.

Normally the pre-processor won't care it just replaces #include blah by expanding blah and putting it inline in the code at that point. The files could probably be called .x and got the same result.
 
Maybe you're right.... I inherited another project that originated from the same programmer. It was about 50,000 lines of code and all in ONE c file. I wouldn't do a #include "cfile.c" either, but maybe that's easier than trying to do things in some cmd line compilers.
 
Last edited:
I wouldn't do a #include "cfile.c" either

Why not? It's a good way to tidy up functions that are complete and won't be changed further, so they are not in the way of the code that needs to be worked on. And it can be a good way to modularise functions that can be used in many projects, things like LCD routines. Just put them in "LCD.c" and add #include "LCD.c" into your source code.

I do things in PIC C (MikroC) that I would never do in a Windows C program, and do things in my Windows C programs that I would never do in "proper" C software that needs to be used by other people on a team etc. Ultimately you're the master of your own code. You can do it any way you like, but preferably a way that improves something.
 
I would make an LCD.h file with all of the functions and global variables declared in it and include the .h file instead. I do modularize my code as much as I can as long as it makes sense, but I can't bring myself to include a C file.
 
I was taught the primary function of the .h file is to provide function prototypes that allows the compiler to constructs call to code it has not or will not see in this compilation. I may toss a few #defines in a .h file but I have a growing distaste for them.

Rather then compile the entire project we used to modularize it. We compile the module (.c) we were working on and linked it to the other object modules. The .h's for the objects were included in our .c to allow it to construct the needed calls. The linker made it all work.

If this is the intended model it makes no sense to put c that generates code in the .h files.

I also feel there should be a 1:1 mapping between .c files and .h files. This allows the programmer to make a change to the .c file and it's .h file. Then all programs using them get the changes the next time they are compiled and linked to the new .h and its obj file.

MPLAB supports this. Objects place the the project will be linked with the .c files in the sources list.
 
Noggin-
but I can't bring myself to include a C file.

I don't think it any more butcherous than a lot of things people do to C code for microcontrollers. Things like using gotos, global variables, fixing variables in specific RAM locations, inline assembler, etc etc.

Cood C practice (like 3V0 rightly recommends) definitely has its place but when working with a tiny micro that has very specific limits on code size/speed and ram then sometimes you need to do what is best for that app, not what is best for C coding practice in general.

I never use .h files with PICs as most of my projects are a single .C file, with 10 functions or less. Many have only 3 or 4 functions so adding a .H file seems ludicrous.

But occasionally I'll hand off a few functions in an #included .C file, or add a .C file of library functions. To me anyway a tiny project consisting of two .C files is better than one that needs two .C and two .H files with all the extra file opening, maintenance etc for basically zero benefit.
 

Lets say you have a LCD driver you frequently use. Create a lcd.c and lcd.h file with its "standard" port definitions. Store these in C:/mylib and you can use with any program.

create files
C:/mylib/src/lcd.c
C:/mylib/h/lcd.h

compile and store the .o in
C:/mylib/obj/lcd.o

---
If you are happy with the standard port definitions you can simply add the C:/mylib/obj/lcd.o to your project, and include C:/mylib/h/lcd.h in your main.c.

---
If you need different port definitions you have more then one choice and it is where many people give up on the idea.

The easiest is to copy both lcd.c and lcd.h to your project directory and modify the port defs in .h. With both lcd.h and lcd.c files in the project directory lcd.c's #include "lcd.h" will include the altered lcd.h from the project file so there is no need to edit the .c file. (in unix I might use a soft link) Not too hard but we have the extra work of copying lcd.c to our project file

We can attempt to leave the lcd.c file in its original mylib/src location but it raises the question of how to get it to include the lcd.h in the project file without altering lcd.h in the mylib. We should/might be able to do this by setting up the search path correctly for the project. Maybe this would work by placing lcd.h in the project header list. It is 2AM...

The upshot is with enough of you own libs you can put create a c program so fast it will the the arduino crowd blush.

EDIT: I am not suggesting we all run out and do this. It is a personal choice and there if you want to use it.

---
Linker scripts/files are another related subject.
 
Last edited:
The problem with putting C code in header files is if you need to #include those functions in more than one C file you'll hit duplicate symbols. So rather than putting your code into lcd.h, put it into lcd.c instead and the function prototypes in lcd.h. Then you can #include lcd.h in as many C files as you want and you won't get any duplicate symbol errors.

The point of having multiple C files is to save on compile time, and to make file editing a bit easier. The compiler then only compiles the C files that have changed (assuming you use make rather than a batch file to recompile everything), resulting in lower compile time.

If you only have one C file then you don't need an H file. Just bung all the prototypes at the top of the file, or order the functions in such a way as to not need prototypes at all (main at the end, for example).
 
Noggin-

I don't think it any more butcherous than a lot of things people do to C code for microcontrollers. Things like using gotos, global variables, fixing variables in specific RAM locations, inline assembler, etc etc.

I don't think I even know how to use a goto My college instructor showed us one and then refused to teach us how to use it. Strictly speaking that isn't the best teaching practice, but I don't mind.

I am guilty of using globals, but only when there is a good benefit to them being global. For instance, if I have a few temperature sensors on a board, I typically have a temperature.c/h file and an adc.c/h file. I'll have the temperature variables defined globally in the temperature.h file so that the adc functions can read the ADC pin, calculate the temperature, and store it in the global variable. Inside the adc.c files, i'll have low pass filters defined as a local global so that I don't have to pass it between the adc service routine and the temperature calculation routines.

I also commonly have data structures that contain all of my current information for my project such as status_data, parameter_data, history_data, etc. Status_data would have my current temperature settings, what my inputs and outputs are doing, where control knobs are set, etc. In my Service_Inputs() function, I can set status_data.digital_inputs to what the debounced value currently is. This way, when I get around to Service_Comms(), if I have a tell-me-your-status-data command, all I have to do is set up a packet header and memcpy status_data to a buffer following the packet header. This saves me from having to call a dozen Get_Some_Data functions. I'm not arguing that this is the best way to do it, I'm just saying what works for me (like we're all doing right now) and keeps my code clean.

Now, in my second post in this thread I mentioned that I inherited a 50,000 line program all in one c file. The first several hundred lines was every variable definition... including i, j, and k. I hate that project.

As for fixing variables in specific RAM locations, I'm guilty of that if the project has a bootloader. I'll make a persistent watchdog_counter in a specific location in my bootloader and application so they can both keep track of whether or not there is a potentially major issue. It would probably be better to modify the linker script and make a section that is persistent and keep them both in that section.

But like you said, sometimes your MCU is just too small to do what you want to do so you do what you have to do. I have the luxury of working on fairly large code size projects so I get to play in the 512k PIC32 world more often than I play in the 2k PIC12 world
 
Last edited:
3v0- It's nice and professional building obj files and linking them. It has costs in PIC terms though, with the example of LCD.c I can just edit the LCD.c file for each project, cut out unneeded functions to save size, setup for different port pins and different LCD types etc. A lot of versatility would be lost going to an obj file for that, and if modifiying it requires extra work to modify the c and h files and recompile the obj when I could have just edited LCD.c. There are things you do driving a motorcycle that you don't do (and shoudln't do) when driving a truck.

My college instructor showed us one and then refused to teach us how to use it.
I can understand why they do that, but if he was teaching C for PIC (and microcontrollers) he should have been sacked...

I like C language but it's weakest feature is poor flow control. Especially on PICs where speed and ROM usage are critical.

50,000 lines of code and first few hundred lines are variables? I can see why you hate it.
 

These arguments do not hold water. I expect you skimmed my post and did not fully understand what I was saying. Lets try again.

First you can use lib files two ways. You can use them as compiled by listing myLib/obj .o files as objects in your project. You can also list the lib sources from myLib/src dir as a sources in your project. You are not stuck with the lib as it was compiled.

The libs are developed with a uC in mind, and possibly by the user, In many cases the lib will be exactly what the user wants. If needed functionality can be included or excluded from the lib buy using header file #define and #ifdef in the lib source to make changes during compilation or by choosing to link or not link objects as needed. Some programming models use one object per function. If your LCD driver has 8 functions you can have up to 8 objects buy you get to choose which ones to include or exclude in the project.

Changing pin and other definitions can be rather painless. As I pointed on in my previous post one can use myLib/source/lcd.c as a project source and place a modified copy of lcd.h in the project directory. You get the right pin definitions and the library code remains unchanged.

If you have several closely related LCD types you can use a #define in a local copy of the lcd.h file to cause the lcd.c, in myLib, to produce the driver you want without any modification to the .c. You could use #define's in the .h and #ifdef's in the .c to cause the generation or omission of functions as well.

One can use a combination of linking the obj files you need and recompiling, say maybe the read and write routines that are port specific as determined by header files.

----
There is also an organizational benefit for some people. When you want to reuse, that code, you can go right to your myLib and find it instead of hunting through projects to see where you used the latest greatest version.

In summary if done correctly the only down side is that it takes a bit more time to create the code for the lib. If you get it right (and it is not that hard) after that it is all gravy.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…