mardi 23 avril 2019

Creating a C++ template function that allows multiple types of array containers

In modern C++ you can create arrays by three primary methods shown below.

// Traditional method
int array_one[] = {1, 2, 3, 4}
// Vector container
std::vector<int> array_two = {1, 2, 3, 4}
// array container
std::array<int, 4> array_three = {1, 2, 3, 4}

While each array method contains the same data, they are inherently different containers. I am writing a very simple Unit Test class with template functions to make it easier to pass multiple data types. I have an example shown below for the .hpp and .cpp calling file. The one method shown in the file takes a std::vector and compares it to another std::vector indice by indice to ensure that each value is within a certain tolerance of the other.

// main.cpp
#include <iostream>
#include <string>
#include <vector>
#include <array>
#include "unit_test.hpp"

int main(int argc, const char * argv[]) {
    int array_one[] = {1, 2, 3, 4};
    std::vector<int> array_two = {1, 2, 3, 4};
    std::vector<float> array_four = {0.99, 1.99, 2.99, 3.99};
    std::array<int, 4> array_three {1, 2, 3, 4};

    std::string c ("Vector Test");
    UnitTest q;
    double unc = 0.1;
    q.vectors_are_close(array_two, array_four, unc, c);
    return 0;
}

and

#ifndef unit_test_hpp
#define unit_test_hpp
#endif /* unit_test_hpp */
#include <string>
#include <typeinfo>
#include <iostream>
#include <cmath>

class UnitTest
{
public:
    template <class type1, class type2>
    void vectors_are_close(const std::vector<type1> &i, const std::vector<type2> &j,
                           double k, std::string str);
private:
    template <class type1, class type2>
    void is_close(type1 &i, type2 &j, double k);
};

template <class type1, class type2>
void UnitTest::vectors_are_close(const std::vector<type1> &i, const std::vector<type2> &j,
                                 double k, std::string str)
{
    unsigned long remain;
    remain = 50 - str.length();
    if (i.size() != j.size()) {
        std::cout << str + std::string(remain, '.') +
        std::string("FAILED") << std::endl;
    }
    else {
        try {
            for (int a = 0; a < i.size(); a++){
            is_close(i[a], j[a], k);
            }
            std::cout << str + std::string(remain, '.') +
            std::string("PASSED") << std::endl;
        } catch (const char* msg) {
            std::cout << str + std::string(remain, '.') +
            std::string("FAILED") << std::endl;
         }
    }
}

template <class type1, class type2>
void UnitTest::is_close(type1 &i, type2 &j, double k)
{
    double percent_diff = abs((j - i) / ((i + j) / 2.0));
    if (percent_diff > k) {
        throw "Number not in Tolerance";
    }
}

In this example the code compares two vectors; however, if I want to compare std::array containers I will have to crate a whole new function to do that, and if I want to compare two generic arrays, I will have to yet again create another function to do that. In addition, if I want to compare data in a std::array container to a std::vector container, again, I will have to create another function. I would like to create a single templated member function that I can pass any type of container to the function and have it compare it against any other type of container. In other words instead of;

void UnitTest::vectors_are_close(const std::vector<type1> &i, const std::vector<type2> & j);

I would like a simpler function such as;

void UnitTest::arrays_are_close(const type1, const type2);

where type1 and type2 do not just refer to the data in the container, but also the type of container as well. In this way I could pass a std::vector to type1 and std::array to type, or other combinations of the traditional way of creating arrays, array containers and vector containers. Is there any way to facilitate this behavior?

Aucun commentaire:

Enregistrer un commentaire