views:

101

answers:

2

Dear all, I've been stuck with this problem now for a few days and my searches were not successful.

What I am trying to do: I want a template reader class (VariableReader) to handle different types of variables (usually unsigned int and pointers to vector).

I started with

#ifndef READER_H_
#define READER_H_
#include <string>

namespace BAT {
template <typename variableType = unsigned int>
class VariableReader {
public:
 VariableReader<variableType>();
 VariableReader<variableType>(std::string varName);
 virtual ~VariableReader<variableType>();
 std::string getVariableName();
 void setVariableName(std::string varName);
 bool isValidVariableName(std::string varName);
 variableType getVariable();
private:
 std::string variableName;
 variableType variable;

};

}

#endif

and

#include "../../interface/Readers/VariableReader.h"

namespace BAT {

template<typename variableType>
VariableReader<variableType>::VariableReader() :
 variableName("") {
 // TODO Auto-generated constructor stub
}

template <typename variableType>
VariableReader<variableType>::VariableReader(std::string varName) :
 variableName(varName) {

}

template <typename variableType>
std::string VariableReader<variableType>::getVariableName() {
 return variableName;
}

template <typename variableType>
void VariableReader<variableType>::setVariableName(std::string varName) {
 if (VariableReader::isValidVariableName(varName)) {
  variableName = varName;
 }
}

template <typename variableType>
bool VariableReader<variableType>::isValidVariableName(std::string varName) {
 return varName != "";
}

template <typename variableType>
VariableReader<variableType>::~VariableReader() {
 // TODO Auto-generated destructor stub
}

}

However, although it seems to compile I can't use it within other projects. EDIT: forgot to post test-code:

#include "cute.h"
#include "ide_listener.h"
#include "cute_runner.h"

#include "Readers/VariableReader.h"
using namespace BAT;

static VariableReader<int> *reader;

void setUp(){
reader = new VariableReader<int>::VariableReader();//this is problem-line
}

void thisIsATest() {
    ASSERTM("start writing tests", false);  
}

void runSuite(){
    cute::suite s;
    //TODO add your test here
    s.push_back(CUTE(thisIsATest));
    cute::ide_listener lis;
    cute::makeRunner(lis)(s, "The Suite");
}

int main(){
    runSuite();
}

I get following error message:

Building target: BAT_Tests
Invoking: GCC C++ Linker
g++ -L"/workspace/BAT/Debug Gcov" -fprofile-arcs -ftest-coverage -std=c99 -o"BAT_Tests"  ./src/Test.o   -lBAT
./src/Test.o: In function `setUp()':
/workspace/BAT_Tests/Debug Gcov/../src/Test.cpp:13: undefined reference to `BAT::VariableReader<int>::VariableReader()'
collect2: ld returned 1 exit status
make: *** [BAT_Tests] Error 1

As I understand it the linker tries to find the constructor for VariableReader, which is not explicitly defined since I want to have a general constructor only.

Please help me to understand what I am missing.

+2  A: 

The constructor(s) and destructor doesn't need the template arguments in it. In addition, template classes must have the full source available to compile- you can't declare the members and define them in another translation unit like you can with normal classes.

DeadMG
This usually means you need the implementation in the header file.
DanDan
To remove the template arguments from the c'tor and d'tor worked for compilation, but not for the linking. I still have to puttemplate VariableReader<int>::VariableReader();in the .cpp file.It seems nothing is as template-like as I would like it.Basically what I wanted is a class which takes a template to create a single private variable:VariableReader<int> *intReader = new VariableReader();VariableReader<vector<float>*> *floatReader = new VariableReader();int var = intReader->getVariable();vector<float>* var2 = intReader->getVariable();
GoldenThunder
+5  A: 

The C++ FAQ Lite section on How can I avoid linker errors with my template functions? shows two solutions:

  1. Move the template class's methods into the .h file (or a file included by the .h file).
  2. Instantiate the template in the .cpp file using template VariableReader<unsigned int>;.
bk1e
Thanks for the link, it helped me to understand a bit more.I've instantiated the template now in the c++ file. However, this seems for me like a drawback from the template concept. Why should I define a template for each type?Anyway it works for now, I will try No. 1 later.Thx
GoldenThunder
@Golden: you're right, it is a drawback of templates. By making it header-only, you avoid needing to explicitly specify which types it works on, at the expence of pulling in more code everywhere that uses your class. 99% of the time, though, that"s the more useful choice.
Dennis Zickefoose