Reputation: 167
I seem to be getting a linker error. It seems to be multiple definitions of functions, though I am using header guards it still pops up. I have no idea why as I have written code like this before with no errors whatsoever.
Commands I use:
clang++ -std=c++11 main.cpp -o main.o -Wall -pedantic -c
clang++ -std=c++11 main.o TextQuery.cpp -o TextQuery -Wall -pedantic
Error:
/tmp/TextQuery-94b8fe.o: In function `QueryResult::QueryResult(std::string, TextQuery)':
TextQuery.cpp:(.text+0x6b0): multiple definition of `QueryResult::QueryResult(std::string, TextQuery)'
/tmp/main-be50cb.o:main.cpp:(.text+0x6b0): first defined here
/tmp/TextQuery-94b8fe.o: In function `QueryResult::QueryResult(std::string, TextQuery)':
TextQuery.cpp:(.text+0x6b0): multiple definition of `QueryResult::QueryResult(std::string, TextQuery)'
/tmp/main-be50cb.o:main.cpp:(.text+0x6b0): first defined here
/tmp/TextQuery-94b8fe.o: In function `TextQuery::query(std::string const&)':
TextQuery.cpp:(.text+0x430): multiple definition of `TextQuery::query(std::string const&)'
/tmp/main-be50cb.o:main.cpp:(.text+0x430): first defined here
/tmp/TextQuery-94b8fe.o: In function `TextQuery::TextQuery(std::basic_ifstream<char, std::char_traits<char> >&)':
TextQuery.cpp:(.text+0x0): multiple definition of `TextQuery::TextQuery(std::basic_ifstream<char, std::char_traits<char> >&)'
/tmp/main-be50cb.o:main.cpp:(.text+0x0): first defined here
/tmp/TextQuery-94b8fe.o: In function `TextQuery::TextQuery(std::basic_ifstream<char, std::char_traits<char> >&)':
TextQuery.cpp:(.text+0x0): multiple definition of `TextQuery::TextQuery(std::basic_ifstream<char, std::char_traits<char> >&)'
/tmp/main-be50cb.o:main.cpp:(.text+0x0): first defined here
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I'm probably making a really stupid mistake, could someone show me how they would separate declaration from definition. And how they would use header files. Thanks!
TextQuery.h
#ifndef TEXT_QUERY_H
#define TEXT_QUERY_H
#include <ostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <exception>
#include <algorithm>
class TextQuery;
// QueryResult class
class QueryResult
{
friend std::ostream &print(std::ostream &, QueryResult);
public:
QueryResult() = default;
QueryResult(const std::string, TextQuery);
private:
std::string word;
std::shared_ptr<std::vector<std::string>> lines;
std::shared_ptr<std::map<std::string, std::multiset<unsigned>>> lineNum;
};
/// TextQuery class
class TextQuery
{
friend class QueryResult;
public:
TextQuery() = default;
TextQuery(std::ifstream &);
QueryResult query(const std::string &);
private:
std::shared_ptr<std::vector<std::string>> lines = std::make_shared<std::vector<std::string>>();
std::shared_ptr<std::map<std::string, std::multiset<unsigned>>> lineNum
= std::make_shared<std::map<std::string, std::multiset<unsigned>>>();
};
// ifstream contructor
TextQuery::TextQuery(std::ifstream &iFile)
{
lines = std::make_shared<std::vector<std::string>>();
lineNum = std::make_shared<std::map<std::string, std::multiset<unsigned>>>();
if (iFile)
{
std::string l, w;
for (unsigned lineCnt = 1; getline(iFile, l); ++lineCnt)
{
lines->push_back(l);
std::istringstream wStream(l);
while (wStream >> w)
{
(*lineNum)[w].insert(lineCnt);
}
}
} else
throw std::runtime_error("Unable to open input file!");
}
// query member function
QueryResult TextQuery::query(const std::string &w)
{
return ((*lineNum).find(w) != (*lineNum).end()) ? QueryResult(w, *this) : QueryResult(w, TextQuery());
}
// TextQuery contructor for QueryResult
QueryResult::QueryResult(const std::string w, TextQuery tq) : word(w), lines(tq.lines), lineNum(tq.lineNum) { }
std::ostream &print(std::ostream &, QueryResult);
#endif
TextQuery.cpp
#include "TextQuery.h"
std::ostream &print(std::ostream &os, QueryResult qr)
{
os << qr.word << " occured " << (*qr.lineNum)[qr.word].size() << ((*qr.lineNum)[qr.word].size() == 1 ? " time" : " times");
unsigned n = 0;
for (const auto u : (*qr.lineNum)[qr.word])
{
if (u != n)
os << "\n(line " << u << ") " << (*qr.lines)[u-1];
n = u;
}
return os;
}
main.cpp
#include <iostream>
#include <fstream>
#include "TextQuery.h"
void runQueries(std::ifstream &infile)
{
TextQuery tq(infile);
while (true)
{
std::cout << "Enter word to look for, or q to quit: ";
std::string s;
if (!(std::cin >> s) || s == "q")
break;
std::cout << std::endl;
print(std::cout, tq.query(s)) << "\n" << std::endl;
}
}
int main()
{
std::ifstream infile("input.txt");
runQueries(infile);
return 0;
}
Upvotes: 0
Views: 449
Reputation: 37132
The functions you've defined in your header file (e.g. TextQuery::TextQuery(std::ifstream &iFile)
) are being compiled into every translation unit that includes that header - every .cpp file that includes that header is generating a copy of the function, and at link time these are clashing.
If you want to include function definitions in headers they need to be inside the actual class definition (e.g. in this instance, inside class TextQuery {...}
).
For example,
class TextQuery
{
friend class QueryResult;
public:
TextQuery() = default;
TextQuery(std::ifstream &)
{
// .. function body here
}
};
Alternatively, you could explicitly mark the function as inline
, or simply move the definition of the function to a .cpp file (e.g. TextQuery.cpp
).
Upvotes: 4