在函数调用期间避免 sizeof 样板的宏函数
Macro function to avoid sizeof boilerplate during function call
我用以下签名声明了一个函数(简化了实现):
#include <stdio.h>
struct test_s{
int a, b;
};
void foo(struct test_s **out, size_t *szs, size_t arr_len){
for(size_t i = 0; i < arr_len; i++){
for(size_t j = 0; j < szs[i]; j++){
struct test_s ts = out[i][j];
printf("a = %d; b = %d\n", ts.a, ts.b);
}
}
}
如果调用者使用数组调整为指针,可以这样调用:
int main(void){
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
foo((struct test_s *[]){a1, a2},
(size_t[]){sizeof a1 / sizeof(struct test_s), sizeof a2 / sizeof(struct test_s)},
2);
}
可以看出函数调用看起来复杂、容易出错且难以阅读。
当谈到使用 3 个参数时,情况变得更糟:
int main(void){
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
struct test_s a3[] = {{2, 3}, {4, 5}};
foo((struct test_s *[]){a1, a2, a3},
(size_t[]){sizeof a1 / sizeof(struct test_s), sizeof a2 / sizeof(struct test_s), sizeof a3 / sizeof(struct test_s)},
3);
}
所以如果涉及到数组,最好将其实现为宏。如下实现它非常简单:
#define FOO_ARR_2(a1, a2) \
do{ \
foo((struct test_s *[]){a1, a2}, \
(size_t[]){sizeof a1 / sizeof(struct test_s), sizeof a2 / sizeof(struct test_s)}, \
2);\
} while(0)
我发现这样的宏有 2 个问题:
- 我必须定义
FOO_ARR_3
、FOO_ARR_4
等...
- 缺乏类型安全。如果来电者传递了不同的东西,那么
struct test_s[]
问题: 是否可以将其实现为像 #define FOO_ARR(...)
这样的可变参数宏函数?
不要通过将复杂的初始化包装成复杂的(如果可能的话)可变参数宏来使事情变得更加复杂,只需将有问题的函数声明为可变参数本身即可。
这可能看起来像这样:
#include <stdio.h>
#include <stdarg.h>
/* expects: number-of-arrays followed by
number-of-arrays tuples {arrays-size, pointer to array's 1st element} */
struct test_s{
int a, b;
};
void foo(size_t arr_len, ...)
{
va_list vl;
va_start(vl, arr_len);
for (size_t i = 0; i < arr_len; ++i)
{
size_t s = va_arg(vl, size_t);
struct test_s * p = va_arg(vl, struct test_s *);
for (size_t j = 0; j < s; ++j)
{
struct test_s ts = p[j];
printf("a = %d; b = %d\n", ts.a, ts.b);
}
}
va_end(vl);
}
这样使用:
struct test_s{
int a, b;
};
void foo(size_t, ...);
int main(void)
{
/* using two arrays: */
{
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
foo(2,
sizeof a1 / sizeof *a1, a1,
sizeof a2 / sizeof *a2, a2
);
}
/* using three arrays: */
{
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
struct test_s a3[] = {{2, 3}, {4, 5}};
foo(3,
sizeof a1 / sizeof *a1, a1,
sizeof a2 / sizeof *a2, a2,
sizeof a3 / sizeof *a3, a3
);
}
}
我用以下签名声明了一个函数(简化了实现):
#include <stdio.h>
struct test_s{
int a, b;
};
void foo(struct test_s **out, size_t *szs, size_t arr_len){
for(size_t i = 0; i < arr_len; i++){
for(size_t j = 0; j < szs[i]; j++){
struct test_s ts = out[i][j];
printf("a = %d; b = %d\n", ts.a, ts.b);
}
}
}
如果调用者使用数组调整为指针,可以这样调用:
int main(void){
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
foo((struct test_s *[]){a1, a2},
(size_t[]){sizeof a1 / sizeof(struct test_s), sizeof a2 / sizeof(struct test_s)},
2);
}
可以看出函数调用看起来复杂、容易出错且难以阅读。
当谈到使用 3 个参数时,情况变得更糟:
int main(void){
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
struct test_s a3[] = {{2, 3}, {4, 5}};
foo((struct test_s *[]){a1, a2, a3},
(size_t[]){sizeof a1 / sizeof(struct test_s), sizeof a2 / sizeof(struct test_s), sizeof a3 / sizeof(struct test_s)},
3);
}
所以如果涉及到数组,最好将其实现为宏。如下实现它非常简单:
#define FOO_ARR_2(a1, a2) \
do{ \
foo((struct test_s *[]){a1, a2}, \
(size_t[]){sizeof a1 / sizeof(struct test_s), sizeof a2 / sizeof(struct test_s)}, \
2);\
} while(0)
我发现这样的宏有 2 个问题:
- 我必须定义
FOO_ARR_3
、FOO_ARR_4
等... - 缺乏类型安全。如果来电者传递了不同的东西,那么
struct test_s[]
问题: 是否可以将其实现为像 #define FOO_ARR(...)
这样的可变参数宏函数?
不要通过将复杂的初始化包装成复杂的(如果可能的话)可变参数宏来使事情变得更加复杂,只需将有问题的函数声明为可变参数本身即可。
这可能看起来像这样:
#include <stdio.h>
#include <stdarg.h>
/* expects: number-of-arrays followed by
number-of-arrays tuples {arrays-size, pointer to array's 1st element} */
struct test_s{
int a, b;
};
void foo(size_t arr_len, ...)
{
va_list vl;
va_start(vl, arr_len);
for (size_t i = 0; i < arr_len; ++i)
{
size_t s = va_arg(vl, size_t);
struct test_s * p = va_arg(vl, struct test_s *);
for (size_t j = 0; j < s; ++j)
{
struct test_s ts = p[j];
printf("a = %d; b = %d\n", ts.a, ts.b);
}
}
va_end(vl);
}
这样使用:
struct test_s{
int a, b;
};
void foo(size_t, ...);
int main(void)
{
/* using two arrays: */
{
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
foo(2,
sizeof a1 / sizeof *a1, a1,
sizeof a2 / sizeof *a2, a2
);
}
/* using three arrays: */
{
struct test_s a1[] = {{0, 1}, {2, 3}, {4, 5}};
struct test_s a2[] = {{4, 6}};
struct test_s a3[] = {{2, 3}, {4, 5}};
foo(3,
sizeof a1 / sizeof *a1, a1,
sizeof a2 / sizeof *a2, a2,
sizeof a3 / sizeof *a3, a3
);
}
}