Template specialisation in one TU hidden by another TU - c++

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.

Related

How to use a dllexport-ed class which is derived from an explicitly instantiated template in a dll without warnings?

So I have a dll which exports class which is derived from an explicitly instantiated (also exported) template.
parent.hpp
#pragma once
template <typename T>
struct parent {
parent(T t) m_t(t) {};
void print();
T m_t;
};
parent.cpp
template<typename T>
void parent<T>::print() {
cout << m_t << endl;
}
template class LIB_API parent<int>;
child.hpp
#include "parent.hpp"
extern template class parent<int>;
struct LIB_API child : public parent<int> {
using parent<int>::parent;
void some_method();
}
child.cpp defines some_method
So far everything is great and works. I can safely use the child class from targets which link with the dll. The problem comes when I use the child class in the dll itself in another compilation unit:
some_other_dll_file.cpp:
void func()
{
child c(53);
c.print();
c.some_method();
}
In this case I get a warning: warning C4661: 'void parent<int>::print(void)': no suitable definition provided for explicit template instantiation request
(or in my particular case a ton of warnings for each and every method which is not visible in the template header in each and every file in the dll which uses the child class)
Note that it's a warning only. Eventually everything compiles and links, and works fine.
Is there a way to change the code so I don't get this warning?
The code contains lots of errors, probably some are typos, some others are missing parts of code, and so on.
The warning you get is because the explicitly instantiated template (parent<int>) doesn't have the print method defined (only declared) in the some_other_dll_file translation unit. Check [SO]: warning C4661:no suitable definition provided for explicit template instantiation request (#SergeBallesta's answer). You'll get rid of the warning by moving print's body in parent.hpp.
Below it's a working example.
Dll project
dll00.h:
#pragma once
#if defined (DLL00_INTERNAL) || defined(DLL00_STATIC)
# define DLL00_API
#else
# if defined(DLL00_EXPORTS)
# define DLL00_API __declspec(dllexport)
# else
# define DLL00_API __declspec(dllimport)
# endif
#endif
parent.hpp:
#pragma once
#include <dll00.h>
#include <iostream>
template <typename T>
class parent {
public:
parent(T t): m_t(t) {};
void print();
private:
T m_t;
};
template <typename T>
void parent<T>::print() {
std::cout << m_t << std::endl;
}
parent.cpp:
#define DLL00_EXPORTS
#include <parent.hpp>
template class DLL00_API parent<int>;
child.hpp:
#pragma once
#include <dll00.h>
#include <parent.hpp>
extern template class parent<int>;
class DLL00_API child : public parent<int> {
public:
using parent<int>::parent;
void some_method();
};
child.cpp:
#define DLL00_EXPORTS
#include <child.hpp>
#include <iostream>
void child::some_method() {
std::cout << "child::some_method" << std::endl;
}
other.cpp:
#define DLL00_INTERNAL
#include <child.hpp>
void func() {
//*
child c(53);
c.print();
c.some_method();
//*/
}
App project
main.cpp:
#include <child.hpp>
int main() {
child c(12);
c.print();
c.some_method();
return 0;
}
If for some reason you want to have the function body in parent.cpp, then you'll have to simply ignore the warning. If you don't want to see it, add #pragma warning(disable: 4661) at parent.hpp's beginning. But bear in mind that ignoring warnings might get you in trouble in some cases.

CRTP, forward declarations and templates in cpp files

I'm using the CRTP pattern to create an interface, which other classes will derive from.
In the interface I forward declare a structure (important because I don't want to drag other stuff in the interface), but I include its definition in the cpp file which defines the interface.
Interface.h
#ifndef INTERFACE_H_INCLUDED
#define INTERFACE_H_INCLUDED
// forward declaration
class ForwardDecl;
template <class Derived>
class Interface
{
public:
ForwardDecl interfaceMethod();
};
#endif // INTERFACE_H_INCLUDED
ForwardDecl.h
#ifndef FORWARDDECL_H_INCLUDED
#define FORWARDDECL_H_INCLUDED
struct ForwardDecl
{
ForwardDecl(int i):internal(i)
{}
int internal;
};
#endif // FORWARDDECL_H_INCLUDED
Interface.cpp
#include "Interface.h"
#include "ForwardDecl.h"
template<class Derived>
ForwardDecl Interface<Derived>::interfaceMethod()
{
return static_cast<Derived *>(this)->implementation_func();
}
And this is the implementation which implements the interface
Implementation.h
#ifndef IMPLEMENTATION_H_INCLUDED
#define IMPLEMENTATION_H_INCLUDED
#include "Interface.h"
class ForwardDecl;
class Implementation: public Interface<Implementation>
{
friend class Interface<Implementation>;
private:
ForwardDecl implementation_func();
};
#endif // IMPLEMENTATION_H_INCLUDED
Implementation.cpp
#include "Implementation.h"
#include "ForwardDecl.h"
#include <iostream>
struct ForwardDecl Implementation::implementation_func()
{
ForwardDecl fd(42);
std::cout << fd.internal << std::endl;
return fd;
}
And the main file
#include <iostream>
#include "Implementation.h"
#include "ForwardDecl.h"
using namespace std;
int main()
{
Implementation impl;
ForwardDecl fd = impl.interfaceMethod();
cout << fd.internal << endl;
return 0;
}
I get linking errors on both VS and GCC.
Any workaround? Thank you.
There is a flaw in your very approach: You have a public function returning a ForwardDecl instance, so every client wanting to use this function also must include the according definition of that type, which implies you can make that type public from the beginning. This includes making the function definition inline, which will fix your linker problems.
However, if you really want to hide the content of that structure and you are sure clients don't need it directly, you can declare it and then pass around references to such a structure (or pointers, but raw pointers are evil albeit not in the same league of evil as #macros). In that case, I would still make the function definition inline.
If you really, really want to not make the function inline, you can also explicitly instantiate the function template for the types that you need. You would add at the end of the template's .cpp file something like template class Interface<int>; (I don't remember the exact syntax so take that with a few flakes of fleur de sel, check out the C++ FAQ at parashift.com for more info). This makes the template a little less universal though, as it requires adjustments for any type that you want to use it with, but it can be an approach in some corner cases.
The definitions of function templates and member functions of class templates need to be visible in all translation units that instantiate those templates. That is, you shouldn't put template definitions in a .cpp file, which means you need to move the contents of Interface.cpp up into Interface.h.

Setting DSO interface for specialized template method

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?

Calling a C++ namespace-qualified function within a template function in another namespace

If I have a default function 'foo' defined in a namespace in one header file:
//DefaultFoo.h
namespace DefaultFooNamespace
{
template <typename T>
void foo(T& x){/*...*/}
}
and overloads of foo defined in another namespace in one or more other header files, e.g., FooOverloads.h:
//FooOverloads.h
namespace FooOverloadsNamespace
{
template <typename T>
void foo(std::vector<T>& x){/*...*/}
void foo(std::string& x){/*...*/}
//etc.
}
and I have another function 'bar' which calls foo after bringing both DefaultFooNamespace and FooOverloadsNamespace namespaces into scope (note that I #include only DefaultFoo.h in Bar.h, as DefaultFoo.h contains a single function in a namespace which is intended never to be extended, unlike FooOverloadsNamespace which will be extended by the addition of additional overloads):
//Bar.h
#include "DefaultFoo.h"
namespace BarNamespace
{
template <typename T>
void bar(T& x)
{
//... do stuff then call foo
using DefaultFooNamespace::foo;
using FooOverloadsNamespace::foo;
foo(x);
}
}
'bar' won't compile unless I either make sure FooOverloads.h is #include'd before a #include of Bar.h, or I make sure that FooOverloads.h is #include'd in Bar.h, or alternatively I provide a declaration of a 'foo' function for a dummy class in FooNamespace and #include that in Bar.h, e.g.
//Dummy.h:
struct DummyClass
{
private:
DummyClass(){}
};
namespace FooNamespace
{
inline void foo(DummyClass& x); //purely to open up namespace
}
//Bar.h
#include "Dummy.h"
Is there any way around this, such that I can define bar and avoid having to create redundant code to open up FooNamespace in Bar?
My solution would be to get rid of the useless DefaultFooNamespace namespace. Put the default foo in the same namespace as the overloads, then bar just does:
using FooNamespace::foo;
foo(x);
The default foo is used if the caller hasn't included a definition of a more appropriate one for the type of x
I don't see a reason for the default foo to be in a separate namespace, especially because doing that requires a separate dummy definition to make bar valid code, when the same overload could serve both purposes.

templates - undefined reference error

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.