C - 为结构数组创建初始化宏
C - Creating an initialisation macro for an array of structs
我想用宏来做:
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 预处理器没有任何经验,所以我很好奇:
- 有更好的非宏方案
- gcc 有一个简单的方法来完成这个宏操作
此外,更偏理论的是执行速度。 AfaIk 第一个 post 中的示例只是一个 memcpy(.data[?], _timer_llists, sizeof(_timer_llists)),而编程方法至少需要一个计数器,也许是最后一种情况的 if 语句,以及函数开销(好吧,也许是内联的)。另一方面,这将在 .data.
中保存 space
在这种特殊情况下,我使用的是 gcc-avr,但这个问题反复出现在我的脑海中,我希望有一个通用的方法。
如果您真的需要进行初始化而不是以编程方式进行初始化,那么使用宏可以做的最好的事情是这样的:
#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
,使用此解决方案生成的代码很可能会更短。
另一种方法是编写一个特殊程序,通过将初始化 table 写入 .h 文件并将该 .h 文件包含到您的 .c 文件中来生成初始化。然后,您的构建过程应该在编译之前调用该特殊程序。
您可以使用 "X-macros"。它不一定是硬编码所有内容的改进,但它看起来像这样:
#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] },
...
如果你使用 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
然后添加不同的行)。
(仅供参考,除非您在时间胶囊中找到的硬件上进行编程,否则在比较方法时我什至不会考虑编程替代方案的开销)。
我想用宏来做:
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 预处理器没有任何经验,所以我很好奇:
- 有更好的非宏方案
- gcc 有一个简单的方法来完成这个宏操作
此外,更偏理论的是执行速度。 AfaIk 第一个 post 中的示例只是一个 memcpy(.data[?], _timer_llists, sizeof(_timer_llists)),而编程方法至少需要一个计数器,也许是最后一种情况的 if 语句,以及函数开销(好吧,也许是内联的)。另一方面,这将在 .data.
中保存 space在这种特殊情况下,我使用的是 gcc-avr,但这个问题反复出现在我的脑海中,我希望有一个通用的方法。
如果您真的需要进行初始化而不是以编程方式进行初始化,那么使用宏可以做的最好的事情是这样的:
#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
,使用此解决方案生成的代码很可能会更短。
另一种方法是编写一个特殊程序,通过将初始化 table 写入 .h 文件并将该 .h 文件包含到您的 .c 文件中来生成初始化。然后,您的构建过程应该在编译之前调用该特殊程序。
您可以使用 "X-macros"。它不一定是硬编码所有内容的改进,但它看起来像这样:
#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] },
...
如果你使用 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
然后添加不同的行)。
(仅供参考,除非您在时间胶囊中找到的硬件上进行编程,否则在比较方法时我什至不会考虑编程替代方案的开销)。