Distinguish variable and constant in #define macro - c

I have macros like this:
#define _DDR_0 DDRD
#define _DDR_1 DDRD
#define _DDR_2 DDRD
// ....
#define _PN_0 0
#define _PN_1 1
#define _PN_2 2
// ...
/** Configure pin as output */
#define as_output(pin) sbi(_DDR_##pin, _PN_##pin)
void as_output_n(const uint8_t pin);
It is used like so:
as_output(2);
uint8_t b = 1;
as_output_n(b);
The _n variant is used for variables, the macro for constants (because it's much faster to do this with a macro).
Is there any possible way to make it so you can use as_output always, and depending on it being constant OR a variable, use the macro OR the function?

Macros are expanded by the preprocessor which has no knowledge of what the code does. From the preprocessor's point of view all the macro arguments are simply tokens (with no logical meaning). In this case as_output macro is used to manufacture new code (via token pasting).
So, what you're trying to do, is not possible using just macros (there could be a way if b would be a macro itself, but i guess that's not what you're looking for).
You could use a const array for your _DDR* and PN* values, and an inline function instead of macros.

Related

C macro expansion including compiler switches

My current implementation defines a couple of variables depending on corresponding compiler switches:
#ifdef ENABLE_var1
int var1;
#endif
#ifdef ENABLE_var2
int var2;
#endif
The compiler switches will be set by -D option during make. The names will always consist of the same prefix and the variable name. Since it is always the same strategy, my idea is to replace this using a macro like:
DECLARE(var1)
DECLARE(var2)
Calling make -DENABLE_var1 should result in:
int var1;
Calling make -DENABLE_var1 -DENABLE_var2 should result in:
int var1;
int var2;
Since it is not possible to use #ifdef within a macro, is there a trick to achieve this?
As long as the variable names to be defined, potentially, are known, then this can be accomplished:
// Define a DEFINE_x macro for each x that might be enabled.
#if defined ENABLE_var1
#define DEFINE_var1 int var1;
#else
#define DEFINE_var1
#endif
#if defined ENABLE_var2
#define DEFINE_var2 int var2;
#else
#define DEFINE_var2
#endif
// Define DECLARE(x) to expand to the corresponding DEFINE_x macro.
#define DECLARE(x) DEFINE_##x
// List potential definitions.
DECLARE(var1)
DECLARE(var2)
If the names are not known, then this kludge works:
#define Comma() ,
#define Argument3c(a, b, c,...) c
#define Argument3b(a, b,...) Argument3c(a, b, __VA_ARGS__)
#define Argument3a(a,...) Argument3b(a, __VA_ARGS__)
#define Nullify1
#define NullifyHelper(x) Nullify##x
#define Nullify(x) NullifyHelper(x)
#define DECLARE(x) Argument3a(Comma Nullify(ENABLE_##x) (), int x;,,)
DECLARE(var1)
DECLARE(var2)
Understanding this requires following the preprocessing in great detail, but I will provide a few notes:
For -Dname, GCC defines name to be replaced by 1. The Nullify macro, with its helpers, causes ENABLE_x to be replaced by an empty sequence if ENABLE_x is defined to be 1 and by a non-empty sequence otherwise.
Then, if an empty sequence has resulted, we have Comma (), which expands to a comma. If it is not an empty sequence, we have Comma something (), which does not allow the function-like macro to be expanded, so some sequence not including a comma results.
Through the remaining macro expansions, this comma or lack thereof determines which argument is where in the argument list, allowing us to pick out either the desired definition or an empty sequence.
I advise against using this in production code. There is likely a better way to accomplish your configuration goal.

How to force macro to not expand

Using the following code:
#include <stdio.h>
typedef struct
{
int APB1ENR;
int b;
int c;
} RCC_TypeDef;
typedef struct
{
int a;
int b;
int c;
} USART_TypeDef;
#define USART2_BASE 0x1000
#define USART2 ((USART_TypeDef *) USART2_BASE)
#define RCC_BASE 0x2000
#define RCC_APB1ENR_USART2EN_Pos (17U)
#define RCC_APB1ENR_USART2EN_Msk (0x1UL << RCC_APB1ENR_USART2EN_Pos)
#define RCC_APB1ENR_USART2EN RCC_APB1ENR_USART2EN_Msk
#define RCC ((RCC_Typedef *) RCC_BASE)
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define __HAL_RCC_USART2_CLK_ENABLE() SET_BIT(RCC->APB1ENR, (RCC_APB1ENR_USART2EN))
#define UART_PERIPH USART2
#define CONCATENATE(x) // What comes here??
int main()
{
CONCATENATE(UART_PERIPH);
// | should expand to __HAL_RCC_USART2_CLK_ENABLE();
}
How can we define CONCATENATE(x) macro to expand only one layer deep. Using two levels of indirection it would expand all the way to pointer to structure, what I want is to expand UART_PERIPH only one layer and paste it together to form an already existing macro from its argument.
Is this possible?
How can we define CONCATENATE(x) macro to expand only one layer deep. ... Is this possible?
No. Here's what you have available. When macro invocation occurs, the first step is argument substitution (a.s.; 6.10.3.1); during that step, the tokens in the argument are evaluated if their corresponding parameter is mentioned in the macro's replacement list with said mention not being involved in a stringification or paste. The resulting expansion is substituted for said parameters in the replacement list. Next, stringification/pastes are applied in no particular order. Finally, rescan and further replacement (r.a.f.r; 6.10.3.4p1) occurs, during which the resulting replacement list itself is scanned; during this scan, the macro's name is "painted blue" (6.10.3.4p2; "blue paint" is not mentioned by name but is technical jargon for this), meaning if it's encountered it will not expand further.
So let's look at it from this point of view. UART_PERIPH is an identifier. Either it will be recognized as a macro in some context (i.e., will trigger macro invocation), or it will not. It doesn't matter if the context is during an a.s. or a r.a.f.r.; if this is invoked, the invocation involves r.a.f.r. (no a.s. because it's object-like). So the invocation involves taking USART2 and rescanning it. The only possible way for this to not expand USART2 is for this identifier to not be recognized as a macro, but since it's currently defined as one, the only way for that to happen is for this identifier to be painted blue. That's not possible (at least in the intended context) because USART2 would have to be expanding for this to happen, and by that time you're already injecting tokens you don't want.

What is the difference between global variables and #define in c

Whats the reason why there are 2 opportunities :
global variables
symbolic constants with #define ?
So I know what #define does but I don't know what the difference in use is.
Which situation do I must have thus I'm able to decide myself upon the right opportunitie? What is one of the opportunities able to do what the other one doesn't ? I hope that I could clarify what I mean.
Well, global variables can be edited from everywhere.
Basically, in the low level, a variable is stored in RAM memory and created after launching your application, it always has an address in RAM. Defines are just macros, your compiler will just replace your define names with its values in the compilation step.
#define can't be edited, it's just a macros. And #define is not just about values, you can define almost everything that you want, for example:
// Defining a constant
#define PI 3.14
// Defining an expression
#define MIN(x,y) ((x)<(y))?(x):(y)
// Defining a piece of code
#define STOP_TIMER \
clock_gettime(CLOCK_REALTIME, &__t1); \
__lasttime = \
(double) (__t1.tv_sec - __t0.tv_sec) + \
(double) (__t1.tv_nsec - __t0.tv_nsec) / 1000000000.0;
And, in most situations, defines are used to set some OS-specific or hardware-specific values. It's a really powerful thing, because it gives you the opportunity to change things dynamically in the compilation step. For example:
// Example with OS
#ifdef __linux__
#define WELCOME_STRING "welcome to Linux!"
#else
#define WELCOME_STRING "welcome to Windows!"
#endif
// Example with hardware
#if __x86_64__ || __ppc64__
#define USING_64BIT
#else
#define USING_NOT64BIT
#endif
Consider this small example
#define num 5
int number = 5;
num is a macro and number is a global variable.
One important difference is that num is not stored in the memory, num is just the substitute for 5, but number uses memory.
Also, macro's are preprocessor directives, their values cannot be changed like variables.
So, no doing
num = 6;
later in the code. You will have to use #undef to undefine it and define it again to change the value.
Global variables can be accessed and edited from everywhere. #define constants can't be edited, just read.
Examples:
We use #define ERROR 666 to define a programmer pre compile time constant for an error for the whole program.
We use a global variable for a count of how many operations a function did and this value can be read by other functions as well.
There's no point to make the error as a global variable since it shouldn't be edited and you can't use the #define x as a counter.
#define is declared on top of the code, it means before the declaration of the class. And it serves as to define (as the name says) a constant, that can be read but not changed.
A global variable can be accessed globally on the code, and at same time changed.

How do I do different things per macro value?

#define TYPE char *
if TYPE is char *
do A
if TYPE is int
do B
Is there an example how to do such things?
C preprocessor MACROS manipulate text, so are essentially typeless, so NO you can't do that.
You could associate another symbol with it:
#define TYPE char *
#define TYPE_IS_CHAR_STAR
#ifdef TYPE_IS_CHAR_STAR
...
#endif
You just need to keep them consistent manually.
Note that that's a dangerous macro; you should use a typedef instead. With the macro:
TYPE x, y;
x is a pointer, but y isn't.
You can get a similar effect by defining another macro along with the type, and using #ifdef etc. with that other macro. For example:
#define TYPE char *
#define TYPE_IS_PCHAR 1
...then later...
#ifdef TYPE_IS_PCHAR
do A
#endif
#ifdef TYPE_IS_INT
do B
#endif
It's not quite the same thing, but it still gets you there.
Not easily. You could do something like:
#define TYPE_IS_CHARPTR
//#define TYPE_IS_INT
#ifdef TYPE_IS_CHARPTR
do A
#endif
#ifdef TYPE_IS_INT
do B
#endif
But you really should be trying to minimise your use of the preprocessor for tricky things (anything other than simple variables).
With enumerated constants and inline functions, there's little need for such uses nowadays.
It would work if you just used basic types (since they're just strings - see Mitch's answer). But as soon as you try to use pointers, it won't work any more - the asterisk throws the preprocessor for a loop:
[holt#Michaela ~]$ gcc test.c
test.c:3:10: error: operator '*' has no right operand
But if you want do do different things based on different types, I'm going to have to recommend switching to C++ and using templates and template specialization. Reluctantly, since template syntax is incredibly ugly, but you should be able to do whatever you want.
Hope that helps!

How to define a define in C?

Is it possible to write a #define that defines a #define?
For example:
#define FID_STRS(x) #x
#define FID_STRE(x) FID_STRS(x)
#define FID_DECL(n, v) static int FIDN_##n = v;static const char *FIDS_##n = FID_STRE(v)
But instead:
#define FID_DECL2(n, v) #define FIDN_##n v \
FIDS_##n FID_STRE(v)
FID_DECL works fine but creates two static variables. Is it possible to make FID_DECL2 work and having define two defines?
No; preprocessing is performed in a single pass. If you want or need more advanced behavior, consider using another tool to preprocess the source, like m4.
Further, the # in the replacement list (at the beginning of #define FIDN... would be parsed as the # (stringize) operator: the operand of this operator must be a named macro parameter, which define is not.
No while defining macros u should take care of one thing
that macro should not call itself (reccursively) either directly
or indirectly.
I know two static variables consuming 8 bytes will be expansive for u.
I have solution over it
#define FID_STRS2(x) #x
#define FID_STRE(x) FID_STRS2(x)
#define FID_DECL(n, v) static int FIDN_##n = v;static const char *FIDS_##n = FID_STRE(v)
Just rename them going reccursive

Resources