I am using static template members in a class; the templates are instantiated in a cpp built in a software. I have a plug-in for the software that uses the name() template method in the header, but does not build the source file which contains the instantiation. The build works on Linux with g++-4.9 but fails on MinGW 4.8. I want to know how to make it work with the almost same compiler, but on Windows instead.
.hpp :
enum class ToplevelMenuElement
{
FileMenu,
...
AboutMenu
};
enum class FileMenuElement
{
New,
... ,
Quit
};
// Others menus macros are defined
class MenuInterface
{
public:
template<typename MenuType>
static QString name(MenuType elt);
private:
static const std::map<ToplevelMenuElement, QString> m_map;
static const std::map<FileMenuElement, QString> m_fileMap;
};
.cpp :
template<>
QString MenuInterface::name(ToplevelMenuElement elt)
{
return m_map.at(elt);
}
template<>
QString MenuInterface::name(FileMenuElement elt)
{
return m_fileMap.at(elt);
}
const std::map<ToplevelMenuElement, QString> MenuInterface::m_map
{
{ToplevelMenuElement::FileMenu, QObject::tr("File")},
...
{ToplevelMenuElement::AboutMenu, QObject::tr("About")}
};
const std::map<FileMenuElement, QString> MenuInterface::m_fileMap
{
{FileMenuElement::New, QObject::tr("New")},
...,
{FileMenuElement::Quit, QObject::tr("Quit")}
};
Error :
undefined reference to `QString MenuInterface::name<ToplevelMenuElement>(ToplevelMenuElement)'
Is there any flag to use to make some kind of lazy instantiation ? Or should I build the .cpp containing the template instantiation in my plug-in ?
Because you are linking a source file that contains explicit specializations, you need to declare your explicit specializations before you define them. From §14.7.3/3:
A declaration of a function template, class template, or variable template being explicitly specialized shall precede the declaration of the explicit specialization. [ Note: A declaration, but not a definition of the template is required. — end note ]
So you need to put these after your class in your header file:
template<>
QString MenuInterface::name(ToplevelMenuElement elt);
template<>
QString MenuInterface::name(FileMenuElement elt);
This has been asked many times...declaration and definitions for templates should be same file.
Related
Code
I have the following snippets of 2 classes with a separate source and header file. The derived class is a template class.
handler.h
class BaseHandler {
public:
BaseHandler(){}
BaseHandler(const std::string& directive);
virtual ~BaseHandler();
virtual bool operator()();
private:
const std::string m_directive;
};
template<typename C>
class DirectiveHandler : public BaseHandler {
public:
DirectiveHandler(const std::string& directive);
~DirectiveHandler();
bool operator()() override;
private:
std::vector<C> m_configurations;
};
handler.cpp
#include "handler.h"
BaseHandler::BaseHandler(const std::string& directive) : m_directive(directive) {};
BaseHandler::~BaseHandler(){};
template<typename C>
DirectiveHandler<C>::DirectiveHandler(const std::string& directive) :
BaseHandler(directive) {};
template<typename C>
bool DirectiveHandler<C>::operator()(){ return true; };
main.cpp
#include "handler.h"
template class DirectiveHandler<double>; //explicit template instantiation
int main(int argc, char *argv[]){
....
To my understanding I need to instantiate the template after it has been defined, this can happen either implicitly (leave out template class DirectiveHandler<double>;) or explicitly.
I'm assuming that implicit instantiation fails due to the separation of definition and declaration in the respective source and header file.
With the snippet of main.cpp above I have the following warnings:
warning: explicit template instantiation DirectiveHandler<double> will emit a vtable in every translation unit
warning: instantiation of function DirectiveHandler<double>::operator() required here, but no definition available
If change template class DirectiveHandler<double> to extern template class DirectiveHandler<double>;both warnings dissapear. I do understand why warning 2 is cleared, because the template class resides in handler.cpp. I fail to see how it clears warning 1 as well.
Question
Why does the addition of the extern keyword clear warning 1 (see above)?
I assume, you are compiling your code with CLang? (To the best of my knowledge, it is CLang warning, unless g++ started to emit it as well).
On any rate, the answer to the question as asked (I only have to assume that OP understands everything else) is the simple fact that template class DirectiveHandler<double>; - which is implicit template instantiation definition, which produces vtbls and such. (The fact that the warning is emitted for .cpp file might be actually a bug, but OP doesn't ask about it).
On the other hand, extern template class DirectiveHandler<double>; is not a definition. It is a declaration, which doesn't on itself trigger vptr's generation - and thus you see no warning.
I have a set of templates where each base template has some large specializations and many of the other methods are mainly casts around types and constructors and operator=(). I made templates to factor out all those little methods as inlines and put the fat ones in the CPP files for a shared library. In windows it compiles and links fine into a DLL. It all does what I want on MSC but on GCC with the specialization code in the C++ file many of the specializations occur "after instantiation" with GCC.
Structurally the code is like the below. (the real case is more messy) I'm not sure what I need to do as the template method declarations that call their superclass methods (from other templates) are where the method code is "instantiated" for their superclass templates. What I need to do is somehow defer the instantiation until after the header declarations to happen in the .cpp file where the specialization's method bodies for the library are compiled.
// header file .h
template<class SubClassT, class CharT>
class XBaseT
: public YBaseT<char_object<CharT>>
{
typedef XBaseT<XObject,CharT> super;
public:
typedef char_object<CharT> ObjT; // seen by subclasses
inline XBaseT() {}
inline XBaseT(ObjT &p); // note no body, then intention is to have this specialization in library, not header.
...
};
template<>
class XClass<XObject>
: public XBaseT<XObject,char>
{
typedef XBaseT<XObject,char> super;
public:
inline XClass() {}
inline XClass(ObjT &p) : super(p) {}
...
};
template<>
class XObject
: public ContainerT<XObject>
{
typedef ContainerT<XObject> super;
public:
inline XObject() {}
static ContainerT<XObject> *static create(InitStruct &is);
...
};
// specializations in cpp file
tenplate<>
ContainerT<XObject> *
create(InitStruct &is) {
// big code here needs to be in CPP file for library
}
// specialization for <XObject,char> happens "after instantiation" even thoght the method in it's template class is declared with no body.
template<>
XBaseT<XObject, char>::XBaseT(ObjT *p) {
p_ = XObject::create(p);
}
The specialization just above in the.cpp file barfs on GCC as it is "after instantiation",
The instantiation in GCC happens when the "inline XClass(ObjT *p) : super(p) {}" constructor in XClass is declared. And that is so tiny I would like it to be inline as it should optimize out to nothing. On MSC the code is not instantiated until explicitly done so in the .cpp file. and the with MSC the linker seems to sort out what is needed and the compiler warns of missing methods declared but NOT instantiated there. In a declaration of
template class XBaseT;
template class ContainerT;
template class XClass;
The issues is some of the methods specialized for these templates are dependent on more than one of the templates and the specializations need to happen after the declarations.
How do I using "typename" or other template features get this for compile and link under CCC as opposed to windows MSC ?
I am working with templates in C++. Is there any difference in using templates and friend class when compiled with MSVC compiler and when using Mingw gcc compiler. My code successfully compiles and gives the desired output when compiled with MSVC but it gives error when compiled with gcc. Below is my code,
///////////Record.h/////////////////////
#include "Base.h"
class Derived1;
class Derived2;
template <class TYPE_LIST> class List;
class FRecord
{
public:
FRecord();
virtual ~FRecord();
friend class Base;
#if _MSC_VER <= 1200
friend class List<Derived1>;
friend class List<Derived2>;
#else
template <class TYPE_LIST> friend class List;
#endif
};
///////////////////////////////////////////////////////////////
///////////////////Base.h/////////////////////////////////
class Base
{
public:
Base(const HEADER *hc, const FRecord *fr);
virtual ~Base();
__inline bool IsNonValid() const;
protected:
quint32 Size;
};
/////////////////////////////////////
// Data
/////////////////////////////////////
template <class TYPE_LIST>
class Data : public TYPE_LIST
{
public:
Data(const HEADER *hc, const FRecord *fr) : TYPE_LIST(hc, fr)
{
QString val = IsNonValid() ? "Non" : "";
LOG0("Data ("<< val << " Valid)");
}
virtual ~Data()
{
LOG0("Data deleted");
}
}; // Data
///////////////////////////////////////////////////////////////////////////////////////
When compiled the above code with MSVC gives desired output but when compiled with Mingw GCC compiler it gives following error,
Base.h:1154: error: there are no arguments to 'IsNonValid' that depend on a template parameter, so a declaration of 'IsNonValid' must be available
Base.h:1553: error: 'Size' was not declared in this scope
What could be the possible solution to this problem?
Thanks in advance.
MSVC does not implement two-phase name lookup correctly. GCC is correct in reporting this error.
The cause is that names used inside a template which do not depend on the template's parameters are (should be in the case of VC) looked up when the template is defined, not when it's instantiated.
In your case, the compiler has no way of telling that IsNonValid will come from the base class, so it rightfully complains it doesn't know it. There are two possible solutions:
Qualify the access to IsNonValid, so that it's clear to the compiler it (potentially) depends on the template parameters:
QString val = this->IsNonValid() ? "Non" : "";
// or
QString val = TYPE_LIST::IsNonValid() ? "Non" : "";
Introduce the inherited name into the scope of the derived class:
template <class TYPE_LIST>
class Data : public TYPE_LIST
{
public:
using TYPE_LIST::IsNonValid;
// the rest as you had it originally
Either of these will make the name dependent and thus postpone its lookup until instnatiation, when the value of TYPE_LIST is actually known.
gcc is correct. You need to add this-> to delay lookup until instantiation time.
this->IsNonValid();
MSVC is non-conforming in that it delays all lookups until instantiation time as it doesn't properly implement two-phase name lookup.
I'm trying to deal with namespaces & templates in C++. I can get the following code to compile in MSVC (no warnings or errors), but am having no luck at all with various permutations with CYGWIN/GCC. Any help would be appreciated.
In the header file I declare a templated subclass as follows:
#include <gdal.h>
namespace sfms {
template <class _type, GDALDataType _gdal> class SmfsGrid_Typed : public SfmsGrid_Base {
public:
SmfsGrid_Typed();
SmfsGrid_Typed(const SmfsGrid_Typed<_type, _gdal> *toCopy);
SmfsGrid_Typed(std::string filename);
virtual ~SmfsGrid_Typed();
virtual bool OpenRead();
virtual bool OpenWrite();
protected:
_type m_nodata_value;
virtual SfmsGrid_Base *New() const;
virtual SfmsGrid_Base *New(SfmsGrid_Base *toCopy) const;
virtual void initCopy(SfmsGrid_Base *copy) const;
};
template SmfsGrid_Typed<double, GDT_Float64>;
template SmfsGrid_Typed<float, GDT_Float32>;
template SmfsGrid_Typed<int, GDT_Int32>;
typedef SmfsGrid_Typed<double, GDT_Float64> SmfsGrid_Double;
typedef SmfsGrid_Typed<float, GDT_Float32> SmfsGrid_Float;
typedef SmfsGrid_Typed<int, GDT_Int32> SmfsGrid_Int;
}
In the source file I instantiate the specialized template class as follows:
void hi_there() {
//...
sfms::SmfsGrid_Typed<int, GDT_Int32> *grid = new sfms::SmfsGrid_Typed<int, GDT_Int32>(filey);
//...
sfms::SmfsGrid_Int *grid2 = new sfms::SmfsGrid_Int(filey);
//...
}
GDALDataType is an enum, but that doesn't seem to the the issue.
I've tried the class declaration inside and outside the namespace, with no success.
The source file containing the implementations for the templates compile okay with both compilers.
I've tried dropping the explicit template instantation and including the relevant C++ source file, also with no joy.
I've tried 'template', 'typename' and 'typedef' keywords in various different places (in the templated class def'n and where I try to create the object) with no success, but various interesting and often misleading error messages with GCC, such as:
error: 'SmfsGrid_Typed' is not a member of 'sfms'
when it clearly is! :) Regardless, any help on porting this code from MSVC to GCC would help.
Thanks!
Your explicit template instantiation looks wrong. Try replacing it with
template class SmfsGrid_Typed<double, GDT_Float64>;
template class SmfsGrid_Typed<float, GDT_Float32>;
template class SmfsGrid_Typed<int, GDT_Int32>;
(Note the added class keyword)
How do I inherit from a virtual template class, in this code:
// test.h
class Base {
public:
virtual std::string Foo() = 0;
virtual std::string Bar() = 0;
};
template <typename T>
class Derived : public Base {
public:
Derived(const T& data) : data_(data) { }
virtual std::string Foo();
virtual std::string Bar();
T data() {
return data_;
}
private:
T data_;
};
typedef Derived<std::string> DStr;
typedef Derived<int> DInt;
// test.cpp
template<typename T>
std::string Derived<T>::Foo() { ... }
template<typename T>
std::string Derived<T>::Bar() { ... }
When I try to use the DStr or DInt, the linker complain that there are unresolved externals, which are Derived<std::string>::Foo() and Derived<std::string>::Bar(), and the same for Derived<int>.
Did I miss something in my code?
EDIT:
Thanks all. It's pretty clear now.
You need to define template<typename T> std::string Derived<T>::Foo() { ... } and template<typename T>
std::string Derived<T>::Bar() { ... } in the header file. When the compiler is compiling test.cpp it doesn't know all the possible values of T that you might use in other parts of the program.
I think there are some compilers that have connections between the compiling and linking steps that notice references to missing template instantiations and go instantiate them from the .cpp file where they are declared. But I don't know which they are, and the functionality is exceedingly rare to find.
If you define them in the header file most compilers will emit them as a 'weak' symbol into every compilation unit in which they're referenced. And the linker will toss out all except for one definition of the weak symbol. This causes extra compilation time.
Alternately, there are syntaxes for explicitly instantiating templates and forcing the compiler to emit definitions right there. But that requires you to be aware of all the values T could possibly have and you will inevitably miss some.
You have to ensure that the member functions are instantiate for all the required types somewhere.
Usually this is accomplished by defining template functions inline in the header file where they are declared so that any use of the functions will cause them to be instantiated.
As an alternative, you can use explicit instantiations in the source file where you define them but this does require you to know in advance all the types that your template will ever be instantiated for.
This doesn't really have much to do with derivation. It's just a general rule with templates: with most compilers (anything but Comeau) you have to put the full implementation of a template where it's visible in every translation unit that will instantiate that template -- normally in the header.
Even with Comeau, you have to use the export keyword to make things work right. Since They're the only ones that implement export, chances are pretty good that you don't care much though.