2

I would like to do this with a macro:

typedef struct _TIMER_llist {
    struct _TIMER_llist *next;
    uint32_t time;
    const int id;
} TIMER_llist;

TIMER_llist _timer_llists[] =
{
    { .id = 1, .next = &_timer_llists[1] },
    { .id = 2, .next = &_timer_llists[2] },
    { .id = 3, .next = &_timer_llists[3] },
    { .id = 4, .next = &_timer_llists[4] },
    { .id = 5, .next = &_timer_llists[5] },
    { .id = 6, .next = &_timer_llists[6] },
    { .id = 7, .next = &_timer_llists[7] },
    { .id = 8, .next = &_timer_llists[8] },
    { .id = 9, .next = &_timer_llists[9] },
    { .id = 10, .next = &_timer_llists[10] },
    { .id = 11, .next = &_timer_llists[11] },
    { .id = 12, .next = &_timer_llists[12] },
    { .id = 13, .next = 0 } };

//This won't work, because const .id
TIMER_llist _timer_llists[1];
void init() {
    _timer_llists[0].id = 1;
    _timer_llists[0].next = 0;
}

Instead of writing a line for each buffer entry I would like to use a

#define NUMBER_ENTRIES 13  

as this gets really unhandy, if I want to do 64 entries or so...

Unfortunatly I don't have any experience with the C preprocessor, so I'm curious if:

  1. there is a better non macro solution
  2. there is an easy way with gcc to accomplish this macro operation

Besides, and that is more a theoretical aspect, is execution speed. AfaIk the example in the first post would be just a memcpy(.data[?], _timer_llists, sizeof(_timer_llists)), while a programmatical approach would require at least a counter, maybe an if statement for the last case, and a function overhead (ok, maybe inline). On the other hand this would save space in .data.

In this particular case I am using gcc-avr, but this question came to my mind repeatedly and I would like to have an universal approach.

5
  • No, there is no easy way. C preprocessor is not supporting looping or recursion. There are some X-macro tricks, but they are not universal. Commented Nov 1, 2017 at 14:48
  • You could initialize the array programmatically with 3-4 lines of code. Commented Nov 1, 2017 at 14:53
  • Just out of curiosity, why not use a function? Commented Nov 1, 2017 at 15:03
  • You can do it with boost.preproceasor (yes you can use boost.preprocessor with C). Commented Nov 1, 2017 at 15:05
  • One complicating factor is the null pointer for the last entry; it is different from the rest. Any scheme will need to account for that. Commented Nov 1, 2017 at 18:22

3 Answers 3

3

If you use boost preprocessor (see n.m.'s comment), you can simply do this:

#include <boost/preprocessor/enum.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>

#define TIMER_ENTRY(_, n, data) \
   { .id = BOOST_PP_INC(n), .next = &data[BOOST_PP_INC(n)] }
#define DECL_TIMER_LLIST(NAME_, COUNT_) \
   TIMER_llist NAME_[] = \
   { \
      BOOST_PP_ENUM(BOOST_PP_DEC(COUNT_), TIMER_ENTRY, NAME_) \
      , { .id = COUNT_, .next = 0 } \
   }

BOOST_PP_ENUM is almost everything you need.

BOOST_PP_INC and BOOST_PP_DEC supplement the special start case (start count at 1 instead of default 0)/end case (count up to n-1 then add a different line).

Here's a "live" sample.

(FYI, unless you're programming on hardware you found in a time capsule, I wouldn't even take the overhead of programmatic alternatives into consideration when comparing approaches).

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

Comments

1

If you really need to do a initialisation rather than doing the initialisation programmatically the best you can do with macros is something like this:

#define TIMERINIT(a)      {.id = a, .next = &_timer_llists[a] },
#define END_TIMERINIT(a)  {.id = a, .next = 0 },

TIMER_llist _timer_llists[] =
{
  TIMERINIT(1)
  TIMERINIT(2)
  TIMERINIT(3)
  TIMERINIT(4)
  ...
  TIMERINIT(12)
  END_TIMERINIT(13)
};

It's not perfect but still better than writing everything by hand.


Now the programmatic solution (that's the easiest and most flexible solution IMO):

#define NUMBER_ENTRIES 13
TIMER_llist _timer_llists[NUMBER_ENTRIES];

int main()
{
  for (int i = 0; i < NUMBER_ENTRIES; i++)
  {
    _timer_llists[i].id = i + 1;
    _timer_llists[i].next = &_timer_llists[i + 1];
  }
  ...
}

For NUMBER_ENTRIES larger than approximately 8, the generated code will most likely be shorter with this solution.


Another approach would be to write a special program that generates the initialisation table by writing it into a .h file and include that .h file into your .c file. Your build process should then call that special program prior the comnpilation.

Comments

1

You can use "X-macros". It is not necessarily an improvement over hard-coding everything, but it would look like this:

#define TIMER_INIT_LIST \
  X(1)   \
  X(2)   \
  X(3)   \
  X(4)   \
  X(5)   \
  X(6)   \
  X(7)   \
  X(8)   \
  X(9)   \
  X(10)  \
  X(11)  \
  X(12)  \
  X(13)  \

int main()
{
  TIMER_llist _timer_llists[] =
  {
    #define X(n) [n-1] = {.id = n, .next = &_timer_llists[n] },
      TIMER_INIT_LIST
    #undef X
  };

  return 0;
} 

At the same time, I improved the code by using array index designated initializers, to ensure data integrity even if the X macro list is modified or unsorted.

The macros expand to something like this:

[0] = {.id = 1, .next = .next = &_timer_llists[1] },
[1] = {.id = 2, .next = .next = &_timer_llists[2] },
...

Comments

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.