C++元编程的最佳实践:逻辑流

Best practice C++ metaprogramming: logic flow

本文关键字:编程 最佳 C++      更新时间:2023-10-16

也许我被Ruby宠坏了,但在我看来,如果我有两个函数使用相同的基本逻辑(但细节不同),我应该只需要写一次逻辑——因此,我应该只能在一个地方维护代码。

以下是我在许多不同函数中重复使用的基本逻辑。更改的零件标记为A、B、C、D、E和F。

  if (recursions) {
    while (lcurr || rcurr) {
      if (!rcurr || (lcurr && (lcurr->key < rcurr->key))) {
        // A
        lcurr   = lcurr->next;
      } else if (!lcurr || (rcurr && (rcurr->key < lcurr->key))) {
        // B
        rcurr   = rcurr->next;
      } else { // keys are == and both present
        // C
        lcurr   = lcurr->next;
        rcurr   = rcurr->next;
      }
    }
  } else {
    while (lcurr || rcurr) {
      if (!rcurr || (lcurr && (lcurr->key < rcurr->key))) {
        // D
        lcurr         = lcurr->next;
      } else if (!lcurr || (rcurr && (rcurr->key < lcurr->key))) {
        // E
        rcurr         = rcurr->next;
      } else { // keys == and both left and right nodes present
        // F
        lcurr         = lcurr->next;
        rcurr         = rcurr->next;
      }
    }
  }

函数的返回值也可能不同。如果可能的话,我希望在不同的地方也能有额外的逻辑。

我意识到这可以通过C宏来实现,但它们似乎不是特别可维护。我还意识到,如果我的矩阵类型使用嵌套的STL列表,这可能会更容易。但是C++11(或旧的C++)中是否有任何功能允许该逻辑只写一次?也许有人能用lambdas做这件事吗?

我看到的方法是编写回调函数。所以你可以写一次逻辑部分,就像你在第二个文本块中写的一样。您还可以定义函数A、B、C、D、E和F.

在您的逻辑函数中,您将传入所需的参数和指向回调函数的指针。然后,在逻辑函数中,您将调用这些回调并向它们传递所需的参数。

老实说,这似乎最终会是更多的工作。您可以为逻辑维护一个单一的事实点,但函数指针可能会带来巨大的痛苦,并降低代码的可读性。

为了提供尽可能多的信息,举个例子:

int addTwoNumbers(int a, int b) { //A simple adding function
   return a + b; 
}
int subtractTwoNumbers(int a, int b) { //A simple subtracting function
    return a - b;
}
/*
 * This is the fun one. The first argument is a pointer to a function. The other 
 * arguments are the numbers to do math with. They aren't as important.
 * The important part is that, so long as the function declaration matches the one here
 * (so a function that returns an int and takes in two ints as arguments) it can be
 * used by this function
 */
void math(int (*mathFunc)(int, int), int one, int two) {
    cout << *mathFunc(one, two);
}
int main(int argc, char* argv[]) {
    int whichMath = 0; //Assume 1 is add, 2 is subtract
    if(whichMath == 1) {
        math(&addTwoNumbers, 5, 6); //we're going to add 5 and 6
    } else {
        math(&subtractTwoNumbers, 5, 6); // we're going to subtract 5 and 6
    }
}

如果NO有意义,那么欢迎您加入我们这些与函数指针作斗争的大军。再次,我想说,你应该只写两个单独的函数,因为你可以看到这会变得多么丑陋。

作为免责声明,我没有编译此代码。我正在工作,这些机器上没有c++编译器。

我过去曾大量使用此网站作为函数指针的参考:http://www.newty.de/fpt/fpt.html#defi

好吧,一种解决方案是提取多余的代码,并将其放入模板中,例如

  template<T1, T2, T3>
  bool TESTKEYS(T1 lcurr, T2 rcurr, T3 actor)
  {
    while (lcurr || rcurr) {
      if (!rcurr || (lcurr && (lcurr->key < rcurr->key))) {
        if (actor.TestLeft(....)) return false;
        lcurr         = lcurr->next;
      } else if (!lcurr || (rcurr && (rcurr->key < lcurr->key))) {
        if (actor.TestRight(....)) return false;
        rcurr         = rcurr->next;
      } else { // keys == and both left and right nodes present
        if (actor.TestBoth(....)) return false;
        lcurr         = lcurr->next;
        rcurr         = rcurr->next;
      }
    }
    return true;
  }

您需要自己决定TestLeft等使用哪些参数。

template<typename A, typename B, typename C>
void compute (/*some parameters */)
{
   if (recursions) {
     while (lcurr || rcurr) {
       if (!rcurr || (lcurr && (lcurr->key < rcurr->key))) {
         auto aResult = A (lcurr, rcurr);
        lcurr   = lcurr->next;
       } else if (!lcurr || (rcurr && (rcurr->key < lcurr->key))) {
        auto bResult = B (lcurr, rcurr);
       } // ... and so on
       C (aResult, bResult);
    } // ... etc
} 

要调用compute,您需要编写要传递的来代替A到F占位符。实际工作是在每个类的运算符()成员函数中完成的。

class A1 {
  public:
    double operator() (SomeType t1, SomeType t2) {
      // do work
    }
};
class A2 {
  public:
    int operator() (SomeType t1, SomeType t2) {
      // do work
    }
};
class B1 {
  public:
    char* operator() (SomeType t1, SomeType t2) {
      // do work
    }
};
class B2 {
  public:
    SomeClass* operator() (SomeType t1, SomeType t2) {
      // do work
    }
};
class C1 {
  public:
    int operator() (double t1, char* t2) {
}
class C2 {
  public:
    int operator() (int t1, SomeClass* t2) {
}
compute<A1, B1, C1>(whatever);
compute<A2, B2, C2>(whatever);

请注意A1和B1返回类型如何匹配C1参数,A2、B2和C2也是如此。

auto需要C++11,如果你不能使用它,你将不得不做一些额外的工作:

class A1 {
  public:
    typedef double result_type;
    double operator() (SomeType t1, SomeType t2) {
      // do work
    }
};

compute 内部

             typename A::result_type aResult = A (lcurr, rcurr);