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

时间:2013-08-01 18:24:10

标签: c++ c++11 metaprogramming

也许我被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做到这一点吗?

3 个答案:

答案 0 :(得分:1)

我看到这样做的方法是编写回调函数。因此,您可以编写一次逻辑部分,就像您在第二个文本块中一样。您还可以定义函数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

答案 1 :(得分:1)

嗯,一个解决方案就是把冗余代码的一部分拿出来放到模板中,比如

  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等。

答案 2 :(得分:1)

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占位符。实际工作在每个类的 operator()成员函数中完成。

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