0

I am creating a simple MergeSort algorithm with array type(array<int,N>). The function mergeSortByMyself is written with templates. I want to test it with CppUnit, but when I run the code, it shows: Failed Output

My .h files is "mergeModel.h", which contains:

#pragma once
#include <bits/stdc++.h>
using namespace std;

template<typename Iterator>
void print(Iterator list);

template<size_t M, size_t P>
array<int,M+P> merge(array<int,M>& arr1, array<int,P>& arr2);

template<size_t N>
array<int,N> mergeSortByMyself(array<int,N>& list);

Then, I have include "mergeModel.h" in "MergeSortByMyself.cpp", this file contains:

#include "mergeSortModel.h"

template<typename Iterator>
void print(Iterator list){
    for(auto item: list ){
        cout << item << " ";
    }
    cout << endl;
}
template<size_t M, size_t P>
array<int,M+P> merge(array<int,M>& arr1, array<int,P>& arr2){
    cout << "Merge starts here......."<<endl;
    int i = 0, j = 0, k = 0;
    array<int,arr1.size()+arr2.size()> joined;
    cout << "a1: " << arr1.size() << " a2: "<< arr2.size() << " a3: "<<joined.size()<< endl;
    for(k = 0; k < joined.size(); k++){
        if(i < arr1.size() && j < arr2.size() && arr1[i] < arr2[j]){
            joined[k] = arr1[i];
            i++;
        }else if( i < arr1.size() && j < arr2.size() &&  arr1[i] > arr2[j]){
            joined[k] = arr2[j];
            j++;
        }else{
            break;
        }
    }
    
    if(i == arr1.size()){
        for(; k < joined.size();k++  ){
            // cout << "j= "<<j << " k= "<<k<< endl;
            joined[k] = arr2[j];
            j++;
        }
    }
    if(j == arr2.size()){
        for(; k < joined.size(); k++){
            joined[k] = arr1[i];
            i++;
        }
    }
    cout << "joined ";
    print(joined);  
    return joined;
}

//array<int,N>
template<size_t N>
array<int,N> mergeSortByMyself(array<int,N>& list){
    if(list.size() <= 1){
        return list;
    }
    const int firstPart = (const int)list.size()/2;
    array<int, firstPart> leftArr;
    int i;
    for(i=0; i < firstPart; i++){
        leftArr[i] = list[i];
    }
    array<int,list.size() - firstPart> rightArr;
    for(i=firstPart; i < list.size(); i++){
        rightArr[i-firstPart] = list[i];
    }
    print(list);
    print(leftArr);
    print(rightArr);

    leftArr = mergeSortByMyself(leftArr);
    rightArr = mergeSortByMyself(rightArr);

    array<int,list.size()> joined;
    if(leftArr[leftArr.size() -1] > rightArr[0]){
        joined = merge(leftArr,rightArr);
    }else{
        int y = 0;
        for(int g = 0; g < joined.size(); g++){
            if(y < leftArr.size()){
                joined[g] = leftArr[y];
            }else{
                joined[g] = rightArr[y-leftArr.size()];
            }
            y++;
        }
    }
    return joined;
}

Apparently, the code works well in the main() function:

int main(){
    array<int,3> arr = {3,2,1};
    array<int,3> sorted = mergeSortByMyself(arr);
    print(sorted);
}   

But when I try to run "mergeSortByMyself()" function using CppUnit. it shows a that error: Failed Output The file "testing.cpp" use CppUnit and "mergeModel.h":

//testing.cpp
#include "mergeSortModel.h"
#include <cppunit/TestRunner.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
// #define N 100;

template<typename Iterator>
void print(Iterator list){
    for(auto item: list){
        cout << item << " ";
    }
    cout << endl;
}
template<typename Iterator>
string toStr(Iterator list){
    string str="";
    for(auto item: list){
        str += to_string(item);
    }
    return str;
}

class Test : public CPPUNIT_NS::TestCase
{
  CPPUNIT_TEST_SUITE(Test);
  CPPUNIT_TEST(testMergeSort);
  CPPUNIT_TEST_SUITE_END();

public:
  void setUp(void) {}
  void tearDown(void) {} 

protected:
    void testMergeSort(void){
        const size_t n = 12;
        array<int,n> tosort;
        array<int,n> tosort_test;

        for(int i = 0; i < n; i++){
            tosort[i] = rand()%100;
            tosort_test[i] = tosort[i];
        }

        // copy(tosort.begin(), tosort.end(), tosort_test);
        sort(tosort.begin(),tosort.end());

        array<int,n> sorted = mergeSortByMyself<tosort_test.size()>(tosort_test);        

        CPPUNIT_ASSERT(toStr(tosort)==toStr(tosort_test));
        // print(tosort);

    }
};

CPPUNIT_TEST_SUITE_REGISTRATION(Test);

int main( int ac, char **av )
{
    srand(time(0));
 
    CPPUNIT_NS::TestResult controller;

    CPPUNIT_NS::TestResultCollector result;
    controller.addListener( &result );        

    CPPUNIT_NS::BriefTestProgressListener progress;
    controller.addListener( &progress );      

    CPPUNIT_NS::TestRunner runner;
    runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
    runner.run( controller );

    return result.wasSuccessful() ? 0 : 1;
}

Command to run the code that I used: g++ mergeSortByMyself.cpp testing.cpp -lcppunit -o test

matt
  • 29
  • 6
  • Either, all templated code should be in header file or you need to instantiate all template specializations which you will use. – sklott Jan 25 '23 at 22:40

1 Answers1

0

At the end, you were right @sklott. There are only two ways that a compiler knows about a specific template function:

  1. First: Pass all your template definitions(your code implementation) in the ".h" file.
  2. Second: Write a specific implementation in the ".cpp" file.

A useful link what helped to me is this

I think the second way is not feasible for my template function because the mergeSortByMySelf() function is constantly changing the template parameter "size_t N":

template<size_t N>
array<int,N> mergeSortByMyself(array<int,N>& list)

So I decided to do the first method, which is easier, but in isocpp.org, in the link presented before, it says:

The first solution is to physically move the definition of the template functions into the .h file, even if they are not inline functions. This solution may (or may not!) cause significant code bloat, meaning your executable size may increase dramatically (or, if your compiler is smart enough, may not; try it and see).

Nevertheless, I did it. This is "mergeSortModel.h":

#pragma once
#include <bits/stdc++.h>
using namespace std;

template<typename Iterator>
void print(Iterator list);

template<size_t M, size_t P>
array<int,M+P> merge(array<int,M>& arr1, array<int,P>& arr2);

template<size_t N>
array<int,N> mergeSortByMyself(array<int,N>& list);

//-------------------------DEFINITIONS of TEMPLATES
template<typename Iterator>
void print(Iterator list){
    for(auto item: list ){
        cout << item << " ";
    }
    // cout << "\n**********"<<endl;
    cout <<endl;
}
template<size_t M, size_t P>
array<int,M+P> merge(array<int,M>& arr1, array<int,P>& arr2){
    // cout << "Merge starts here......."<<endl;
    int i = 0, j = 0, k = 0;
    array<int,arr1.size()+arr2.size()> joined;
    // cout << "a1: " << arr1.size() << " a2: "<< arr2.size() << " a3: "<<joined.size()<< endl;

    // cout << "partA: ";
    // print(arr1);
    // cout << "\n";
    // cout << "partB: ";
    // print(arr2);
    // cout << "\n";
    
    for(k = 0; k < joined.size(); k++){
        if(i < arr1.size() && j < arr2.size() && arr1[i] < arr2[j]){
            joined[k] = arr1[i];
            i++;
        }else if( i < arr1.size() && j < arr2.size() &&  arr1[i] >= arr2[j]){
            //arr1[i] >= arr2[j] This is important, 
            //there was an error with "arr1[i] > arr2[j]"
            joined[k] = arr2[j];
            j++;
        }else{
            break;
        }
    }
    
    if(i == arr1.size()){
        for(; k < joined.size();k++  ){
            // // cout << "j= "<<j << " k= "<<k<< endl;
            joined[k] = arr2[j];
            j++;
        }
    }
    if(j == arr2.size()){
        for(; k < joined.size(); k++){
            joined[k] = arr1[i];
            i++;
        }
    }
    // cout << "joined ";
    // print(joined);  
    return joined;
}

//array<int,N>
template<size_t N>
array<int,N> mergeSortByMyself(array<int,N>& list){
    if(list.size() <= 1){
        return list;
    }
    const int firstPart = (const int)list.size()/2;
    array<int, firstPart> leftArr;
    int i;
    for(i=0; i < firstPart; i++){
        leftArr[i] = list[i];
    }
    array<int,list.size() - firstPart> rightArr;
    for(i=firstPart; i < list.size(); i++){
        rightArr[i-firstPart] = list[i];
    }
    // cout << "\nCall starts here.....................\n";
    // print(list);
    // print(leftArr);
    // print(rightArr);
    // cout << "\nCall ends here.....................\n";

    leftArr = mergeSortByMyself(leftArr);
    rightArr = mergeSortByMyself(rightArr);

    array<int,list.size()> joined;
    if(leftArr[leftArr.size() -1] > rightArr[0]){
        joined = merge(leftArr,rightArr);
    }else{
        int y = 0;
        for(int g = 0; g < joined.size(); g++){
            if(y < leftArr.size()){
                joined[g] = leftArr[y];
            }else{
                joined[g] = rightArr[y-leftArr.size()];
            }
            y++;
        }
    }
    return joined;
}

And this is the UnitTest with CppUnit:

//testing.cpp
#include "mergeSortModel.h"
#include <cppunit/TestRunner.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
// #define N 100;

// template<typename Iterator>
// void print(Iterator list){
//     for(auto item: list){
//         cout << item << " ";
//     }
//     cout << endl;
// }
template<typename Iterator>
string toStr(Iterator list){
    string str="";
    for(auto item: list){
        str += to_string(item)+" ";
    }
    cout <<"\n|"<<str<<"|\n";
    return str;
}
template<size_t M>
bool test_arrays(array<int,M> a, array<int,M> b){
    if(a.size() != b.size()) 
        return false;

    for(int i = 0; i < a.size(); ++i){
        if(!(a[i] == b[i])) 
            return false;
    }
    return true;
}
class Test : public CPPUNIT_NS::TestCase
{
  CPPUNIT_TEST_SUITE(Test);
  CPPUNIT_TEST(testMergeSort);
  CPPUNIT_TEST_SUITE_END();

public:
  void setUp(void) {}
  void tearDown(void) {} 

protected:
    void testMergeSort(void){
        const size_t n = 73;
        array<int,n> tosort;
        array<int,n> tosort_test;

        for(int i = 0; i < n; i++){
            tosort[i] = rand()%100;
            tosort_test[i] = tosort[i];
        }
        toStr(tosort);
        toStr(tosort_test);
        // copy(tosort.begin(), tosort.end(), tosort_test);
        sort(tosort.begin(),tosort.end());
        array<int,n> sorted = mergeSortByMyself<tosort_test.size()>(tosort_test);
        cout << "------------RESULTS--------------\n";
        toStr(tosort);
        toStr(sorted);
        CPPUNIT_ASSERT(test_arrays<n>(tosort,sorted));

        // print(tosort);
        // print(sorted);
    }
};


CPPUNIT_TEST_SUITE_REGISTRATION(Test);

int main( int ac, char **av )
{
    srand(time(0));
 
    CPPUNIT_NS::TestResult controller;

    CPPUNIT_NS::TestResultCollector result;
    controller.addListener( &result );        

    CPPUNIT_NS::BriefTestProgressListener progress;
    controller.addListener( &progress );      

    CPPUNIT_NS::TestRunner runner;
    runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
    runner.run( controller );

    return result.wasSuccessful() ? 0 : 1;
}

Command to run it: g++ testing.cpp -lcppunit -o test

Size of "test" is:

122792 Jan 25 22:55 test
matt
  • 29
  • 6