如何实现能够对结构体中不同字段执行相同功能的函数?

4

我有以下结构体:

struct tmatrix {
    struct tmatrix_entry {
        double price;
        double amt;
    } **entries;

    double *stocks;
    double *needs;
    int rows;
    int cols;
};

并且有以下功能:
void tmatrix_set_prices (struct tmatrix *tm, double *prices[]) {
    for (int i = 0; i < tm->rows; ++i)
        for (int j = 0; j < tm->cols; ++j)
            tm->entries[i][j].price = (prices) ? prices[i][j] : 0;
}

void tmatrix_set_amts (struct tmatrix *tm, double *amts[]) {
    for (int i = 0; i < tm->rows; ++i)
        for (int j = 0; j < tm->cols; ++j)
            tm->entries[i][j].amt = (amts) ? amts[i][j] : 0;
}

我猜创建两个几乎相同的函数并不是很酷,所以到目前为止我得出以下结论:

#define TMATRIX_SET_2D_ARRAY(TM, FIELD, ARRAY)\
do {\
    for (int i = 0; i < TM->rows; ++i)\
        for (int j = 0; j < TM->cols; ++j)\
            TM->entries[i][j].FIELD = (ARRAY) ? ARRAY[i][j] : 0;\
} while (0)

然后:

void tmatrix_set_prices (struct tmatrix *tm, double *prices[]) {
    TMATRIX_SET_2D_ARRAY(tm, price, prices);
}

void tmatrix_set_amts (struct tmatrix *tm, double *amts[]) {
    TMATRIX_SET_2D_ARRAY(tm, amt, amts);
}

这是一个不好的解决方案吗?有人告诉我是这样的。他们还告诉我可以使用 offsetof() 来完成,但它看起来更复杂、更难使用。或者将 entries 设为数组而不是结构体会更好吗?实现这种函数的最佳方法是什么?


2
这真的将成为一个编码风格偏好问题。有很多方法可以解决这个问题,而不使用宏,但是宏确实能够完成它的工作。这可能更适合 CodeReview - aruisdante
1
你说*...创建两个几乎相同的函数并不好*,但是你的解决方案仍然有相同的函数。一个改进的方法是让一个函数完成这项工作。 - γηράσκω δ' αεί πολλά διδασκόμε
3个回答

1
你可以通过使用设置struct tm的一个条目的函数来减少一些冗余逻辑。你可以在更高级别的函数中使用这些函数。
typedef void (*set_matrix_data_function)(struct tmatrix *tm, double *data[], int i, int j);

void set_matrix_price(struct tmatrix *tm, double *data[], int i, int j)
{
   tm->entries[i][j].price = (data) ? data[i][j] : 0;
}

void set_matrix_amt(struct tmatrix *tm, double *data[], int i, int j)
{
   tm->entries[i][j].amt = (data) ? data[i][j] : 0;
}

void tmatrix_set_data (struct tmatrix *tm, double *data[], set_matrix_data_function fun)
{
    for (int i = 0; i < tm->rows; ++i)
        for (int j = 0; j < tm->cols; ++j)
           fun(tm, prices, i, j);
}

void tmatrix_set_prices (struct tmatrix *tm, double *prices[])
{
   tmatrix_set_data(tm, prices, set_matrix_price);
}

void tmatrix_set_amts (struct tmatrix *tm, double *amts[])
{
   tmatrix_set_data(tm, prices, set_matrix_amt);
}

请注意,该代码比仅有两个功能的代码更加冗长。只有当您需要处理更多的数据成员时,才会得到回报。

1
我没有测试过这个方法,但是可以尝试使用offsetof()来实现类似以下的功能:
#include <stddef.h>

static void tmatrix_set_field(struct tmatrix *tm, double *vals[],
                              const size_t f_offset) {
    for (int i = 0; i < tm->rows; ++i)
        for (int j = 0; j < tm->cols; ++j)
            *(double *)(((char *)&tm->entries[i][j]) + f_offset) =
                 (vals) ? vals[i][j] : 0;
}

void tmatrix_set_prices (struct tmatrix *tm, double *prices[]) {
    tmatrix_set_field(tm, prices, offsetof(struct tmatrix_entry, price));
}

void tmatrix_set_amts (struct tmatrix *tm, double *amts[]) {
    tmatrix_set_field(tm, amts, offsetof(struct tmatrix_entry, amt));
}

你提到它“看起来更复杂”,但似乎比宏潜在地不那么复杂。

0
我会更关注你向该结构的用户呈现的接口,而不是固定它的内部实现方式。通过向结构的用户提供两个函数,您可以在使用宏调用字段名称或使用传递到该字段偏移量的函数调用之间来回切换,而不会对代码库产生任何连锁反应。我同意这只是一种风格偏好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接