Reputation: 900
I have a folllowing problem:
I want to implement following structure of classes:
Parent in IParser.h
#ifndef IPARSER_H
#define IPARSER_H
#include "json.h"
class IParser
{
public:
template <typename T>
json::Object Parse(const T&, json::Object);
};
#endif // IPARSER_H
Child in HTMLParser.h
#ifndef HTMLPARSER_H
#define HTMLPARSER_H
#include <iostream>
#include "IParser.h"
class HTMLParser : public IParser
{
public:
HTMLParser();
~HTMLParser();
json::Object Parse(std::string const&, json::Object&);
};
#endif
Child in HTMLParser.cpp
#include "HTMLParser.h"
HTMLParser::HTMLParser()
{
std::cout << "constructed" << std::endl;
}
HTMLParser::~HTMLParser()
{
std::cout << "destructed" << std::endl;
}
json::Object HTMLParser::Parse(std::string const& data, json::Object& object)
{
// do something
return json::Object();
}
But when I want to build it, it throws me this error:
error LNK2019: unresolved external symbol "public: class json::Object __thiscall
IParser::Parse<class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > >(class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > const &,class json::Object)" (??$Parse@V?$basic_string@DU?
$char_traits@D@std@@V?$allocator@D@2@@std@@@IParser@@QAE?AVObject@json@@ABV?$basic_string@DU?
$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Z) referenced in function _main
Any idea what could be wrong? Basically I want to create interface class with templated function which child classes will specify and implement.
Any help would be appreciated. Thanks.
Upvotes: 1
Views: 472
Reputation: 11582
First let's see what the error is trying to tell you, not very eloquently. The error is coming from the linker
error LNK2019: unresolved external symbol
so the compiler was ok with your code, its just that it created a dependency for a symbol that the linker did not find. The symbol is
"public: class json::Object __thiscall IParser::Parse< class std::basic_string < char,struct std::char_traits < char >,class std::allocator < char > > >(class std::basic_string < char,struct std::char_traits < char >,class std::allocator < char > > const &,class json::Object)" blah blah mangled signature ... referenced in function _main
that's not very readable, lets make it more readable by making this substitution
using string = class std::basic_string < char,struct std::char_traits < char >,class std::allocator < char > >
Now the error is
"public: class json::Object __thiscall IParser::Parse< string >(class string const &, class json::Object)"
What this says is that in function _main you are making a call to the function Parse<string>
which is a member of the class Iparser
with two parameters, a const reference to a string and a json::Object by value.
But wait, you say, I did provide a definition in my derived class!
json::Object HTMLParser::Parse(std::string const& data, json::Object& object)
{
// do something
return json::Object();
}
There are three reasons why this won't work as you intended:
Parse
member function, so that if you call it using a pointer to the derived class HTMLParser
your derived class member function will be called. If you try to call the Parse
member function using a pointer to the base class IParser
then the compiler generates a call to that function (they are different!) and you haven't defined it. So what do you do in order to make the compiler call the Parse
member function of the derived class HTMLParser::Parse
when you invoke it using a pointer to the base class IParser
? In order to do that you need to understand polymorphism and virtual inheritance. Ok, you say, I'll make the Parse
member function of the IParser
base class pure virtual, and force each derived class to provide a definition. That's when you'll run into the third problem.One way to get around this problem of trying to use both generic programming (templates) and object oriented programming (inheritance) is to use a pattern called type erasure, which works in certain cases... you can read more in On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It
Upvotes: 1
Reputation: 9648
Make the whole class a template:
template <typename T>
class IParser
{
public:
json::Object Parse(const T&, json::Object);
};
Then your child classes can inherit from the templated class:
class HTMLParser : public IParser<std::string>
Note that classes that inherit from different template version will not share a common base class so you may want:
class IParserBase
{
//...
};
template <typename T>
class IParser : public IParserBase
{
public:
json::Object Parse(const T&, json::Object);
};
Upvotes: 0