This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 7 years ago.
I have a templated class named DataHandler
#ifndef DATAHANDLER_H
#define DATAHANDLER_H
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <set>
#include "constants.h"
template <typename T>
using Car = std::pair< T, T>;
template <typename T>
using SparseMatrix = std::vector< Car<T> >;
template <class T>
class DataHandler
{
public:
// initializes a new DataHandler only if none has been created,
// otherwise return the living instance
static DataHandler<T>* getInstance()
{
if(!dataHandler)
dataHandler = new DataHandler();
return dataHandler;
}
void readFile();
SparseMatrix<T>* getSparseBlue(){ return &sparseBlue; }
SparseMatrix<T>* getSparseRed(){ return &sparseRed; }
virtual ~DataHandler();
private:
// static DataHandler to ensure only one instance can be created
static DataHandler<T> *dataHandler;
// private constructor to use DataHandler as a Singleton
DataHandler();
int numElem = 0;
int m_rows, m_cols = -1;
#endif // DATAHANDLER_H
The source file is:
#include "data_handler.h"
#include <fstream>
#include <algorithm>
#include <omp.h>
#include <chrono>
using namespace std;
using namespace constants;
// Global static pointer used to ensure a single instance of the class.
template<typename T>
DataHandler<T>* DataHandler<T>::dataHandler = NULL;
template<typename T>
DataHandler<T>::DataHandler()
{
//ctor
}
template<typename T>
DataHandler<T>::~DataHandler()
{
//dtor
}
template<typename T>
void DataHandler<T>::readFile()
{
// do some stuff
}
// Instantiation of relevant templates
template class DataHandler<unsigned char>;
template class DataHandler<unsigned short int>;
In the last two lines I instantiate the templates which I define in main.cpp:
#include <iostream>
#include <chrono>
#include <fstream>
#include <algorithm>
#include "data_handler.h"
#include "dense_traffic_handler.h"
#include "sparse_traffic_handler.h"
#include "constants.h"
using namespace std;
// Check the number of rows/cols to choose between char or short int for the sparse case
bool matrixIsSmall()
{
return true;
}
void integerCase()
{
typedef unsigned char T;
DataHandler<T> *dh = DataHandler<T>::getInstance();
dh->readFile();
DenseTrafficHandler dth(dh); // ****** ERROR HERE *****
}
void charCase()
{
typedef unsigned char T;
DataHandler<T> *dh = DataHandler<T>::getInstance();
dh->readFile();
DenseTrafficHandler dth(dh); // ****** ERROR HERE *****
SparseTrafficHandler<T> sth;
set<unsigned short int> step = dh->getstep();
int currentStep = 0;
set<unsigned short int>::const_iterator stepToSave = step.begin();
}
int main(int argc, char *argv[])
{
if(matrixIsSmall())
charCase();
else
integerCase();
return 0;
}
Compiler gives me an error: undefined reference to DenseTrafficHandler::DenseTrafficHandler<unsigned short>(DataHandler<unsigned short>*)
DenseTrafficHandler header is like that:
#ifndef TRAFFICHANDLER_H
#define TRAFFICHANDLER_H
#include "constants.h"
#include "data_handler.h"
class DenseTrafficHandler
{
public:
template<typename T>
DenseTrafficHandler(DataHandler<T> *dh);
virtual ~DenseTrafficHandler();
private:
int m_cols, m_rows;
char* data;
char ** dense = NULL;
};
#endif // TRAFFICHANDLER_H
DenseTrafficHandler source is:
#include "dense_traffic_handler.h"
using namespace std;
using namespace constants;
template <typename T>
DenseTrafficHandler::DenseTrafficHandler(DataHandler<T> *datah)
{
DataHandler<T> *dh = datah;
dense = dh->getDense();
m_rows = dh->getm_rows();
m_cols = dh->getm_cols();
}
DenseTrafficHandler::~DenseTrafficHandler()
{
//dtor
}
So I have two questions:
Why do I receive this error and how can I manage it?
Is there a way in DataHandler source to not specify
template <typename T>
DataHandler<T>::functionName() for every function? (I mean something like using namespace Datahandler<T>)
You receive this error because compiler did not generate the code for this template type. One of solutions is to tell the compiler to do this explicitly by template instantiation:
add to your DenseTrafficHandler.cpp:
template class DenseTrafficHandler<unsigned short>;
Yes, just implement it in the header file. Reading more about it here.
Related
I am attempting to create a templated vector class, but upon compilation I am receiving an error of
def.hpp:3:1: error: 'TempVector' does not name a type
I keep referring to reference material and my syntax and handling of the header file declaration and definition (.h and .hpp) seem right to me, but I can not figure out what I am overlooking.
Below is the three files I am working with, thank you.
driver.cpp:
#include <iostream>
#include <string>
#include "dec.h"
using namespace std;
int main() {
TempVector <int> v1;
cout<<"ran successfully"<<endl;
}
dec.h:
#ifndef DEC_H
#define DEC_H
#include <iostream>
#include <utility>
// Declaration of class Vector
template <typename T>
class TempVector {
public:
TempVector ();
private:
T* array;
static const unsigned int spare = 10;
};
#include "def.hpp"
#endif
def.hpp:
template <typename T>
TempVector<T>::TempVector () {
std::cout<<"ran successfully";
}
I have three class. One is abstract, second is based on the abstract one and its storing pointers in std::vector to instances of another.
I want to create std::shared_ptr of ClientRepository to pass it to the Manager class instance in the future.
There is a template class called "Repository". I want to use it to create a few types of Repositories, for example: CarsRepository, ItemsRepository, etc.
Unfortunately I am getting an error while compiling:
main.cpp:84:139: error: template argument 1 is invalid
std::shared_ptr, std::vector> > p = std::make_shared;
^
Repository.hpp
#ifndef REPOSITORY_HPP
#define REPOSITORY_HPP
#include <string>
template<typename typeBOOL, typename typeShared_ptr, typename VectorOfSmarPtrs > class Repository
{
protected:
VectorOfSmarPtrs nameOfVector;
public:
virtual typeBOOL create(const typeShared_ptr&) = 0;
};
#endif
ClientRepository.hpp
#ifndef CLIENTREPOSITORY_HPP
#define CLIENTREPOSITORY_HPP
#include <memory>
#include <string>
#include "Client.hpp"
#include "Repository.hpp"
class ClientRepository : public Repository<bool, std::shared_ptr<Client>, std::vector<std::shared_ptr<Client> > >{
public:
bool create(const std::shared_ptr<Client> & newClient) override;
};
#endif
ClientRepository.cpp
include "ClientRepository.hpp"
bool ClientRepository::create(const std::shared_ptr<Client> & newClient) {
if(newClient != NULL){
for(int i = 0; i < this->nameOfVector.size(); i++) {
if(this->nameOfVector.at(i)->GetPersonalID() == newClient->GetPersonalID()) {
return 0;
}
}
this->nameOfVector.push_back(newClient);
return 1;
}
else return 0;
}
main.cpp
#include <iostream>
#include <memory>
#include "Client.hpp"
#include "ClientRepository.hpp"
#include "Repository.hpp"
int main(){
ClientRepository x;
std::shared_ptr<Repository< bool, std::shared_ptr<Client>, std::vector<std::shared_ptr<Client>> > p = std::make_shared<ClientRepository>;
}
What is wrong with this code? What should I change?
You miss parenthesis:
std::shared_ptr<Repository<bool,
std::shared_ptr<Client>,
std::vector<std::shared_ptr<Client>>>> p
// ^
= std::make_shared<ClientRepository>();
// ^^
I'm trying to implement a generic resource manager which would ensure that every resource gets only loaded once with C++11.
My first attempt:
resourcemanager.h
#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H
#include <map>
#include <memory>
template<typename T>
class ResourceManager {
public:
static std::shared_ptr<T> load(std::string filePath);
private:
static map<std::string, std::weak_ptr<T>> resources;
virtual static std::shared_ptr<T> loadResource(std::string filePath) = 0;
};
#endif // RESOURCEMANAGER_H
#include "resourcemanager.h"
resourcemanager.cpp
using namespace std;
template<typename T>
map<string, weak_ptr<T>> ResourceManager<T>::resources;
template<typename T>
shared_ptr<T> ResourceManager<T>::load(std::string filePath) {
auto search = resources.find(filePath);
if (search != resources.end()) {
auto ptr = search->second.lock();
if (ptr) {
return ptr;
}
}
auto ptr = loadResource(filePath);
resources[filePath] = ptr;
return ptr;
}
However since abstract static methods are apparently forbidden black magic I tried to use CRTP:
resourcemanager.h
#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H
#include <map>
#include <memory>
template<typename T, class Derived>
class ResourceManager {
public:
static std::shared_ptr<T> load(std::string filePath);
private:
static std::map<std::string, std::weak_ptr<T>> resources;
static std::shared_ptr<T> loadResource(std::string filePath);
};
#endif // RESOURCEMANAGER_H
resourcemanager.cpp
#include "resourcemanager.h"
using namespace std;
template<typename T, class Derived>
map<string, weak_ptr<T>> ResourceManager<T, Derived>::resources;
template<typename T, class Derived>
shared_ptr<T> ResourceManager<T, Derived>::load(string filePath) {
auto search = resources.find(filePath);
if (search != resources.end()) {
auto ptr = search->second.lock();
if (ptr) {
return ptr;
}
}
auto ptr = ResourceManager::loadResource(filePath);
resources[filePath] = ptr;
return ptr;
}
template<typename T, class Derived>
shared_ptr<T> ResourceManager<T, Derived>::loadResource(string filePath) {
return Derived::loadResource(filePath);
}
This looks like it should do what I want. However when I try to use it, it fails at the linking stage:
managedstring.h
#ifndef MANAGEDSTRING_H
#define MANAGEDSTRING_H
#include "resourcemanager.h"
class ManagedString {
public:
ManagedString(std::string filePath);
std::string get();
private:
std::shared_ptr<std::string> ptr;
class StringManager : public ResourceManager<std::string, StringManager> {
private:
static std::shared_ptr<std::string> loadResource(std::string filePath);
};
};
#endif // MANAGEDSTRING_H
managedstring.cpp
#include "managedstring.h"
using namespace std;
ManagedString::ManagedString(string filePath) {
ptr = StringManager::load(filePath);
}
string ManagedString::get() {
return *ptr;
}
shared_ptr<string> ManagedString::StringManager::loadResource(string filePath) {
// dummy implementation
return make_shared<string>("foo");
}
main.cpp
#include <iostream>
#include "managedstring.h"
using namespace std;
int main() {
ManagedString string1 = ManagedString("bar");
ManagedString string2 = ManagedString("foobar");
cout << string1.get() << endl;
cout << string2.get() << endl;
}
When I try to compile this with g++ -std=c++11 -o bin -Wall main.cpp managedstring.cpp resourcemanager.cpp (using gcc version 5.3.0) I get this error message:
/tmp/ccgqljOQ.o: In function `ManagedString::ManagedString(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
managedstring.cpp:(.text+0xdd): undefined reference to `ResourceManager<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >,
ManagedString::StringManager>::load(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
Should this work? Is this a compiler shortcoming? Or am I trying to do something I shouldn't do.
I also thought about altering my design, however I think it's not that bad. Feel free to disagree with me on this.
In resourcemanager.h, this line:
#include "resourcemanager.h"
Should be:
#include "resourcemanager.cpp"
This seems valid only for your first example, but the same applies to all the others too.
Otherwise, as an alternative, put both declarations and definitions of template classes in the same file.
I can not link template functions within non template class
I have:
class base {};
class mgr : base
{
template function a;
........
non template functions
};
template a access variables of class m
for example
static m<T>* ptr;
ptr = reinterpret_cast<m<T>*>( std::get<0>(d) );
ptr->m<T>::clear();
(m private section is friend of mgr)
in class g I want to use function a
static mgr mg;
mgr *p = &mg;
m<T> v = *( reinterpret_cast<T*>( p->a(p1,p2) );
note that func. a() return pointer to base (similar idea to void*) regardless of <T>
at the end of m.cpp I have (per specialization)
example:
template class m<string>;
at the end of mgr.cpp I have (per specialization)
example:
template pbase mgr::a<string>( ... );
hpp files used for declarations,
cpp files for definition (implementation)
watching the link map I can see that mgr linked template specialized
function a
however it failed to link it in g
any help would be appreciated
here is more code
mgr.hpp
#ifndef __mgr__mgr___
#define __mgr__mgr___
#include <iostream> // cout
#include <ostream> // cout
#include <string>
#include <vector>
#include <map>
#include <tuple>
#include <type_traits>
#include <limits>
#include <climits>
#include <exception>
using namespace std;
typedef string tInstName;
enum tInstType { Typ1 , Typ2 , Typ3 };
class mgr
{
public:
class MIObase {};
map<string,tuple<MIObase*,string,tInstType>> Imap;
template <class T> MIObase* getInstance ( const string& name , const iName& instname );
};
#endif
mgr.cpp
#include <ostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <utility>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <limits>
#include <climits>
#include <exception>
#include "mgr.hpp"
#include "mfunc.hpp"
using namespace std;
template<>
pMIObase mgr::getInstance<string> ( const string& filename , const string& name )
{
tuple<pMIObase,string,tInstType> d;
static mfunc<string>* ptr;
static pMIObase ptr_;
auto it = Imap.find ( instname );
if ( it != Imap.end() )
{
d=Imap[instname];
ptr = reinterpret_cast<mfunc<string>*>( std::get<0>(d) );
ptr->mfunc<string>::clear();
ptr_ = std::get<0>(d);
cout<<instname <<" already exists .......\n";
return ptr_;
}
else
{
//mfunc<string> I;
ptr = new mfunc<string>;
d = make_tuple (reinterpret_cast<pMIObase>(ptr),name,Tstr);
Imap[instname] = d;
ptr->clear();
ptr_ = reinterpret_cast<pMIObase>(ptr);
return ptr_;
}
}
template pMIObase mgr::getInstance<string>(const string& filename , const tInstName& instname);
g.cpp
#include "mgr.hpp"
using namespace std;
void g()
{
static mgr mb;
mgr *pMIO=&mb;
// this one dose not link
static mfunc<string> &v = *( reinterpret_cast<mfunc<string>*>( pMIO->mgr::getInstance<string> ( ) ));
}
I am trying to create a class linkedList using template but when I compile it the IDE gives an error :
undefined reference to `listType::add(int)
I am not understanding why ?
linkedList.h
#ifndef LINKEDLISTS_H_INCLUDED
#define LINKEDLISTS_H_INCLUDED
#include "struct.h"
template <class type1>
class listType
{
public:
void add(type1);
void print();
private:
node<type1> *head;
};
#endif // LINKEDLISTS_H_INCLUDED
LinkedList.cpp
#include "linkedLists.h"
#include "struct.h"
#include <iostream>
using namespace std;
template <class type1>
void listType<type1>::add(type1 temp)
{
node<type1> *t;
t->value=temp;
t->link=head;
head=t;
}
template <class type1>
void listType<type1>::print()
{
node<type1> *p;
p=head;
while(p!=NULL)
{
cout<<p->value<<endl;
p=p->link;
}
}
Struct.h
#ifndef STRUCT_H_INCLUDED
#define STRUCT_H_INCLUDED
template <class type1>
struct node
{
type1 value;
node *link;
};
#endif // STRUCT_H_INCLUDED
main.cpp
#include <iostream>
#include "linkedLists.h"
using namespace std;
int main()
{
listType <int> test;
test.add(5);
}
You can't have the implementation of templated classes and functions in the cpp file.
The code has to be in the header, so the including files can see the implementation, and instantiate the correct version with their template argument type.