【问题标题】:Macro function to avoid sizeof boilerplate during function call在函数调用期间避免 sizeof 样板的宏函数
【发布时间】:2020-02-25 09:24:24
【问题描述】:

我声明了一个具有以下签名的函数(实现被简化):

#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 个问题:

  1. 我必须定义FOO_ARR_3FOO_ARR_4 等...
  2. 缺乏类型安全性。如果来电者传递了不同的东西,那么struct test_s[]

问题:是否可以将其实现为像#define FOO_ARR(...) 这样的可变参数宏函数?

【问题讨论】:

  • "我声明了一个具有以下签名的函数" 您是完全使用此签名绑定到此,还是可以随意更改它?
  • @alk 是的,如果可以简化函数调用,可以更改签名
  • @alk 但我也需要支持malloced 指针。这就是它被这样声明的原因

标签: c function macros type-safety variadic


【解决方案1】:

与其通过将复杂的初始化包装到一个复杂的(如果可能的话)可变参数宏中来使事情变得更加复杂,只需将相关函数声明为可变参数。

这可能看起来像这样:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多