首页 > 解决方案 > 在 if/else 中使用几乎相同的语句减少 C 程序中的代码重复?

问题描述

我试图减少我的 C 程序中的代码重复,其中 if/else 块的每个分支中的所有语句都是相同的,除了函数名称及其参数。这个想法是用户指定xyz,然后程序测量运行func_xfunc_yfunc_z1000 次所需的时间。

更具体地说,这里是 C 代码的高级设计:

// struct definitions
struct dat_x {...};
struct dat_y {...};
struct dat_z {...};

// reading structs from a text file
struct dat_x read_dat_x_from_file(char *path);
struct dat_y read_dat_y_from_file(char *path);
struct dat_z read_dat_z_from_file(char *path);

// functions
int func_x(struct dat_x);
int func_y(struct dat_y);
int func_z(struct dat_z);

// runner computing runtime of func_x, func_y, or func_z
int main(int argc, char** argv) {
    char *func_name = argv[1];
    char *path = argv[2];

    int a;
    clock_t t;

    if (strcmp(func_name, "x") == 0) {
        struct dat_x args = read_dat_x_from_file(path);

        t = clock();
        for (int i = 0; i < 1000; i++) {
            a += func_x(args);
        }
        t = clock() - t;

    } else if (strcmp(func_name, "y") == 0) {
        struct dat_y args = read_dat_y_from_file(path);

        t = clock();
        for (int i = 0; i < 1000; i++) {
            a += func_y(args);
        }
        t = clock() - t;

    } else if (strcmp(func_name, "z") == 0) {
        struct dat_z args = read_dat_z_from_file(path);

        t = clock();
        for (int i = 0; i < 1000; i++) {
            a += func_z(args);
        }
        t = clock() - t;

    }

    // report runtime
    double e = ((double)t) / CLOCKS_PER_SEC;
    printf("%s: %f %d\n", func_name, e, a);
}

如您所见,在main函数中,if-else 块的每个分支中的所有语句都是相同的;唯一的区别是func_x,func_yfunc_z.

在函数式语言中,这种模式可以通过一个函数来抽象,该函数run_timing_benchmark接受func_*anddat_*参数并且 hten 运行循环(可能使用多态性来定义 的签名g)。虽然我可以在 C 中使用函数指针,但我不能编写多态类型签名。

关于如何减少该程序中的重复以使时序代码只定义一次,有什么建议?在实践中,我可能有几十个函数(不仅仅是x// yz使用相同的代码进行基准测试,而且时序代码可能更复杂。

标签: cdesign-patternscode-duplication

解决方案


一种想法可能是建立一个联合来抽象函数签名和返回值的差异。然后构建一个函数表并根据传递的名称调用正确的函数。像这样的东西:(警告:未经测试!)

// struct definitions
struct dat_x {...};
struct dat_y {...};
struct dat_z {...};

// Build a union that contains the structs
union uargs
{
    struct dat_x;
    struct dat_y;
    struct dat_z;
};

// reading structs from a text file (but packaged into the union type)
union uargs read_dat_x_from_file(char *path);
union uargs read_dat_y_from_file(char *path);
union uargs read_dat_z_from_file(char *path);

// functions
int func_x(union uargs dat);
int func_y(union uargs dat);
int func_z(union uargs dat);

struct table_t
{
    char *name;
    union uargs (*read_dat_fp);
    int (*fp)(union uargs dat);
};

// Table of function pointers
struct table_t func_table[]
{
    { "x", read_dat_x_from_file, func_x},
    { "y", read_dat_y_from_file, func_y},
    { "z", read_dat_x_from_file, func_z}
};

// runner computing runtime of func_x, func_y, or func_z
int main(int argc, char** argv) {
    char *func_name = argv[1];
    char *path = argv[2];

    int a;
    clock_t t;

    for(int i = 0; i < sizeof(func_table) / sizeof(table_t); i++)
    {
        if(strcmp(func_name, func_table[i].name) == 0)
        {
            union uargs args = func_table[i].read_dat_fp(path);
            t = clock();
            for (int i = 0; i < 1000; i++) {
                a += func_table[i].fp(args);
            }
            t = clock() - t;
            break;
        }
    }

    // report runtime
    double e = ((double)t) / CLOCKS_PER_SEC;
    printf("%s: %f %d\n", func_name, e, a);
}

这消除了代码重复,并且在某种程度上也很容易扩展。另一种选择可能是在@Barmar的这个答案中使用一些宏魔法。

编辑:当然,您可以简单地使用void*和类型转换来传递指向结构的指针,而不是联合,在函数内部根据需要重新转换它们。但是你完全抛弃了所有类型检查。


推荐阅读