How to implement functions that do the same with different fields of the structure?

I have the following structure:

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

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

and the following functions:

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

I think it’s not great to make 2 almost identical functions, so I still came to this:

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

And then:

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

Is this a bad decision? I was told so. I was also told that this can be done using offsetof (), but it looks more complicated and tricky to use. Or maybe it would be better to make an entriesarray instead of a structure? What is the best way to implement such features?

+4
source share
3 answers

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

, " ", , .

+1

I would focus on the interface that you present to users of this structure, and not how it is implemented internally. Thanks to the two functions available to users of the structure, you can switch between them to force these function bodies to call macros, passing the field name, or calls to the function passed by offset to this field without rounding effect through your code base. I agree that this is just a style preference.

0
source

All Articles