【问题标题】:How to reuse the same C code but with different operators如何重用相同的 C 代码但使用不同的运算符
【发布时间】:2016-02-19 13:32:36
【问题描述】:

我正在用 C 语言构建一个库,该库中的一个函数类似于:

void myFunction(double *inPtr, double start, double step, double *outPtr, int N, t_shape shape)
{
    int i;

    switch (shape) {

        default:
        case ShapeLinear:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) = *(inPtr++) * start;
                start += step;
            }
            /* more stuff going on */
            break;

        case ShapeExponential:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) = *(inPtr++) * start;
                start *= step;
            }
            /* more stuff going on */
            break;

        case ShapeSquared:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) = *(inPtr++) * start * start;
                start += step;
            }
            /* more stuff going on */
            break;

        case ShapeCubed:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) = *(inPtr++) * start * start * start;
                start += step;
            }
            /* more stuff going on */
            break;
    }
}

这是一个简化版本,仅用于演示。
库中的实际函数有更多的案例,每个案例都更长,更复杂。

现在我想创建一个完全相同的函数的另一个版本,唯一的区别是我想添加和分配 它。

void myFunctionAdd(double *inPtr, double start, double step, double *outPtr, int N, t_shape shape)
{
    int i;

    switch (shape) {

        default:
        case ShapeLinear:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) += *(inPtr++) * start;
                start += step;
            }
            /* more stuff going on */
            break;

        case ShapeExponential:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) += *(inPtr++) * start;
                start *= step;
            }
            /* more stuff going on */
            break;

        case ShapeSquared:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) += *(inPtr++) * start * start;
                start += step;
            }
            /* more stuff going on */
            break;

        case ShapeCubed:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) += *(inPtr++) * start * start * start;
                start += step;
            }
            /* more stuff going on */
            break;
    }
}

正如我所说,我正在处理的真正功能更长、更复杂,因此必须将相同的代码重写两次确实是我不想做的事情。
这将是一种糟糕的编程习惯,并且更难以维护。

是否有 C 编程技术来处理这个问题?
块(闭包)在这种情况下有用吗?
我需要以某种方式重构代码吗?
你会如何解决这个问题?

【问题讨论】:

    标签: c operators semantics code-reuse


    【解决方案1】:

    经典的 c 解决方案是传入一个函数,该函数被调用以执行您想要更改的操作。然而,这取决于操作的基本形状是否相同

    typedef int (*func_ptr)(int,int); 
    myFunction(....... func_ptr do_what)
    {
    int i;
    
    switch (shape) {
    
        default:
        case ShapeLinear:
            /* more stuff going on */
            for (i = 0; i < N; i++) {
                *(outPtr++) = func_ptr(*(inptr++), outPtr);
                start += step;
            }
            /* more stuff going on */
            break;
    }
    
    int assign(int a, int b)
    {
    return a;
    }
    
    int add_assign(int a, int b)
    {
    return a + b;
    }
    

    然后调用

     myFunction(......assign);
    

    myfunction(......add_assign);
    

    在你的情况下这感觉有点强迫,因为我们必须传递 outPtr 的旧值

    【讨论】:

    • 谢谢你,但是我不明白如何不用你的策略来复制我的代码。我仍然需要编写两个 do_what() 函数。一个会将 outPtr 添加到结果中,而另一个不会。的确,我可以放一个条件语句,但是它会对效率产生影响。最重要的是,我并不热衷于在循环中调用函数,除非它是绝对必要的。
    • 是的,但是 do_what 函数只有一行 - 请参阅编辑
    【解决方案2】:

    您尝试执行的操作或多或少类似于地图操作。我会改为定义一个函数 Map 并使用不同的输入函数调用它。下面是一个可编译的例子。希望您可以扩展它以适应您的问题。

    #include <assert.h>
    #include <stdio.h>
    
    #define LEN(array) (sizeof (array) / sizeof (array)[0])
    
    typedef double (*Function)(double x);
    
    static void Map(Function f, const double arr[], int arrLen, double result[], int resultLen)
    {
        int i;
    
        assert(resultLen >= arrLen);
    
        for (i = 0; i < arrLen; i++) {
            result[i] = f(arr[i]);
        }
    }
    
    
    static double Square(double x)
    {
        return x * x;
    }
    
    
    int main(void)
    {
        double arr[] = {1.0, 2.0, 3.0, 4.0};
        double result[LEN(arr)];
        int i;
    
        Map(Square, arr, LEN(arr), result, LEN(result));
    
        for (i = 0; i < LEN(result); i++) {
            printf("%f\n", result[i]);
        }
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      使用一个函数来代替运算符,该函数接受一些常量来定义函数的作用。这样,您可以以相同的方式构建许多逻辑,并且只改变函数的参数来决定函数中的实际操作是什么。

      一个或多个函数也可以是一系列重载,以帮助您避免重复过多的代码。请注意,您必须重构代码以传递分配(或非分配)的目标。

      // rough, simplistic, pseudo-codish example:
      
      public void MyOperatorFunc(int opType, int var1, int var2, int* output) {
            if (opType == 1 ) {
                output = output + var1 + var2; 
            }
            else if (opType == 2) {
                output = var1 + var2;
            }
            // ...
       }
      

      请注意,对于这样的解决方案,唯一重要的“形状”是您的操作数数量。

      【讨论】:

      • 感谢您的回复,但我不明白这将如何帮助我不重复我的代码。 opType == 1 时调用的相同代码将在 opType == 2 时调用。 opType == 2 时唯一不同的是赋值运算符。但这意味着我仍然需要复制我的代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-02
      • 1970-01-01
      相关资源
      最近更新 更多