避免通过隐藏/取消隐藏代码行来编写两个版本的函数

时间:2016-07-08 22:11:04

标签: c++ templates

一个函数有两个版本(下面的代码是简化版)。这两个版本都在程序中使用。在实际功能中,两个版本之间的差异可能发生在两个或三个不同的地方。

如何在不牺牲性能的情况下,通过模板或其他方式避免在代码中编写两个版本?这是一次尝试 使代码更具可读性。

性能至关重要,因为它会运行很多次,我正在为不同的实现编写基准。

(另外,如果我为少数人写图书馆,这是一个好的api吗?)

示例:

int set_intersect(const int* A, const int s_a,
                  const int* B, const int s_b,
                  int* C = 0){

    //if (int* C == 0), we are running version
    //0 of the function.

    //int* C is not known during compilation
    //time for version 1.

    int Count0 = 0;
        //counter for version 0 of the function.

    const int* const C_original(C);
        //counter and pointer for version 1 of
        //the function

    int a = 0;
    int b = 0;
    int A_now;
    int B_now;

    while(a < s_a && b < s_b){
        A_now = A[a];
        B_now = B[b];
        a += (A_now <= B_now);
        b += (B_now <= A_now);
        if (A_now == B_now){    
            if (C == 0){
                Count0++;
            } else {
                C++;
                *(C)=A_now;
            }
        }
    }
    if (C == 0){
        return Count0;
    }else{
        return C - C_original;
    }
}

感谢。

更新

Conditional compile-time inclusion/exclusion of code based on template argument(s)

(其中一些模板看起来很长)

Remove/Insert code at compile time without duplication in C++

(这与我的情况更相似。但我的情况更简单。)

我想以下内容可行,但它增加了一个新论点。

int set_intersect(const int* A, const int s_a,
                  const int* B, const int s_b,
                  int* C = 0,
                  char flag);

将版本0的所有代码放入if (flag == '0') { /* version 0 code */ }

将版本1的所有代码放入if (flag == '1') { /* version 1 code */}

可能可以将标志变量放入模板中(正如Barmar在评论中建议的那样),这样,它不会为函数添加另一个参数。也可以用枚举替换0和1(如enum class set_intersection_type {find_set, size_only})。调用该函数将类似于set_intersect<find_set>(const int* A, const int s_a, const int* B, const int s_b, int* C)set_intersect<size_only>(const int* A, const int s_a, const int* B, const int s_b)希望这比以前更具可读性,编译器足够聪明,可以看到发生了什么。

另一个问题是,如果有人使用findset版本(版本1),然后忘记更改默认参数(int C * = 0),该怎么办?可以通过这种方式调用函数:set_intersect<find_set>(const int* A, const int s_a, const int* B, const int s_b)

可能我可以在评论中使用dasblinkenlight的想法。创建两个包装函数(set_intersectionset_intersection_size)。每个包装器使用不同的参数调用实际函数。还将实际函数列为私有函数,因此没有人可以直接调用它。

对于集合交集的不同实现,也许可以创建带有模板的公共包装器。调用包装器类似于set_intersection<basic>set_intersection<binary_search>set_intersection_size<simd>等。这似乎看起来更好。

3 个答案:

答案 0 :(得分:1)

一般来说似乎可行,问题是你是否想这样做。会说不。从我可以告诉你做两件事:

  1. 版本0计算交叉点的大小
  2. 版本1计算交叉点的大小,并将交叉点写入超过C *的位置,假设有足够的空间来存储它。
  3. 我不仅要考虑速度,还要明确两个不同的功能set_intersectionset_intersection_size,但如果你坚持使用一个,我会根据std::set_intersection对你的代码进行基准测试,如果可能的话,只要C != 0重定向到:: std版本。

    在您当前的版本中,我不会使用您的库。但是我也很难想出一个我更喜欢STL版本的set_intersection定制版本的情况。如果我需要比STL更好的性能,我希望已经将代码中的点确定为瓶颈,我根本不会使用库调用,而是自己编写代码,可能在汇编和展开循环等。

    我有点不高兴这是怎么回事:

    const int* const Count1(C);
        //counter and pointer for version 1 of
        //the function
    ...
        Count1++;
        *(Count1)=A_now;
    

答案 1 :(得分:1)

如果在编译时知道你想要什么版本,你可以使用条件编译。

#define Version_0 //assuming you know this compilation is version 0

然后你可以去:

int set_intersect(...)
#ifdef Version_0
    //Version 0 of the code
#else
    //Version 1 of the code

这样只会编译一个版本的代码。

如果您不知道编辑的版本,我建议您使用两个单独的功能,这样您就不必检查每个函数实例的版本。

答案 2 :(得分:1)

有一个专门研究bool参数的类型:

template<bool b>
struct Counter
{
};

template<>
struct Counter<false>
{
    int c;

    Counter(int *)
    : c(0)
    {
    }

    int operator++() { return ++c; }
    void storeA(const int a_now) {}
};

template<>
struct Counter<true>
{
    const int* const c;

    Counter(int * c_orig)
    : c(c_orig)
    {
    }

    int operator++() { return ++C; }
    void storeA(const int a_now) { *C = a_now; }
}

然后将Counter上的算法专门化为模板参数。请注意,对于这两种情况,这将完全相同,也就是说,您不需要专门化:

template<typename Counter>
struct SetIntersectHelper
{
    static int set_intersect(const int* A, const int s_a,
                             const int* B, const int s_b,
                             int* C)
    {
        // your function's body, using Counter
    }
};

现在,您已准备好添加通用方法:

int set_intersect(const int* A, const int s_a,
                  const int* B, const int s_b,
                  int* C = 0)
{
    return C ? SetIntersectHelper< Counter< true  > >::set_intersect(A, s_a, B, s_b, C):
               SetIntersectHelper< Counter< false > >::set_intersect(A, s_a, B, s_b, C);
}