【问题标题】:C - Creating an initialisation macro for an array of structsC - 为结构数组创建初始化宏
【发布时间】:2017-11-01 14:42:55
【问题描述】:

我想用一个宏来做这个:

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;
}

我不想为每个缓冲区条目写一行,而是使用一个

#define NUMBER_ENTRIES 13  

因为这真的很不方便,如果我想做 64 个左右的条目...

不幸的是,我对 C 预处理器没有任何经验,所以我很好奇:

  1. 有更好的非宏解决方案
  2. gcc 有一个简单的方法来完成这个宏操作

此外,更多的是理论上的方面,是执行速度。 AfaIk 第一篇文章中的示例只是一个 memcpy(.data[?], _timer_llists, sizeof(_timer_llists)),而编程方法至少需要一个计数器,可能是最后一种情况的 if 语句,以及一个函数开销(好的,也许是内联的)。另一方面,这将节省 .data 中的空间。

在这种特殊情况下,我使用的是 gcc-avr,但这个问题反复出现在我的脑海中,我希望有一个通用的方法。

【问题讨论】:

  • 不,没有简单的方法。 C 预处理器不支持循环或递归。有一些 X 宏技巧,但它们并不通用。
  • 您可以使用 3-4 行代码以编程方式初始化数组。
  • 只是出于好奇,为什么不用函数呢?
  • 您可以使用 boost.preproceasor 来实现(是的,您可以将 boost.preprocessor 与 C 一起使用)。
  • 一个复杂的因素是最后一个条目的空指针;它与其他的不同。任何方案都需要考虑到这一点。

标签: c c-preprocessor array-initialization


【解决方案1】:

如果您使用 boost 预处理器(请参阅 n.m. 的评论),您可以简单地这样做:

#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 几乎是您需要的一切。

BOOST_PP_INC 和 BOOST_PP_DEC 补充了特殊的开始情况(从1 开始计数而不是默认的0)/结束情况(计数到n-1 然后添加不同的行)。

Here's a "live" sample.

(仅供参考,除非您在时间胶囊中找到的硬件上进行编程,否则在比较方法时我什至不会考虑编程替代方案的开销)。

【讨论】:

    【解决方案2】:

    如果您确实需要进行初始化而不是以编程方式进行初始化,那么您可以使用宏做的最好的事情是这样的:

    #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)
    };
    

    它并不完美,但仍然比手写所有内容要好。


    现在是程序化解决方案(这是 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];
      }
      ...
    }
    

    对于大于大约 8 的NUMBER_ENTRIES,使用此解决方案生成的代码很可能会更短。


    另一种方法是编写一个特殊的程序,通过将初始化表写入 .h 文件并将该 .h 文件包含到您的 .c 文件中来生成初始化表。然后,您的构建过程应在编译之前调用该特殊程序。

    【讨论】:

      【解决方案3】:

      您可以使用“X 宏”。它不一定是对所有内容的硬编码的改进,但它看起来像这样:

      #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;
      } 
      

      同时,我改进了代码,使用了数组索引指定的初始化器,以确保即使 X 宏列表被修改或未排序,数据的完整性。

      宏扩展成这样:

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

      【讨论】:

        猜你喜欢
        • 2011-05-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-26
        • 1970-01-01
        • 2019-07-17
        • 1970-01-01
        相关资源
        最近更新 更多