Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reducing code duplication in C program with nearly identical statements in if/else?

I am trying to reduce code duplication in my C program, where all the statements in each branch of an if/else block are identical, except for a function name and its arguments. The idea is that the user specifies either x, y, or z, and the program measures how long it takes to run either func_x, func_y, or func_z 1000 times.

More specifically, here is the high level design of the C code:

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

As you can see, in the main function all the statements in each branch of the if-else block are identical; the only difference is that either func_x, func_y, or func_z.

In a functional language, this pattern can be abstract by having a function run_timing_benchmark which takes the func_* and dat_* arguments and hten runs the loop (possibly using polymorphism to define the signature of g). While I can use function pointers in C, I can't write a polymorphic type signature.

What are suggestions for how to reduce the duplication in this program so that the timing code is only defined once? In practice I may have dozens of functions (not just x/y/z) to benchmark using the same code, and the timing code may be more complex.

like image 553
jII Avatar asked Dec 31 '22 16:12

jII


1 Answers

You can use a macro to generate the code. Since all the structures and functions follow a common naming scheme, you can use token pasting to generate them.

#define PROCESS(suffix, func_name_var, path_var, sum_var, time_var) \
time_var = time();
if(strcmp(func_name_var, #suffix) == 0) { \
    struct dat_##suffix args = read_dat_##suffix##_from_file(path_var); \
    for (int i = 0; i < 1000; i++) { \
        sum_var += func_##suffix(args); \
    } \
time_var = time() - time_var;

then you can use it like this:

        PROCESS(x, func_name, path, a, t)
        else PROCESS(y, func_name, path, a, t)
        else PROCESS(z, func_name, path, a, t)
like image 166
Barmar Avatar answered Jan 05 '23 14:01

Barmar