There are two versions of a function (the code below is a simplified version). Both versions are used in the program. In the actual function, the differences between the two versions can occur at two or three different places.
How to avoid writing both versions in the code without sacrificing performance, through template or other means? This is an attempt to make the code more readable.
Performance is critical because it will get run many many times, and I am writing benchmark for different implementations.
(Also, is this an ok api, if I am writing a library for a few people?)
Example:
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;
}
}
Thanks.
Updates:
Conditional compile-time inclusion/exclusion of code based on template argument(s)
(some of those templates look so long)
Remove/Insert code at compile time without duplication in C++
(this is more similar to my case. my case is simpler though.)
I guess the following can work, but it adds a new argument.
int set_intersect(const int* A, const int s_a,
const int* B, const int s_b,
int* C = 0,
char flag);
put all code for version 0 into if (flag == '0') { /* version 0 code */ }
put all code for version 1 into if (flag == '1') { /* version 1 code */}
Probably can put the flag variable into template (as Barmar suggested in comments), that way, it doesn't feel like adding another argument for the function. Can also replace the 0 and 1 with enum (like enum class set_intersection_type {find_set, size_only}
). Calling the function will be like set_intersect<find_set>(const int* A, const int s_a, const int* B, const int s_b, int* C)
or set_intersect<size_only>(const int* A, const int s_a, const int* B, const int s_b)
Hopefully this is more readable than before, and the compiler is smart enough to see what is going on.
Another problem is, what if someone uses the findset version (version 1), and then forgets to change the default argument (int C* = 0)? It is possible to call the function this way: set_intersect<find_set>(const int* A, const int s_a, const int* B, const int s_b)
.
May be I can use dasblinkenlight's idea in the comments. Create two wrapper functions (set_intersection
, set_intersection_size
). Each wrapper calls the actual function with different arguments. Also list the actual function as a private function so no one can call it directly.
For the different implementations of set intersections, maybe can create a common wrapper with templates. Calling the wrapper would be similar to set_intersection<basic>
, set_intersection<binary_search>
, or set_intersection_size<simd>
etc. This seems to look better.