I would like to be able to use a generic string parsing function as follows:
Utils::parse::fromString<int>(whatever)
Utils::parse::fromString<float>(whatever)
Following the suggestion here, I moved template specialization to separate *.ipp files, which are #includeed from the *.hpp:
Utils.hpp
#ifndef UTILS_HPP_
#define UTILS_HPP_
#include <string>
namespace Utils
{
namespace parse
{
template<typename T>
T fromString(std::string s);
}
}
#include "Utils.ipp"
#endif // UTILS_HPP_
Utils.ipp
#ifndef UTILS_IPP_
#define UTILS_IPP_
template<>
int Utils::parse::fromString<int>(std::string s) { return 42; }
template<>
float Utils::parse::fromString<float>(std::string s) { return 42.0; }
#endif // UTILS_IPP_
Now I have a class with a specialized method:
Foo.hpp
#ifndef FOO_HPP_
#define FOO_HPP_
#include <string>
#include "Utils.hpp"
class Foo
{
public:
Foo();
template<typename T>
T get(std::string s);
};
#include "Foo.ipp"
#endif // FOO_HPP_
Foo.ipp
#ifndef FOO_IPP_
#define FOO_IPP_
#include <string>
template<typename T>
T Foo::get(std::string s)
{
return Utils::parse::fromString<T>(s);
}
#endif // FOO_HPP_
test.cpp
#include <iostream>
#include <string>
#include "Utils.hpp"
#include "Foo.hpp"
// Custom template specialization
template<>
char Utils::parse::fromString<char>(std::string s)
{
return 'c';
}
int main()
{
// Calling `fromString` directly - this works!
const std::string whatever = "whatever";
std::cout << Utils::parse::fromString<int>(whatever) << std::endl;
std::cout << Utils::parse::fromString<char>(whatever) << std::endl;
// Calling `fromString` via `Foo` - linking error!
Foo foo;
std::cout << foo.get<int>(whatever) << std::endl;
return 0;
}
If I only use fromString directly, it works fine. However, if I use Foo, i get "multiple definition" linking errors:
g++ test.cpp Foo.cpp
/tmp/cc7ACcVe.o: In function `int Utils::parse::fromString<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
Foo.cpp:(.text+0x0): multiple definition of `int Utils::parse::fromString<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/tmp/ccrN171X.o:test.cpp:(.text+0x0): first defined here
/tmp/cc7ACcVe.o: In function `float Utils::parse::fromString<float>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
Foo.cpp:(.text+0xf): multiple definition of `float Utils::parse::fromString<float>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
/tmp/ccrN171X.o:test.cpp:(.text+0xf): first defined here
collect2: error: ld returned 1 exit status
Whereas template functions doesn't require inline to avoid multiple definitions, specialization does as regular functions.
You have to add it:
template <>
inline int Utils::parse::fromString<int>(std::string s) { return 42; }
template<>
inline float Utils::parse::fromString<float>(std::string s) { return 42.0; }
Related
Through the act of separating a set of input and output related functions from other parts of a program, I have encountered a problem with compiling files when functions in a header are placed within a namespace. The following files compile:
main.cpp
#include "IO.h"
int main()
{
testFunction("yikes");
}
IO.h
#ifndef IO_H_INCLUDED
#define IO_H_INCLUDED
#include <string>
void testFunction(const std::string &text);
#endif
However, when testFunction is placed in a namespace:
#ifndef IO_H_INCLUDED
#define IO_H_INCLUDED
#include <string>
// IO.h
namespace IO
{
void testFunction(const std::string &text);
}
#endif
within IO.h, and then invoked as IO::testFunction, compilation fails, throwing
undefined reference to `IO::testFunction(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
collect2.exe: error: ld returned 1 exit status`
In each case, IO.cpp is
#include <string>
#include <iostream>
void testFunction(const std::string &text)
{
std::cout << text << std::endl;
}
and the compilation command is g++ -std=c++11 main.cpp IO.cpp, with the compiler being x86_64-w64-mingw32 from TDM-GCC, on Windows 10 Home.
If you change the declaration of your function to be in a namespace you need to implement the function within this namespace aswell.
The signature of your function will be IO::testFunction(...) but you only implemented testFunction(...) so there is no implementation for IO::testFunction(...)
The header file (IO.h):
namespace IO {
void testFunction(const std::string &text);
}
The cpp file (IO.cpp):
#include "IO.h"
// either do this
namespace IO {
void testFunction(const std::string &text) { ... }
// more functions in namespace
}
// or this
void IO::testFunction(const std::string &text) { ... }
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'm working on my first large C++ project, and I'm having huge problems getting it to build corectly.
first of all, the errors:
/tmp/ccn7hjru.o: In function `match(std::basic_istream<char, std::char_traits<char> >&, char const*)':
Geometry.cpp:(.text+0x0): multiple definition of `match(std::basic_istream<char, std::char_traits<char> >&, char const*)'
/tmp/ccfuS3Jb.o:Camera.cpp:(.text+0x0): first defined here
/tmp/ccn7hjru.o: In function `eat(std::basic_istream<char, std::char_traits<char> >&)':
Geometry.cpp:(.text+0xda): multiple definition of `eat(std::basic_istream<char, std::char_traits<char> >&)'
/tmp/ccfuS3Jb.o:Camera.cpp:(.text+0xda): first defined here
/tmp/ccIOhdcQ.o: In function `match(std::basic_istream<char, std::char_traits<char> >&, char const*)':
Light.cpp:(.text+0x0): multiple definition of `match(std::basic_istream<char, std::char_traits<char> >&, char const*)'
/tmp/ccfuS3Jb.o:Camera.cpp:(.text+0x0): first defined here
/tmp/ccIOhdcQ.o: In function `eat(std::basic_istream<char, std::char_traits<char> >&)':
Light.cpp:(.text+0xda): multiple definition of `eat(std::basic_istream<char, std::char_traits<char> >&)'
/tmp/ccfuS3Jb.o:Camera.cpp:(.text+0xda): first defined here
....
this goes on for hundreds of lines
All of my c++ course files look like this:
#include "Camera.h"
#include "util.h"
Camera::Camera() {
// TODO Auto-generated constructor stub
}
Camera::Camera(int x, int y) {
this->resX = x;
this->resY = y;
}
Camera::~Camera() {
// TODO Auto-generated destructor stub
}
...more class methods below...
and the header files all look like this:
#ifndef CAMERA_H_
#define CAMERA_H_
#include "SceneElement.h"
#include "P3D.h"
#include "Ray.h"
#define CAMERA_PRE "{CAM:"
#define CAMERA_POST ":CAM}"
#define TAG_LOCATION "LOC:"
#define TAG_PLANE "PLANE:"
#define TAG_UPPER_RIGHT "UR:"
#define TAG_UPPER_LEFT "UL:"
#define TAG_LOWER_RIGHT "LR:"
#define TAG_LOWER_LEFT "LL:"
#define TAG_RES_X "RESX:"
#define TAG_RES_Y "RESY:"
class Camera: public SceneElement {
public:
P3D location;
P3D upperLeft;
P3D upperRight;
P3D lowerLeft;
P3D lowerRight;
int resX, resY;
Camera();
Camera(int, int);
virtual ~Camera();
virtual void toStream(std::ostream &);
virtual void fromStream(std::istream &);
Ray getRay(int, int);
};
#endif /* CAMERA_H_ */
the one exception is a util file, which looks like this:
#include "util.h"
#include <iostream>
#include <stdlib.h>
#include <string.h>
void match(std::istream &str, const char* expected){
int len = strlen(expected);
char* fromStream = (char*)malloc(len+1);
str.read(fromStream, len);
fromStream[len] = 0;
if(strcmp(fromStream, expected)){
std::cout << "expected " << expected << ", got " << fromStream << "\n";
free(fromStream);
exit(1);
}
free(fromStream);
}
void eat(std::istream &str){
char c;
while(c=str.peek(), c == ' ' || c == '\n' || c == '\t'){
str.get();
}
}
with a header file that looks like this:
#ifndef UTILS
#define UTILS
#include <iostream>
void match(std::istream &str, const char* expected);
void eat(std::istream &str);
#endif
Look into SceneElement.h, P3D.h and Ray.h. Most likely, you have included util.cpp in one of these header files.
Another possibility could be a definition of match() and eat() in one of the header files.
in foo.h:
#ifndef FOO_H
#define FOO_H
enum Rat
{
A,
B
};
class Foo
{
public:
template<Rat r>
int aMemberFunc(int, int, int);
};
#endif
in foo.cpp:
#include "foo.h"
namespace {
template<Rat r>
int helper(int a, int b)
{
return a+b*((int) r);
}
}
template<Rat r>
int Foo::aMemberFunc(int a, int b, int c)
{
return a + helper<r>(b,c);
}
in main.cpp:
#include "foo.h"
#include <iostream>
using namespace std;
int main(void)
{
Foo test;
cout << test.aMemberFunc<B>(1,2,3) << endl;
}
I compile with g++ main.cpp foo.cpp and I get:
main.cpp:(.text+0x88): undefined reference to `int Foo::aMemberFunc<(Rat)1>(int, int, int)'
collect2: ld returned 1 exit status
I would prefer not to move stuff to the header because that brings along the helper and a lot of baggage, I tried to add a file fooimpl.cpp:
#include "foo.h"
#include "foo.cpp"
template int Foo::aMemberFunc<A>(int,int,int);
template int Foo::aMemberFunc<B>(int,int,int);
and then compile with g++ fooimpl.cpp main.cpp foo.cpp
This was per the suggestion of Dietmar (thanks!) but as soon as I add a function void rand(); in the header of foo.h and void rand() {} in foo.cpp the above trick yields this error:
foo.cpp:(.text+0x0): multiple definition of `Foo::rand()'
/tmp/ccoCtGMk.o:fooimpl.cpp:(.text+0x0): first defined here
how do i work around this?
You need to instantiate your function, not specialize it:
#include "foo.h"
#include "foo.cpp"
template int Foo::aMemberFunc<A>(int,int,int);
template int Foo::aMemberFunc<B>(int,int,int);
Problem fixed. Thanks a lot!
I am having the following error in the code shown below:
Error is as follows:
$ g++ main.cpp Neighbor.cpp Graph.cpp
/tmp/ccclDcUN.o: In function main':
main.cpp:(.text+0xc1): undefined reference toGraph::add(int, Neighbor&)'
main.cpp:(.text+0xd3): undefined reference to `Graph::add(int, Neighbor&)'
collect2: ld returned 1 exit status
what could be going wrong?
// FILENAME: Graph.cpp
#include "Neighbor.h"
#include "Graph.h"
template <typename NS>
void Graph<NS>::add(int id,NS& n){
if(id>=adj_list.size())
while(adj_list.size()<id+1)
adj_list.push_back(list<NS>());
adj_list[id].push_back(n);
}
template <typename NS>
void Graph<NS>::remove(int id,NS& n){
if(id<adj_list.size()){
adj_list[id].remove(n);
}
}
// FILENAME: Graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include "utils.h"
#include <vector>
#include <list>
class Neighbor;
template <typename NS>
class Graph {
private:
std::vector<std::list<NS> > adj_list;
public:
void add(int,NS&);
void remove(int,NS&);
inline typename std::vector<std::list<NS> >::iterator begin() { return adj_list.begin(); }
inline typename std::vector<std::list<NS> >::iterator end() { return adj_list.end(); }
};
#endif
// FILENAME: Neighbor.cpp
#include "Neighbor.h"
#include <iostream>
Neighbor::Neighbor(int id,float e,float p):id(id),edge_cost(e),price(p){}
bool operator==(const Neighbor& n1,const Neighbor& n2) {
if(&n1==&n2) return true;
return false;
}
ostream& operator<<(ostream& ostr,const Neighbor& n1) {
ostr<<"["<<n1.id<<","<<n1.price<<","<<n1.edge_cost<<"]";
return ostr;
}
// FILENAME: Neighbor.h
#ifndef NEIGHBOR_H
#define NEIGHBOR_H
#include <iosfwd>
class Neighbor {
private:
int id;
float edge_cost;
float price;
public:
Neighbor(int,float,float p=0.0);
friend bool operator==(const Neighbor&,const Neighbor&);
friend std::ostream& operator<<(std::ostream&,const Neighbor&);
};
#endif
// FILENAME: utils.h
#ifndef UTILS_H
#define UTILS_H
#include <iostream>
#include <fstream>
#include <stack>
#include <queue>
#include <vector>
#include <list>
#include <string>
#include <algorithm>
namespace utility {
typedef std::pair<int,int> ii;
typedef std::vector<int> vi;
typedef std::vector<ii> vii;
typedef std::vector<vii> vvii;
typedef std::stack<int> si;
typedef std::queue<int> qi;
}
#define UTILITY_TR(c,i) for(typeof((c).begin()) i = (c).begin() ; i!=(c).end() ; ++i )
#define UTILITY_ALL(c) (c).begin(),(c).end()
#define UTILITY_CPRESENT(c,x) (find(all(c),x) != (c).end())
#endif
// FILENAME: main.cpp
#include "utils.h"
#include "Neighbor.h"
#include "Graph.h"
using namespace std;
int main() {
Graph<Neighbor> graph;
Neighbor n1(1,10);
Neighbor n2(0,10);
graph.add(0,n1);
graph.add(1,n2);
cout<<"Printing graph"<<endl;
cout<<"--------------"<<endl;
UTILITY_TR(graph,it) {
UTILITY_TR(*it,n) {
cout<<*n<<endl;
}
}
};
What I usually do is manually verify the symbol exists in the library:
objdump --syms foo.o
This will output a list of symbols contained in the .o file... (since it's a link error, you should have .o files... (make sure you pass -c to g++ to get it to stop after compilation))... Then you can just visually verify the object has the symbols you think it does...
You need to have the definition of Graph's functions (add and remove) in the .h file so that the linker can find it.
I try to think of templates like envelopes. It's nonsensical to send it (compile) before you put in a letter (defined type). Seeing as cpp files are what is compiled, it makes sense that there shouldn't be cpp files for templated types.
HTH!