LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   C++ Template For Generic Function - Is This Possible? (https://www.linuxquestions.org/questions/programming-9/c-template-for-generic-function-is-this-possible-451028/)

taylor_venable 06-02-2006 05:44 PM

C++ Template For Generic Function - Is This Possible?
 
I'm trying to write something roughly equivalent to the get-from (>>) and put-to (<<) operators from the istream and ostream STL classes. My class, however, is really just a wrapper for an fstream; it bundles two fstream objects into one interface, providing both << and >> operators. So my problem is, I want to use operator<< to write whatever the argument is to an "input" instance variable, and operator>> to read from an "output" instance variable into the argument. In other words, I want to pass the call to the << and >> operators down to whatever stream they apply to. But it's not working like I expected, and I get linker errors saying that specific functions (for different-typed arguments) are not defined.

The code will probably illustrate it best. Here's the header file for my class, Text_IO:
Code:

#ifndef _TEXT_IO_HH
#define _TEXT_IO_HH

#include <fstream>
#include <sstream>
#include <string>

#include "taylor/string.hh"

class Text_IO {
public:
    Text_IO(std::string* in, std::string* out);
    Text_IO(const char* in, const char* out);
    std::string getline();
    template <typename DATA_TYPE> Text_IO* operator>>(DATA_TYPE& dest);
    template <typename DATA_TYPE> Text_IO* operator<<(DATA_TYPE src);
    std::string getSentence();
private:
    std::ifstream* input;
    std::ofstream* output;
    const char* SENTENCE_DELIM;
    const char* WORD_DELIM;
};

#endif

And here's the actual implementation:
Code:

#include "text_io.hh"

Text_IO :: Text_IO(std::string* in, std::string* out) : SENTENCE_DELIM(".;:?!"), WORD_DELIM(" \t\r\n") {
    this->input = new std::ifstream(in->c_str());
    this->output = new std::ofstream(out->c_str());
}

Text_IO :: Text_IO(const char* in, const char* out) : SENTENCE_DELIM(".;:?!"), WORD_DELIM(" \t\r\n") {
    this->input = new std::ifstream(in);
    this->output = new std::ofstream(out);
}

std::string Text_IO :: getline(void) {
    std::ostringstream oss;
    char c;
    while ((c = this->input->get()) != std::char_traits<char>::eof() && \
          taylor :: string :: is_member_of(c, "\n")) oss << c;
    return oss.str();
}

template <typename DATA_TYPE>
Text_IO* Text_IO :: operator>>(DATA_TYPE& dest) {
    input >> dest;
    return this;
}

template <typename DATA_TYPE>
Text_IO* Text_IO :: operator<<(DATA_TYPE data) {
    this->output << data;
    return this;
}

std::string Text_IO :: getSentence(void) {
    std::ostringstream oss;
    char c;

    while ((c = this->input->get()) != std::char_traits<char>::eof() && \
          taylor :: string :: is_member_of(c, SENTENCE_DELIM));
    while ((c = this->input->get()) != std::char_traits<char>::eof() && \
          ! taylor :: string :: is_member_of(c, SENTENCE_DELIM)) oss << c;

    return oss.str();
}

Lastly, here's my testing code:
Code:

#include <iostream>

#include "text_io.hh"

int main(int argc, char** argv) {
    Text_IO file_io("input.txt", "output.txt");
    std::string s;
    s = file_io.getline();
    std::cerr << "Read line from file: " << s << std::endl;
    int i;
    file_io >> i;
    std::cerr << "Read integer from file: " << i << std::endl;
    file_io << i;
    file_io << "w00t!\n";
    return 0;
}

And finally, here's the compilation command:
Code:

$ gmake text_io_test
g++ -g -Wall -Wabi -ansi -pedantic -o text_io_test text_io.o text_io_test.cc taylor/string.o
/var/tmp//ccQ993Ei.o(.text+0x1d4): In function `main':
/home/taylor/programs/testing/text_io_test.cc:21: undefined reference to `Text_IO* Text_IO::operator>><int>(int&)'
/var/tmp//ccQ993Ei.o(.text+0x21b):/home/taylor/programs/testing/text_io_test.cc:23: undefined reference to `Text_IO* Text_IO::operator<< <int>(int)'
/var/tmp//ccQ993Ei.o(.text+0x22f):/home/taylor/programs/testing/text_io_test.cc:24: undefined reference to `Text_IO* Text_IO::operator<< <char const*>(char const*)'
gmake: *** [text_io_test] Error 1

I thought that what I was trying to do was possible in C++, but maybe I was wrong. Do you have to define the function differently for every single type? Or can you use a template to automatically discern what type is being used in the call? Maybe it's just a syntax mistake on my part. Whatever the case, thanks in advance for any advice; this problem's had me stumped all day.

spooon 06-04-2006 02:47 AM

I am not very familiar with C++ syntax, but I thought it was "template<class DATA_TYPE>"

graemef 06-04-2006 10:11 AM

If I remember correctly the definition and declaration of the member template need to be in the same header file. This is because the compiler will then expand the required functions.

I would also suggest that you first build it without using templates and once taht is working add the template feature (you may have done that in which case sorry for butting in ;) )

graemef 06-04-2006 10:15 AM

Quote:

Originally Posted by spooon
I am not very familiar with C++ syntax, but I thought it was "template<class DATA_TYPE>"

In C++ the use of class and typename in a template are interchangable and have the same meaning. Convention is, use class if it refers to a class otherwise use typename.

taylor_venable 06-05-2006 11:33 AM

Quote:

Originally Posted by graemef
If I remember correctly the definition and declaration of the member template need to be in the same header file. This is because the compiler will then expand the required functions.

Ah, thank you! It works beautifully now. :)

But I don't quite understand why it works. Doesn't the #include at the top of the implementation ensure that the header file is present before the implementation, in the same file? Or is it that when #include is activated, template expansion takes place in the header, and then (after the expansion) the file is included into the implementation file?

graemef 06-05-2006 11:54 AM

(I believe) It's because that header files are one way. That is the source code knows about the header file but the header file doesn't know about the source code. So unless the template code is in the header file the compiler is not able to expand it when it comes across a new implementation of the template.

taylor_venable 06-05-2006 08:46 PM

Hmm, OK; that sounds sensical. Thanks a bunch for the idea, that actually solved another problem I was having, as well. It's kind of crazy how flexible and powerful C++ can be when working with this sort of stuff. :)


All times are GMT -5. The time now is 08:47 AM.