Function testing using function pointers in C++

I do like the way programmers think in terms of DRY, for instance, among other forms of optimization. A couple of days ago I wanted to test different implementations of the same algorithm in C++.

The usual approach would be to implement those algorithms, call them in main() and see the results. A more interesting approach would be to write a validator for each function and print whether the result is correct or not.

Since both are boring and break the rule of DRY, I’ve decided to go complicated but productive, so I’ve implemented a function testing method using function pointers that will allow me to write a single function and use it to test the algorithms using different input and expected results.

Let’s say I have 3 algorithms that implement Modular Exponentiation.

uint64 modexp1new(const uint64 &a, const uint64 &_p, const uint64 &n){
 ...
}

uint64 modexp2(const uint64 &_a, const uint64 &_p, const uint64 &_n){
 ...
} 

uint64 modexp3(const uint64 &a, const uint64 &p, const uint64 &n){
 ...
}

If you are not familiar with the syntax, const means that the parameter is constant, thus it will not be modified inside the function. The & means that the parameter is passed as a reference, read the memory address of the calling variable, so we don’t overload fill the RAM with new variables new copies of the variables.

Now, the testing part. For my very own purposes I just want to test those particular functions and know whether if the result they output conforms what I do expect. Some people call this unit testing.
I do also want to test it in different scenarios, meaning input and output.

So, I’m creating a function that will get the three parameters required to call the functions, and a fourth parameter that is the value I do expect to be the correct answer.

Now, since I don’t want to repeat myself writing a call for each case, I’m going to create a function pointer that is an array and has the function’s address as their value. That way I can call them in a loop, and voila! we are done.

Finally, after calling the function I check the result with the expected value and print an OK or FAIL.

Tricky part for this could be understanding the function pointer. Two things to consider: the returning value from the referenced functions has to be the same for all. Second: the input definition for each function has to be similar too. This is important because function pointers are just pointers and they need to know the size of the data type in order to navigate the memory.

For this sample code the uint64 is a typedef for long long, of course. Full code is below.

void testAlgos(const uint64 &a, const uint64 &e, const uint64 &n, const uint64 &r){
  uint64 (*fP[3])(const uint64&, const uint64&, const uint64&) = { &modexp1new, &modexp2, &modexp3 };
  uint64 t;

  for(int i=0; i<3; i++){
  t = fP[i](a, e, n);
  std::cout < < "F(" << i << ") " << t << "\t";
  if (t == r){
    std::cout << "OK";
  } else {
    std::cout << "FAIL";
  }
    std::cout << std::endl;
  }
}

Now that I have this, I can use it in main this way.

int main(){

  uint64 a = 3740332;
  uint64 e = 44383;
  uint64 n = 3130164467;
  uint64 r = 1976425102;
  testAlgos(a, e, n, r);

  a = 404137263;
  r = 2520752541;
  testAlgos(a, e, n, r);

  a = 21;
  e = 3;
  n = 1003;
  r = 234;
  testAlgos(a, e, n, r);

  return 0;
}

Resulting in:

F(0) 657519405 FAIL
F(1) 1976425102 OK
F(2) 657519405 FAIL
F(0) -2752082808 FAIL
F(1) 2520752541 OK
F(2) -2752082808 FAIL
F(0) 234 OK
F(1) 234 OK
F(2) 234 OK 

I’m happy about not having to write tests like this for all use cases:

std::cout < < "f1: " << modexp1new(a, e, n) << std::endl;
std::cout << "f2: " << modexp2(a, e, n) << std::endl;
std::cout << "f3: " << modexp3(a, e, n) << std::endl; 

Now, happiness can be more exciting if I do use templates so I can test any function independent of the data type for the returning and input values. You know, more abstraction. Homework if time allows! Have fun!

  • Mariusz Ceier

    “The & means that the parameter is passed as a reference, read the memory address of the calling variable, so we don’t overload RAM with new variables.”

    That’s not true, even if you use ‘&’ you overload RAM with pointer (reference), so in terms of memory in this case you gain nothing. References introduce additional indirections, so probably you lose some time, unless these functions get inlined – but they shouldn’t since you’re using pointers to functions.

  • dhardy

    As Mariusz pointed out, a pointer (on 64-bit machines) is as big as a uint64_t, and requires indirection to access.

    But go read some Imperfect C++, you’ll enjoy it!

  • http://stone-head.org/ Rudy Godoy

    Yes, I do agree. However the size of the required RAM for a memory address storage is smaller usually. Thanks for pointing it out.

  • http://stone-head.org/ Rudy Godoy

    I was not aware of that book, looks great. I’m going to get me a copy. Thanks!

  • dhardy

    Don’t be so sure how that pans out — values may be passed in registers and compiler optimisations (unimpeded by references) may not generate the code you expect. As far as I am aware, passing by reference is more useful in avoiding the extra heap allocations copying a (non-empty) vector, string or other container would require.