0

I want to create some kind of smart name definition using a preprocessor in my library project. So I have for example a timer named TC0 and some additional names for interrupts handler like TC0_Handler and TC0_IRQn. It will be nice to be able to define only a timer name (it can also be TC1, TC2 and so on) and use it like that:

#define MY_TIMER          TC0
#define MY_TIMER_HANDLER  MY_TIMER ## _Handler
#define MY_TIMER_IRQ      MY_TIMER ## _IRQn

But the problem is that the timer ic actually not a simple name but a macro defined in a platform-specific header:

#define TC0               ((Tc       *)0x42003000UL)

So it's processed by a preprocessor and doesn't allow to use it how I want to do.

It it any solution for this problem?

3
  • 1
    You have two possibilities: 1. Use a name that does not match an existing definition. 2. undefine TC0 (#undef TC0) before you use it. Commented Nov 7, 2024 at 10:15
  • 1
    Standard code review remark: "handler" is a horrible name to be avoided. It suggests "here I handle stuff and don't you worry about what the function does". What does it even mean? An ISR? Some internal code in the driver handing the timer? An external API function exposed to the caller? Some manner of callback? Name functions after what it does - what does it actually "handle"? Commented Nov 7, 2024 at 12:23
  • 1
    @OldBoy Or option 3 use the code as-is, because ## is always applied before any macro expansion inside that macro. Commented Nov 7, 2024 at 12:26

3 Answers 3

3

## does not expand its operands to their actual value. Try compiling the code below with -E option:

#define VAR ((int*)0xffff)
#define VAR_HANDLER VAR ## _handler

int VAR_HANDLER = VAR; // just to see the result

int
main (void)
{
  ;
}

The output is:

int VAR_handler = ((int*)0xffff);

int
main (void)
{
  ;
}

With that said, I don't think that doing this is a good idea. I'd recommend sticking to the most simple way of doing this — without macros.

Sign up to request clarification or add additional context in comments.

Comments

2

To expand on the correct answer by @Moses, when the ## operator is present in a so-called "macro replacement list", there's two scenarios given some a ## b:

  • Either a and b are function-like macro parameters, in which case they are expanded before token concatenation.
  • Or a and b is something else, in which case the pre-processor applies the ## operator to the sequences of pre-processing tokens 'a' and 'b' respectively, not considering if those form valid pp-tokens that may be expanded.

For this reason, you can use the code somewhat as-is. However, MY_TIMER becomes problematic because once the preprocessor starts expanding macros, it goes all the way and expands TC0 too. We can solve that with a work around, moving the _ from the _Handler etc into the MY_TIMER macro:

#define MY_TIMER          TC0 ## _

And then we need some helper macros to expand MY_TIMER before token concatenation, we can't do MY_TIMER ## _Handler or we'd just end up with MY_TIMER_Handler with no connection to TC0:

#define CONCAT(a,b) a ## b
#define EXPAND_CONCAT(a,b) CONCAT(a,b)

#define MY_TIMER          TC0 ## _
#define MY_TIMER_HANDLER  EXPAND_CONCAT(MY_TIMER, Handler)

Full example:

#include <stdio.h>

typedef struct
{
  int tc0_stuff [123];
} Tc;
#define TC0 ((Tc       *)0x42003000UL)

#define CONCAT(a,b) a ## b
#define EXPAND_CONCAT(a,b) CONCAT(a,b)

#define MY_TIMER          TC0 ## _
#define MY_TIMER_HANDLER  EXPAND_CONCAT(MY_TIMER, Handler)

void TC0_Handler (void)
{
  printf("handling mysterious stuff");
  TC0->tc0_stuff[0] = 456;
}

int main (void)
{
  MY_TIMER_HANDLER();
}

3 Comments

But in this case MY_TIMER will not equals to TC0. As we don't know how OP want to use MY_TIMER symbol, we can't answer definitely.
This is a good solution, but I can't really use it, because I have to use TC0 when timer functions from the library are called. So I will have 2 definitions instead of 1 definition as I want to have.
@AlexKiselev The best solution is probably to forget all of this entirely and stuff everything into a struct or similar.
0

If you don't need the previous value, you may undefined it (and at least emit some warning):

#ifdef TC0
#undef TC0
#warning "TC0 already defined, beware."
#endif

#define MY_TIMER          TC0
#define MY_TIMER_HANDLER  MY_TIMER ## _Handler
#define MY_TIMER_IRQ      MY_TIMER ## _IRQn

If you need both, it is not possible; you then have to find non conflicting symbol names.

As we known nothing about the use cases, it not possible to help you more.

2 Comments

"If you need both, it is not possible" Of course it is possible, that's how the ## operator works.
Of course I need it for calling the timer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.