Jon
Jon

Reputation: 1801

C++ templates containing std::vector

Very recently I wrote a class containing four functions to open and read multicolumn data files (up to 4 columns). In the function the name of the file to be opened "file_name" is passed from the main program to the function in class "Read_Columnar_File". The data is read in using std::vector and passed back to the main program. However, it required that the programmer change the data types of the input columns every time it is used which is a recipe for errors. The file name will always be a character string, so that does not need to be templated; however, the data type of the arrays read in using vector can change, so that needs to be generically templated. I am trying to convert the class to a template class and am missing some fundamental understanding of the process with respect to making templates containing std::vector. To simplify the development process I have gone back to a single routine titled "Read_One_Column" inside the class and am trying to convert it a template where the data type is labeled as Type1. I think my problem is in the syntax since the debugger is telling me that the command in the main program is undefined. Any advice to help correct this would be appreciated. A copy of the existing code is attached below.

#include <vector>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <iterator>

template <class Type1> class Read_Columnar_File {
public:
    void Read_One_Column(const std::string& file_name,std::vector<Type1>& Column1);
};

template <class Type1> void Read_Columnar_File<Type1>::Read_One_Column(const std::string& file_name,
                                                                       std::vector<Type1>& Column1)
{
    std::ifstream inp(file_name,std::ios::in | std::ios::binary);
    std::istream_iterator<Type1> start((inp)), end;
    if(inp.is_open()) {
    Column1.assign(start,end);
}
    else std::cout << "Cannot Open " << file_name << std::endl;
    inp.close();
}

int main(int argc, const char * argv[]) {
    int i;
    std::vector<float> str1;
    Read_Columnar_File<float> ob1;

    char str[20];
    std::strcpy(str,"Test.txt");

    ob1.Read_One_Column(str,str1);

    for(i=0; i < 7; i++) std::cout << str1[i] << std::endl;

    return 0;
}

Upvotes: 0

Views: 1641

Answers (2)

Jon
Jon

Reputation: 1801

To fully close out this question I am posting the final and correct code since I am sure others will have the same question in the future and i hope this helps them. To answer my question, when programming a template, the entire algorithm needs to be included in the header and cannot be split between a header and implementation file. This program is meant to be a very generic method of reading in columnar data from an input file and assumes that each column of data is the same length as the others. The user can merely like the header file to their main program, specify the data type of each column in the vector definition and read in the data. The main program is shown below. This version allows the user to call 4 different functions which can be used to read in up to as much as four columns of data.

    #include <vector>
    #include <iostream>
    #include <cstring>
    #include "Read_Columnar_File.h"

    int main(int argc, const char * argv[]) {
        char str[20];
        strcpy(str,"Test.txt");

        // - Format for reading in a single column of data
        //   Data in this case is declared as a float in
        //   the vector, but it can be any data type
        /*
        std::vector<float> str2;
        Read_One_Column(str,str2);
        */

        // - Format for reading in two columns of data from
        //   an input file
        /*
        std::vector<float> str2;
        std::vector<int> str3;
        Read_Two_Columns(str,str2,str3);
         */

        // - Format for reading in three columns of data from
        //   an input file
        /*
        std::vector<float> str2;
        std::vector<int> str3;
        std::vector<int> str4;
        Read_Three_Columns(str,str2,str3,str4);
         */

        std::vector<float> str2;
        std::vector<int> str3;
        std::vector<int> str4;
        std::vector<float> str5;
        Read_Four_Columns(str,str2,str3,str4,str5);

        return 0;
    }

The implementation file is shown below.

    #include <vector>
    #include <stdio.h>
    #include <fstream>
    #include <iterator>

    template <class X> void Read_One_Column(const std::string& file_name,std::vector<X>& Column1)
    {
        std::ifstream inp(file_name,std::ios::in | std::ios::binary);
        std::istream_iterator<X> start((inp)), end;
        if(inp.is_open()) {
            Column1.assign(start,end);
        }
        else std::cout << "Cannot Open " << file_name << std::endl;
        inp.close();
    }

    template <class X,class Y> void Read_Two_Columns(const std::string& file_name,std::vector<X>& Column1,
                                            std::vector<Y>& Column2)
    {
        int i;
        X Col1;
        Y Col2;
        std::ifstream inp(file_name,std::ios::in | std::ios::binary);
        if(inp.is_open()){
        for(i=0; i < 7; i++){
                inp >> Col1 >> Col2;
                Column1.push_back(Col1), Column2.push_back(Col2);
            }
        }
        else std::cout << "Cannot Open " << file_name << std::endl;
        inp.close();
     }

    template <class X,class Y, class Z> void Read_Three_Columns(const std::string& file_name,std::vector<X>& Column1,
                                                                std::vector<Y>&     Column2,std::vector<Z>& Column3
    {
        int i;
        X Col1;
        Y Col2;
        Z Col3;
        std::ifstream inp(file_name,std::ios::in | std::ios::binary);
    if(inp.is_open()){
        for(i=0; i < 7; i++){
            inp >> Col1 >> Col2 >> Col3;
            Column1.push_back(Col1), Column2.push_back(Col2), Column3.push_back(Col3);
        }
    }
    else std::cout << "Cannot Open " << file_name << std::endl;
    inp.close();
}

template <class X,class Y, class Z,class A> void Read_Four_Columns(const std::string& file_name,std::vector<X>& Column1,
                                                            std::vector<Y>& Column2,std::vector<Z>& Column3,
                                                            std::vector<A>& Column4)
{
    int i;
    X Col1;
    Y Col2;
    Z Col3;
    A Col4;
    std::ifstream inp(file_name,std::ios::in | std::ios::binary);
    if(inp.is_open()){
        for(i=0; i < 7; i++){
            inp >> Col1 >> Col2 >> Col3 >> Col4;
            Column1.push_back(Col1), Column2.push_back(Col2),
            Column3.push_back(Col3), Column4.push_back(Col4);
        }
    }
    else std::cout << "Cannot Open " << file_name << std::endl;
    inp.close();
}

Upvotes: 0

6502
6502

Reputation: 114579

The syntax is simpler:

template <typename Type1>
void Read_One_Column(const std::string& file_name,
                     std::vector<Type1>& Column1) {
    ...
}

no need to create a class at all (it's just a template function).

If you need to put the function in a class for other reasons then the syntax is the same

struct Read_Columnar_File {
    ...
    template<typename Type1>
    void Read_One_Column(const std::string& file_name,
                         std::vector<Type1>& Column1) {
        ...
    }
}

and it will be a template method of the class.

Upvotes: 1

Related Questions