Link to home
Start Free TrialLog in
Avatar of stevid
stevid

asked on

#if defined in C Source File ???

Hi,



Having an issue using #if defined (...) in a C source file but on studying into the problem further a lot of people are suggesting that using #ifdef in C Source files is not a great idea and it would be better to create multiple source files, Ill explain what i am tryign to do and maybe someone could help me,



I have a load of code written that communicates with a GSM Module and now i want to break up the code into libraries to make it more portable, In this instance i am using UART1 for Comms but in the future i may want to use UART2 for a different system



Plan was i have my header file with prototypes such as BootModem(), EchoOff() etc etc



Then in my C Source file i wanted to have something like


#if defined (GSM_UART1)
void Boot_GSM_Modem (void)
{
 }

#elif defined (GSM_UART2)
void Boot_GSM_Modem (void)
{
}


#endif

Open in new window




Plan was in the main C file then i would at the start put something like

#define GSM_UART1
#include <stdlib.h>
#include <p18f27j13.h>
#include "GSM_GL865.h"

Open in new window




Now when i try to compile the program it tells me it can not find the function, if i remove the #ifdef it compiles fine



I would be interested to know whats wrong with this but maybe i should create two seperate header files and two seperate source files and call them GSM_UART1.h and GSM_UART2.h



Anyone any thoughts,



Thanks,



Stephen
Avatar of mccarl
mccarl
Flag of Australia image

Basically, the reason for it not finding your function, is that your main C source file and your GSM C source file are compiled in two totally separate steps. Hence, when compiling your GSM C source file, neither GSM_UART1 or GSM_UART2 are defined, no matter what is in your main C source file, and hence neither function gets compiled into the GSM object file. And then when the object files are linked, there is no Boot_GSM_Modem function for the main object file to link to.

Where you may be confused, is if your were to see in other examples, that #defines in one C source file being referred to in other "HEADER" files. This is the big difference. If you look at your main C source file, you first #define GSM_UART1 and then later #include the GSM header file. Therefore that header file (and only the header file, not the GSM source file) gets included in your main C file and hence can refer to GSM_UART1 if it wanted to.

The above is a bit of a convoluted concept to understand, so please ask if it's still not clear.



As for solving your requirement, not that I fully know what the actual code in those different flavours of Boot_GSM_Modem would be, but I am guessing that it would be mostly the same except for the UART that is opened? In that case, I would just have the one function which would take a parameter that indicates what UART to open, ie. void Boot_GSM_Modem(int uartNumber) { //..... }
Avatar of stevid
stevid

ASKER

Hi,

Yes makes sense, my problem is that i have lots of functions such as BootGSM, EchOff, SendString etc etc,

All will be the exact same function except some will reference and use registers associated with UART1 or UART2,

As i have lots of functions i would prefer not to have have to pass in the UART2 every time or UART1, I have seen what i have done done in other source files so not sure how they get it to work,

Any ideas how to get it to work like this?

Would i be better to create multiple header files and multiple sources??

Thanks,
you could use a header file which would map the function names to specialized functions and have a declaration for all specialized functions.

// gsm.h
#ifndef GSM_H

#if defined (GSM_UART1)
#define Boot_GSM_Modem Boot_GSM_Modem_UART1
#elif defined (GSM_UART2)
#define Boot_GSM_Modem Boot_GSM_Modem_UART2
#endif

void  Boot_GSM_Modem_UART1(void);
void  Boot_GSM_Modem_UART2(void);

#endif /* GSM_H */

Open in new window


in your main c source you do

// main.c
#define GSM_UART2
#include "gsm.h"

....
int main(...
{
      ....
      Boot_GSM_Modem();   // would call Boot_GSM_Modem_UART2

Open in new window


the implementation of the functions probably should be done in different c files.

using that approach the compiler would compile the appropriate function call depending on the macro defined before including the header file.  

if your sources are c++ rather than c you better would have a common baseclass gsm and derived classes gsm_uart1, gsm_uart2. then a source could use the one or the other for example by using a template argument. or you could use either the one or the other depending on information you get at runtime. generally a c++ solution would be much more mighty and flexible, while using preprocessor macros is a much more static solution which much likely requires a lot of redundant code.

Sara
Yes, what you saw was probably similar to what Sara describes above. I was thinking about describing this in my first post but decided not to as, from what I can see, it would be a *bad* idea in your case.


This is the way I'm looking at what you are wanting... You want to DUPLICATE EVERY function (except for the small difference of which port it accesses) in your GSM C file, ie. double the amount of code, double the amount of effort if you ever have to change any code in the GSM C file, open up the possibilities that if you do have to change code in the GSM C file that you make the change to one version and not the other, etc, etc.... JUST so that you can have code like this...
#define GSM_UART1
...
...
Boot_GSM_Modem();
Echo_Off();
Send_String();

Open in new window

... rather than sometihng like this...
#define UART    1
...
...
Boot_GSM_Modem(UART);
Echo_Off(UART);
Send_String(UART);

Open in new window

You can still change which UART is used by altering just one line (the #define at the top). Note this is how most C code that you will see tackles these situations, ie. for all the file handling functions, read(), write(), close(), you pass them a "file handle" so that they know what to actually operate on. When you do Windows programming, you might pass a "window handle" to each function that might operate on a window so they know what to apply to. It is a very common methodology!
>> Would i be better to create multiple header files and multiple sources??

You can add the GSM_UART1 from your build scripts, like make files or if using an IDE then set it as a project wide pre-processor symbol. This way all the source files in the same project would have the same symbol and functions would get included accordingly. From build scripts its typically the -D GSM_UART1 command line parameter but you can check your compiler manual.

Having said that, if you have the luxury to create multiple headers and sources then thats even better.
Avatar of stevid

ASKER

You can still change which UART is used by altering just one line (the #define at the top). Note this is how most C code that you will see tackles these situations, ie. for all the file handling functions, read(), write(), close(), you pass them a "file handle" so that they know what to actually operate on. When you do Windows programming, you might pass a "window handle" to each function that might operate on a window so they know what to apply to. It is a very common methodology

This does make sense and obviously i get the point about writing all of the functions twice, Only problem here is i then have to test in every function to see is it Uart 1 or Uart2 which is wasted clock cycles in an already tight application.

(In case it was not clear this is all being ran on a microcontroller so not endless resources)

Can anyone see an issue with creating a header file say called GSM setting and in this defining which UART1 or UART2. Then include this file in the C Source file and then just put UART in the source file which from the header file will be replaced with either UART1 or UART2?
the minimal code is to use inline code instead of a function call. you could use macros which take the original function name and the UART parameter and expand to the full code of the function.

Sara
ASKER CERTIFIED SOLUTION
Avatar of mccarl
mccarl
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of stevid

ASKER

Ye between this and some research i think i am happy with a solution now, One last quick question i have never used the
#error
directive above, What will it do, Will it halt the compiler and display the error string??
Will it halt the compiler and display the error string??
Exactly!!

I'm glad that you have arrived at a solution that suits! :)
Avatar of stevid

ASKER

One very last question slightly off topic but this is it,

When i want to include other header files in a header file say stdio.h, I want to put an include guard to make sure its not added in multiple times,

Should i have something like this

 
# ifndef stdio_h
#define stdio_h
include stdio.h
#endif

Open in new window


or

 
# ifndef stdio_h
#define stdio_h
#endif

Open in new window


Basically i normally see the second method but it never seems to actually include the file, Ill start a new Question if its thought best??

Thanks
I'm really not sure what those two snippets are supposed to be... is that in the file that is including stdio.h or in stdio.h itself? Anyway, for completeness this is how it is done...

main.c
#include "GSM.h"
#include "IO.h"

...
// Use the functions/defines/etc from both GSM and IO

Open in new window

GSM.h
#ifndef __GSM_H
#define __GSM_H

#include "hardware.h"

...
// Defines, prototypes for GSM functions, etc, that can use details from hardware.h
...
#endif //__GSM_H

Open in new window

IO.h
#ifndef __IO_H
#define __IO_H

#include "hardware.h"

...
// Defines, prototypes for IO functions, etc, that can also use details from hardware.h
...
#endif //__IO_H

Open in new window

hardware.h
#ifndef __HARDWARE_H
#define __HARDWARE_H

...
// Common definitions of things used in multiple other headers/sources
...
#endif //__HARDWARE_H

Open in new window

As you can see from the above, it is the actual "included" file itself that guards itself from being included more than once. It's not the responsibility of the "including" file to guard against this. Again, this is so that it only needs to be done once... in the file that gets included rather than potentially multiple times everywhere that the file may get included from! Does that make sense?