Solved

#if defined in C Source File ???

Posted on 2013-02-06
12
443 Views
Last Modified: 2013-02-11
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
0
Comment
Question by:stevid
  • 5
  • 4
  • 2
  • +1
12 Comments
 
LVL 35

Expert Comment

by:mccarl
ID: 38859133
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) { //..... }
0
 

Author Comment

by:stevid
ID: 38859560
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,
0
 
LVL 32

Expert Comment

by:sarabande
ID: 38860703
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
0
 
LVL 35

Expert Comment

by:mccarl
ID: 38862070
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!
0
 
LVL 22

Expert Comment

by:ambience
ID: 38863040
>> 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.
0
 

Author Comment

by:stevid
ID: 38863160
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?
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 32

Expert Comment

by:sarabande
ID: 38865387
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
0
 
LVL 35

Accepted Solution

by:
mccarl earned 500 total points
ID: 38866351
Ok, since now we have a bit more information... this is how I would do it.

main.c
...
#include "GSM.h"
...
...
Boot_GSM_Modem();
...
Echo_Off();
...

Open in new window

GSM.c
...
#define GSM_UART1                    // You can have this here or define it in build scripts/command line as 'ambience' posted above
...
#if defined GSM_UART1
#define GSM_UART_BASE         0x0a10           // Whatever the UART1 base address might be
#elif defined GSM_UART2
#define GSM_UART_BASE         0x0a20           // Whatever the UART2 base address might be
#else
#error GSM_UART1 or GSM_UART2 must be defined
#endif
...
#define GSM_UART_TX_PORT      GSM_UART_BASE + 0x02    // Whatever the offset for a TX port is
#define GSM_UART_RX_PORT      GSM_UART_BASE + 0x04    // etc
#define GSM_UART_CONTROL      GSM_UART_BASE + 0x08    // and so on for whatever ports you need
...
...
void Boot_GSM_Modem() {
    // use the GSM_UART_CONTROL, GSM_UART_TX_PORT, etc defines to access your hardware
}

Open in new window

GSM.h   (note, there is nothing too special about this header file, I just posted it for completeness)
#ifndef __GSM_H
#define __GSM_H

void Boot_GSM_Modem();
void Echo_Off();
...
#endif  // __GSM_H

Open in new window

The above gives you...
Ability to choose which port to communicate on, either by modifying GSM.c or using command line/build scripts (I know the original aim was to select this from your main.c file, but in this embedded case, I highly doubt that the alternative presented here be an issue for you).
No run time overhead, in either passing parameters to the GSM functions (pushing parameters on a stack, etc) or in code to decide/calculate the correct addresses to access. It's all done at compile time.
No duplication of code, leading to the errors/issues that I outlined above

Hope we are getting closer to what you need
0
 

Author Comment

by:stevid
ID: 38867300
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??
0
 
LVL 35

Expert Comment

by:mccarl
ID: 38867391
Will it halt the compiler and display the error string??
Exactly!!

I'm glad that you have arrived at a solution that suits! :)
0
 

Author Comment

by:stevid
ID: 38867482
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
0
 
LVL 35

Expert Comment

by:mccarl
ID: 38870210
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?
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use while-loops in the C programming language.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now