What is the logic of "#ifdef" lines in the embedded if condition? - c

In the code below I have an push button example of efr32fg14 evalution board.
They use the #ifdef command which checks if the BSP_GPIO_LED1_PORT variable is defined.
BSP_GPIO_LED1_PORT is the port number neccesary for the command.
What if the logic of using #ifdef in the code below?
#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "bsp.h"
// Push-buttons are active-low
#define PB_PRESSED (1)
/**************************************************************************//**
* #brief GPIO initialization
*****************************************************************************/
void initGPIO(void)
{
// Enable GPIO clock
CMU_ClockEnable(cmuClock_GPIO, true);
// Configure PB0 and PB1 as input
GPIO_PinModeSet(BSP_GPIO_PB0_PORT, BSP_GPIO_PB0_PIN, gpioModeInput, 0);
GPIO_PinModeSet(BSP_GPIO_PB1_PORT, BSP_GPIO_PB1_PIN, gpioModeInput, 0);
// Configure LED0 and LED1 as output
GPIO_PinModeSet(BSP_GPIO_LED0_PORT, BSP_GPIO_LED0_PIN, gpioModePushPull, 0);
#ifdef BSP_GPIO_LED1_PORT
GPIO_PinModeSet(BSP_GPIO_LED1_PORT, BSP_GPIO_LED1_PIN, gpioModePushPull, 0);
#endif
}
/**************************************************************************//**
* #brief Main function
*****************************************************************************/
int main(void)
{
// Chip errata
CHIP_Init();
// Initializations
initGPIO();
while (1)
{
// Set the state of LED0
if (GPIO_PinInGet(BSP_GPIO_PB0_PORT, BSP_GPIO_PB0_PIN) == PB_PRESSED)
{
// LED0 On
GPIO_PinOutSet(BSP_GPIO_LED0_PORT, BSP_GPIO_LED0_PIN);
}
else
{
// LED0 Off
GPIO_PinOutClear(BSP_GPIO_LED0_PORT, BSP_GPIO_LED0_PIN);
}
#ifdef BSP_GPIO_LED1_PORT
// Set the state of LED1
if (GPIO_PinInGet(BSP_GPIO_PB1_PORT, BSP_GPIO_PB1_PIN) == PB_PRESSED)
{
// LED1 On
GPIO_PinOutSet(BSP_GPIO_LED1_PORT, BSP_GPIO_LED1_PIN);
}
else
{
// LED1 Off
GPIO_PinOutClear(BSP_GPIO_LED1_PORT, BSP_GPIO_LED1_PIN);
}
#endif
}
}
I have tried to look for the #ifdef definition to see the logic of this command in the code.

#ifdef is a pre-processor directive (rather then a "command"), and is used for conditional compilation.
If the condition (in this case defined BSP_GPIO_LED1_PORT) is false, the code between the condition and the corresponding #endif is removed from the compilation so that no code is generated for that section of source.
In this case the "logic" appears to be to conditionally build code that supports either one or two LEDs. That is code related to LED0 is always included, and that for LED1 is excluded unless BSP_GPIO_LED1_PORT.
So to support LED1, you might either have a header that has (for example):
#define BSP_GPIO_LED1_PORT gpioPortA
or you might use a command line swithc such as -D BSP_GPIO_LED1_PORT=gpioPortA in GCC, CLANG and many other compilers. Note that defined/!defined are Boolean states, the macro need not itself have a value, but here it is actually the port identifier for the GPIO.
Note that there is a corresponding #ifndef (if-not-defined), and both can have a #else section and zero or more #elif (else-if) sections.
#ifdef/#ifndef are however rather inflexible. In C89 the more useful defined operator was added so that you can use #if and more complex expressions such as:
#if defined BSP_GPIO_LED1_PORT && defined BSP_GPIO_LED1_PIN
#if expressions allow Boolean expressions to control conditional compilation, rather then just defined/!defined. #ifdef has been largely redundant since C89, but it seems to not be going away.

Related

Conditional execution not working for this case of code?

Why the code inside the #ifdef INITIALISATION and #endif is not executed?
int main(void)
{
uint8_t DLEVEL = 10;
#if DLEVEL > 5
#define INITIALISATION
#endif
while (1)
{
#ifdef INITIALISATION
Display(); // This line is never being executing :: please help for a solution
#endif
}
}
edited...
Thanks for the reply
Actually the below mentioned logic of the code is used as a part of my code memory optimization.
Now the code memory size is overflowed, so i need to execute the one time initializations when the SYM_DLEVEL value is 10 and then the value of SYM_DLEVEL is changed from 10 to 2, then the initial sections i need to be automatically commented or disabled and then only the Display_2() function needed to be enable and need to execute.
Is it possible?
#define SYM_DLEVEL 10
int main(void)
{
#if SYM_DLEVEL > 5
Display_1(); // need to execute this line once(before the value changes from 10 t0 2)
#endif
#define SYM_DLEVEL 2 // after this line execution i need to automatically disable the above section and automatically enable the below section
#if SYM_DLEVEL < 5
#define INITIALISATION // need to execute only when value changes from 10 to 2
#endif
while (1)
{
#ifdef INITIALISATION
Display_2();
#endif
}
}
It doesn't execute code because the preprocessor runs in the compiler, while the local variable DLEVEL is assigned during execution.
The preprocessor simply looks for a preprocessor symbol DLEVEL that evidently is undefined.
Undefined symbols are equated to 0, so the symbol INITIALISATION isn't defined.
To make it work define a preprocessor symbol, i.e. SYM_DLEVEL, to be used in the conditional preprocessing, and eventually assign it to the runtime variable. I.e.
#define SYM_DLEVEL 10
int main(void)
{
uint8_t DLEVEL = SYM_DLEVEL;
#if SYM_DLEVEL > 5
#define INITIALISATION
#endif
while (1)
{
#ifdef INITIALISATION
Display(); // This line is now is executed
#endif
}
}
Remember that preprocessing is more or less a text processor (which actuates on base piece of text, tokens, as outlined in comments below), and is executed before the compilation of the code. It doesn't know about runtime variables and their assignments, it only knows symbols defined with a preprocessor directive (#define).
Don't confuse actions as dead code removal or other optimization made by compiler with what the preprocessor can do. The golden rule is: "preprocessing only understand preprocessing objects, C code is a plain text without any special meaning for it".

multiple definition error in Clion

I added "Mersenne Twist Pseudorandom Number Generator Package" (one header file and one source file) to my Clion C project, but I can not build the project.
the error is :
multiple definition of `mts_lrand'
I can successfully compile and run the program using just command line and gcc but in Clion i can not.
I think it has something to do with macros, but I do not know how to fix it.
here is some part of the code i added:
mtwist.h
.
.
.
.
/*
* In gcc, inline functions must be declared extern or they'll produce
* assembly code (and thus linking errors). We have to work around
* that difficulty with the MT_EXTERN define.
*/
#ifndef MT_EXTERN
#ifdef __cplusplus
#define MT_EXTERN /* C++ doesn't need static */
#else /* __cplusplus */
#define MT_EXTERN extern /* C (at least gcc) needs extern */
#endif /* __cplusplus */
#endif /* MT_EXTERN */
/*
* Make it possible for mtwist.c to disable the inline keyword. We
* use our own keyword so that we don't interfere with inlining in
* C/C++ header files, above.
*/
#ifndef MT_INLINE
#define MT_INLINE inline /* Compiler has inlining */
#endif /* MT_INLINE */
/*
* Try to guess whether the compiler is one (like gcc) that requires
* inline code to be available in the header file, or a smarter one
* that gets inlines directly from object files. But if we've been
* given the information, trust it.
*/
#ifndef MT_GENERATE_CODE_IN_HEADER
#ifdef __GNUC__
#define MT_GENERATE_CODE_IN_HEADER 1
#endif /* __GNUC__ */
#if defined(__INTEL_COMPILER) || defined(_MSC_VER)
#define MT_GENERATE_CODE_IN_HEADER 0
#endif /* __INTEL_COMPILER || _MSC_VER */
#endif /* MT_GENERATE_CODE_IN_HEADER */
#if MT_GENERATE_CODE_IN_HEADER
/*
* Generate a random number in the range 0 to 2^32-1, inclusive, working
* from a given state vector.
*
* The generator is optimized for speed. The primary optimization is that
* the pseudorandom numbers are generated in batches of MT_STATE_SIZE. This
* saves the cost of a modulus operation in the critical path.
*/
MT_EXTERN MT_INLINE uint32_t mts_lrand(
register mt_state* state) /* State for the PRNG */
{
register uint32_t random_value; /* Pseudorandom value generated */
if (state->stateptr <= 0)
mts_refresh(state);
random_value = state->statevec[--state->stateptr];
MT_PRE_TEMPER(random_value);
return MT_FINAL_TEMPER(random_value);
}
.
.
.
.
you see the function definition is in header file, i think this is the problem but I don't know what to do
you can see complete code here.

Doxygen in C preprocessor if

I document my C-Code with Doxygen and got a problem. Consider following example:
in defines.h:
#ifndef DEFINES_H_
#define DEFINES_H_
#define ENABLED 1
#endif
in test.c
#include <defines.h>
/**
* #brief This is the first testfunction
* #return void
*/
void testFunc1(void)
{
//...do stuff
}
#if ENABLED
/**
* #brief This is the second testfunction
* #return void
*/
void testFunc2(void)
{
//...do stuff
}
#endif
Doxygen finds testFunc1 and documents it well, but it cannot find testFunc2. To define ENABLED in the .doxyfile wont fix my problem.
Is there a way to get this running? (Note, I need to define ENABLED inside my c-Project!)
[misread the source snippet]
You probably want #ifdef instead of #if.
From the docs for #if:
The ‘#if’ directive allows you to test the value of an arithmetic expression, rather than the mere existence of one macro.
Its syntax is
#if expression
controlled text
#endif /* expression */
expression is a C expression of integer type, subject to stringent restrictions.
and for #ifdef:
The simplest sort of conditional is
#ifdef MACRO
controlled text
#endif /* MACRO */
This block is called a conditional group. controlled text will be included in the output of the preprocessor if and only if MACRO is defined. We say that the conditional succeeds if MACRO is defined, fails if it is not.

Check for concatenated define in C preprocessor

I access registers by concatenated defines unsing the function GETREG
#define NUMBER 1 //changes
#define REG1 register.N1
#define REG2 register.N2
#define REG8 register.N8
#define GETREG_(N) REG ## N
#define GETREG(N) GETREG_(N)
Sometimes the registers for that NUMBER are not defined so i want to make sure the macro correctly expands before inserting it in the code so i tried to do:
#define NUMBER 5
#ifdef GETREG(NUMBER)
GETREG(NUMBER) = 0
#endif
However this always seems to evaluate as true and the compiler prints
Warning:extra tokens at end of #ifdef directive
Background Story:
In my projects I create libraries to interface with a HAL to abstract the hardware level. Often from one project to another the codebasis in the HAL stays exactly the same but just the location of pins changes. For that reason i would like to use macro-expansion to access the pins. The following macro does the job for me at adressing the analog features of the pin:
#define ANSEL_(Pin) _ANS ## Pin
#define ANSEL(...) ANSEL_(__VA_ARGS__)
that way i can turn on or off analog features by:
#define PIN_RX A0
ANSEL(PIN_RX)= 0;
and other registers by similar macros. The problem i am facing now is that some pins for example do not have analog features (e.g. Pin A5). Because of that i would like to test if the define in the library esists. I was tryin to do this by:
#ifdef ANSEL(PIN_RX)
ANSEL(PIN_RX)= 0;
#endif
However this simple approach is not working.
The microcontroller (PIC33) lib:
Edit: Shipped from the manufactorer
#define ANSELA ANSELA
extern volatile unsigned int ANSELA __attribute__((__sfr__));
typedef struct tagANSELABITS {
unsigned ANSA0:1;
unsigned ANSA1:1;
unsigned ANSA2:1;
unsigned ANSA3:1;
unsigned ANSA4:1;
unsigned :4;
unsigned ANSA9:1;
} ANSELABITS;
extern volatile ANSELABITS ANSELAbits __attribute__((__sfr__));
/* ANSELA */
#define _ANSA0 ANSELAbits.ANSA0
#define _ANSA1 ANSELAbits.ANSA1
#define _ANSA2 ANSELAbits.ANSA2
#define _ANSA3 ANSELAbits.ANSA3
#define _ANSA4 ANSELAbits.ANSA4
#define _ANSA9 ANSELAbits.ANSA9
You have the wrong expectation for the behavior of the #ifdef directive. That directive is equivalent to #if defined, where defined is a preprocessing operator in that context. Although the condition of an overall #if directive is evaluated only after the line is fully macro-expanded, the defined operator operates on the next preprocessing token, which must have the form of an identifier, before macro expansion:
Prior to evaluation, macro invocations in the list of preprocessing tokens that will become the controlling constant expression are replaced (except for those macro names modified by the defined unary operator) [...]
[C2011, 6.10.1/4]
Thus, your
#ifdef GETREG(NUMBER)
tests whether GETREG is a defined macro, and the (NUMBER) constitutes a sequence of unexpected extra tokens.
The defined operator therefore cannot serve your purpose. It is conceivable that there is a way to use a more general #if directive to achieve your aim, but I'm not seeing it at the moment. Instead, I'm inclined to suggest letting the compiler find such errors for you, instead of expecting the preprocessor to do it. If compile time is too late, then perhaps you need to look to a build configuration system such as the Autotools or CMake could help you create.
You can do more or less what you want if you are prepared to use two #defines instead of one for each pin:
#define HAS_ANS_A0 1
#define ANS_A0 ANSELAbits.ANSA0
#define HAS_ANS_A1 1
#define ANS_A1 ANSELAbits.ANSA1
#define HAS_ANS_A5 1
#define ANS_A5 ANSELAbits.ANSA5
#define HAS_ANSEL_(Pin) HAS_ANS_ ## Pin
#define HAS_ANSEL(...) HAS_ANSEL_(__VA_ARGS__)
#define ANSEL_(Pin) ANS_ ## Pin
#define ANSEL(...) ANSEL_(__VA_ARGS__)
#if HAS_ANSEL(PIN_RX)
ANSEL(PIN_RX)= 0;
#endif
This works because in an #if, an undefined identifier token (that is, an identifier token which has not been #defined) has the value 0. It is not an error, or even a warning.
Note: I changed the symbols starting _ANS to conform with §7.1.3 (Reserved Names), paragraph 1:
All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

Any workaround to self-referential macros in C? I'd like to append to a macro

I have a file that uses a FLAGS macro from an include that I do not control. What is in FLAGS is not consistent. Occasionally I need to change FLAGS to add a flag to it. Now I know I can't do #define FLAGS FLAGS|MY_FLAG, but I thought if I stored FLAGS in a temporary variable that I could then undefine it and redefine it using the temporary and my flag. For example:
// Assume this next line is what's in the include file
#define FLAGS (1|2|4)
// The rest of this is source, assume compile with -DMOD
#ifdef MOD
#define TEMP (FLAGS|8)
#undef FLAGS
#define FLAGS TEMP
#endif
int main()
{
printf("0x%x\n", FLAGS);
}
And if MOD is defined the error is error: 'FLAGS' was not declared in this scope. I know that I can change all the actual C code that uses FLAGS to instead use FLAGS|MY_FLAG but I was hoping to modify the macro rather than all the code.
Your only real way to do exactly what you are trying to do is to define an additional macro
// Assume this next line is what's in the include file
#define FLAGS_FOR_A (1|2|4)
#define FLAGS FLAGS_FOR_A
// The rest of this is source, assume compile with -DMOD
#ifdef MOD
#undef FLAGS
#define FLAGS ( FLAGS_FOR_A | 8 )
#endif
int main()
{
printf("0x%x\n", FLAGS);
}
Macros just do simple text replacement, computed before runtime
You can do something logically equivalent to #define FLAGS FLAGS|MY_FLAG if you define the macro as modifiable using Boost's "evaluated slots":
#include <boost/preprocessor/slot/slot.hpp>
// define FLAGS as a modifiable macro and create a setter for it
#define FLAGS BOOST_PP_SLOT(1)
#define UPDATE_FLAGS BOOST_PP_ASSIGN_SLOT(1)
int main(void) {
// set the initial value of FLAGS
#define BOOST_PP_VALUE (1|2|4)
#include UPDATE_FLAGS
printf("0x%x\n", FLAGS); // 0x7
// update FLAGS with a new value using the old one
#define BOOST_PP_VALUE (FLAGS|8)
#include UPDATE_FLAGS
printf("0x%x\n", FLAGS); // 0xf
}
Despite being witchcraft, this is completely standard-compliant C, no extensions. Only works for integers.
(It works by taking advantage of something important: macros aren't just expanded into program code, but also need to be expanded to determine whether to follow an #if branch as well. Since #if directives are also capable of evaluating integer math, this is able to expand the actual numeric value and use it to construct a new expansion for the PP_SLOT that doesn't involve a reference to any macro name. This is all hidden behind the #include UPDATE_FLAGS directives.)

Resources