I have a class that has a template method:
class DoStuffWithAnything {
public:
template <typename T>
void doStuff(const T&);
};
Note that the method is not defined and will never be defined generically. Compilation units will define the implementation for these methods.
On gcc, I actually have no issue, because if a symbol is not defined it will be searched on the DSO.
On msvc I have a problem, how can I tell MSVC that it should import the definition (and when compiling the DSO, that it should export the definition)?
Clarifications
Suppose I have a lib called XX. and the libXX has a class XX. Also, the libXX defines <> doStuff(const XX&).
So, XX.hpp
class XX {
//...
};
And, XX.cpp:
#include <XX.hpp>
#include <do_stuff_with_anything.hpp>
//...
template <>
void DoStuffWithAnything::doStuff(const XX&) {
//...
}
So, my app.exe, would have a code like that:
#include <XX.hpp>
#include <do_stuff_with_anything.hpp>
int main() {
XX a;
DoStuffWithAnything stuffer;
stuffer.doStuff(a);
}
app.exe must know that doStuff<XX> is imported. How can I tell that?
Related
In regular C++ classes, we can have a class A as follows:
in A.h:
class A {
public:
void method();
// rest of class declaration
};
in A.cpp:
#include "A.h"
#include "implementation_dependencies.h"
void A::method(){
// use dependencies to implement method
}
But if A is a template class, this isn't possible. How can we still achieve information hiding with C++ template classes?
The use of extern template in C++11 (or global function prior to C++11 with the same effect) proves useful for this, but what to do when the template class needs to be available for all types?
Like a smart pointer for example: I can't put the definition of the class inside A.h since that would expose "implementation_dependencies.h" to anyone including "A.h".
From c++20, you can do this with modules:
// A.cpp
export module A;
export
template <typename>
class A {
// ...
};
#include "implementation_dependencies.h"
export template <typename T>
void A<T>::method() {
// use dependencies, but don't export them from this module
}
I'm pretty new to C++ but know majority of other mainstream programming languages. I've looked online for a solution to my problem but can't seem to find one. Here is some of my code so far:
object.h:
class Object final {
public:
template <Component T>
const Component& AddComponent<T>();
};
object.cpp:
#include "object.h"
template <Component T>
const Component& Object::AddComponent<T>() {
}
The issue is that symbol 'T' is not resolved on the line after the template keyword. I am using eclipse and the g++ compiler on linux.
First, in c++, the final keyword means that a virtual class method will not be overloadable by a child class, which is not how you are using it. This keyword should therefore go away.
Then, "Component" doesn't exist in c++. The way you're using it makes me think it is a typename, seeing as you are returning an element of type "Component". You should first define it or, if it is supposed to be a typename that varies for different calls of the function, it should be a template parameter.
You also shouldn't write "<T>" in the declaration of the function, as it doesn't make sense.
Last but not least, the definition of the function template should be specified in the header file, as it is required for instanciation.
The correct syntax should therefore be :
object.h:
class Object {
public:
template <typename Component, Component T>
const Component &AddComponent() {
// adding component and return statement here.
}
};
example main.cpp:
#include "object.h"
int main() {
Object obj;
obj.AddComponent<int, 4>();
return 0;
}
object.h (if "Component" is predefined):
class Object {
public:
template <Component T>
const Component &AddComponent() {
// adding component and return statement here.
}
};
main.cpp (if "Component" is predefined):
#include "object.h"
int main() {
Object obj;
obj.AddComponent<4>();
return 0;
}
Have a nice day.
PS: Sorry if I made any English mistakes, I'm French.
I have a class template which can be specialised in order to change behaviour of a utility function for user defined types.
I have one translation unit which uses the utility function with my type's specialisation available. The specialisation is used.
In a separate translation unit, if I call the utility function without including the specialisation, it changes the behaviour of the other TU (the specialisation is not used in both TUs)
Here is a complete example describing my issue:
check.h: defines the class template which can be specialised for user defined types
#pragma once
#include <iostream>
template<typename T>
struct Check
{
static void type();
};
template<typename T>
void Check<T>::type()
{
std::cout << "check fall-back\n";
}
template<typename T>
void check(T&&)
{
Check<T>::type();
}
type.h: definition of my user-defined type
#pragma once
enum Foo
{
FOO,
BAR
};
type_check.h: specialises Check for Foo
#pragma once
#include "type.h"
#include "check.h"
template<>
struct Check<Foo>
{
static void type()
{
std::cout << "check Foo\n";
}
};
lib.h: TU header file
#pragma once
void lib();
lib.cpp: TU source file - uses the specialisation from type_check.h
#include "lib.h"
#include "type_check.h"
void lib()
{
check(FOO);
}
main.cpp:
#include "check.h"
#include "type.h"
#include "lib.h"
#include "type_check.h" // uncomment this to make specialisation avail to all
int main()
{
check(5);
lib();
// check(FOO); // uncomment this to change behaviour of lib()`
return 0;
}
Results:
Calling lib() without calling check(FOO) in main results in the following:
check fall-back
check Foo
Calling lib() and check(FOO) in main results in the following:
check fall-back
check fall-back <-- main changes behaviour of lib
check fall-back
Including type_check.h in main.cpp, and then calling lib() and check(FOO) in main results in the following:
check fall-back
check Foo
check Foo
Question:
Why does calling check(FOO) in a separate TU when the Check<Foo> specialisation is not available remove it from the overload set in lib.cpp?
Notes:
I can't just put the Check<Foo> specialisation in the same file as the definition of Foo, as Foo is actually a generated file (protobuf)
This is a violation of the One Definition Rule. The linker sees two function definitions for the same function and will pick one. No diagnostic is required.
In this case, void Check<Foo>::type is defined once by the instantiation of the template definition from check.h used in lib.cpp, while the other definition is from type_check.h used in main.cpp.
I have the following simple template code:
#ifndef CLUSTER_H
#define CLUSTER_H
#include <iostream>
#include <vector>
template <typename T, size_t K>
class Cluster
{
public:
void Print() const;
private:
std::vector<T> objects;
};
template <typename T, size_t K>
void Cluster<T,K>::Print() const
{
for (int i=0; i<objects.size(); i++)
{
T curr=objects[i];
std::cout << curr << " ";
}
std::cout << std::endl;
}
#endif
and for some reason I get the following error: "undefined reference to 'Cluster<int, 5u>::Print() const'. What could be the cause for this?
Thanks!
So, I'm going to go out on a limb here and say that you've defined a template function in a CPP file, which means it will end up in a different translation unit. Here's a simple example:
A header, example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
template<int TValue>
class example
{
public:
int get_tvalue();
};
#endif
A source file, example.cpp
#include "example.h"
template<int TValue>
int example<TValue>::get_tvalue()
{
return TValue;
}
And another source file, main.cpp
#include "example.h"
int main()
{
example<5> instance;
instance.get_tvalue();
return 0;
}
If I compile these together using GCC, I get undefined reference to 'example<5>::get_tvalue()'. This is because of the way template classes are instantiated. A template class definition is just that... a template, not an actual class. The actual class definition is created when a parameterised (or specifically, fully specialised) definition of that class occurs, in this case, example<5>. That fully specialised class definition only exists in main.cpp... there's no such class inside example.cpp! Example.cpp contains only the template, and no specialisations. This means the function, get_tvalue is not defined for example<5> in main.cpp, hence the error.
You can fix this in one of two ways. The first way is to always have your entire template class defined in its header file. This is the way its done with STL containers, for example. The alternative is to force creation of a parameterised class in example.cpp... you can do this by adding
template class example<5>;
to the end of example.cpp. Because there's now an actual class definition for example<5> in example.cpp, you will also get an actual function definition for example<5>::get_tvalue and when your translation units main.o and example.o are linked together at the end of the compilation step everything will be fine.
Obviously, this would be a poor approach in most cases, but under circumstances where your template parameters take only a small range of values it can work. Putting your whole class in the header file is probably easiest, safest and most flexible though.
I'm using templates for implementing the CRTP pattern. With the code below I'm getting linker errors (for all the methods that are defined in the base class CPConnectionBase) like this:
error LNK2001: unresolved external symbol "public: void __thiscall CPConnectionBase::start(void)" (?start#?$CPConnectionBase#VTNCPConnection####QAEXXZ)
I guess the solution here is explicit template instantiation. And indeed I can build my code when I add
#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;
to the file CPConnectionBase.cpp. This is certainly the wrong place since I don't want to include the header of all possible derived classes into the source of the base class (I might want to use the base class also in another project with other derived classes).
So my goal is to instantiate the template in the source file of the derived class (TNCPConnection.h or TNCPConnection.cpp), but I couldn't find a solution. Adding
template class CPConnectionBase<TNCPConnection>;
to the file TNCPConnection.cpp does not solve my linker problems, and adding
template<> class CPConnectionBase<TNCPConnection>;
to the file TNCPConnection.cpp gives me a compile time error:
error C2908: explicit specialization; 'CPConnectionBase' has already been instantiated
How can I get rid of the linker errors without making the implementation of the base class dependent of the header files of the derived classes?
Here is the skeleton of my code:
CPConnectionBase.h
template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
void start();
};
CPConnectionBase.cpp
#include "stdafx.h"
#include "CPConnectionBase.h"
template<class Derived>
void CPConnectionBase<Derived>::start()
{
...
}
TNCPConnection.h
#include "CPConnectionBase.h"
class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
void foo(void);
};
TNCPConnection.cpp
#include "stdafx.h"
#include "TNCPConnection.h"
void TNCPConnection::foo(void)
{
...
}
The definition of CPConnectionBase::start() must be available at the place where you explicitly instantiate the class - otherwise the function will not be instantiated, and this non-instantiation happens silently (with linker errors following).
The standard solution is a header CPConnectionBase.hpp that defines the template functions declared in CPConnectionBase.h. Include CPConnectionBase.hpp in TNCPConnection.cpp and explicitly instantiate there.
I should add a note here:
MSVC allows to declare the explicit specialization before the member functions, inside the same compilation unit.
GCC (4.7) requires them to be at the end of the file.
I.e.
MSVC:
template class TClass<Base>;
template <class T> void TClass<T>::Function() {}
GCC:
template <class T> void TClass<T>::Function() {}
template class TClass<Base>;
First, I'd suggest renaming CPConnectionBase.cpp to CPConnectionBase.inl, as it doesn't contain any non-template code.
At the point where you instantiate the template, you need to #include <CPConnectionBase.inl> first. I'd suggest doing it in TNCPConnection.cpp.
Alternatively, you can move the CPConnectionBase implementation to the the CPConnectionBase.h header file, and the compiler will handle the instantiation automatically.
move the templated code from the .cpp to the header.
When you include a header with templates, the templated code is genereated in the target code according to what it finds in the header.
If the code is in the .cpp file it can't be found and thus cant be generated (because you only included the .h)
You could also use the "Seperation Model". Just define the template in one file, and mark that
definition with the keyword export:
export template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
void start();
};