function template explicit instantiation extern - c++

I have a function which has exactly the same code for many basic types. To save code lines, I want to declare it as template and instantiate it explicitly for all later used types once: declaration in header file and implementation + explicit instantiation in cpp file (same design as writing normal function bibs).
My approach was:
// header.h
template <class T> T func (T arg);
// header.cpp
#include "header.h"
template <class T> T func (T arg)
{
// implementation...
}
template int func<int> (int arg);
template double func<double> (double arg);
However, on parashift, I found this form of declaration:
// header.h
template <typename T> extern void foo();
What is the sense of extern here? How differs it from my approach? What is correct?
Also normal functions never have to be declared extern. It would be different if it was extern template void foo<int>(); (see accepted answer here), what would prohibit the compiler to instatiate a template that is already implemented in the header file.
Edit:
Is
// header.h
int func (int arg);
double func (double arg);
// header.cpp
#include "header.h"
int func (int arg)
{ ... }
double func (double arg)
{ ... }
not completely analogous/equivalent to
// header.h
template <class T> T func (T arg);
// here is the question: like above or template <class T> extern T func (T arg); ???
// header.cpp
#include "header.h"
template <class T> T func (T arg)
{ ... }
template int func<int> (int arg);
template double func<double> (double arg);
concerning later usage
// main.cpp
#include "header.h"
int main ()
{
// for overloading
func(20);
func(20.0);
func((int)20.0);
// for template
func(20); // == func<int>(20)
func(20.0); // == func<double>(20.0)
func((int)20.0); // == func<int>((int)20.0)
func<int>(20.0); // == func<int>((int)20.0)
return 0;
}

Your syntax is declaring in the header file that there exists somewhere a template <class T> T func (T arg); which would still not let user have the code using that template function, but would let having template code doing that (which compiler won't be able to instantiate without having seen the template function definition).
The FAQ section you refer to is featuring the syntax declaring that there exists an instantiation of template for certain class X somewhere and the compiler, when seeing X x = func(X()); should not be instantiating a template but rather leave the symbol unresolved and let linker deal with it.

Related

Function overloading and template

I have an overloaded function, their definitions are the same and only one parameter change.
So, I tried to create a template function but when I call it I got a symbol lookup error.
Header file
void func(Myobject obj);
void func(MySecondObject obj);
Source file
template<typename T>
void func(T obj) { obj.foo(); }
Test file
#include "MyHeaderFile.h"
func(MySecondObject()); // symbol lookup error at runtime
Thanks.
If you say
void func(Myobject obj);
void func(MySecondObject obj);
then you promise to the compiler that it will eventually find
void func(Myobject obj)
{ /* implementation */ }
void func(MySecondObject obj)
{ /* implementation */ }
Since you didn't provide these implementations (there are no definitions for these symbols you declared), you get an error.
What you can do however is this:
Header file
void func(Myobject obj);
void func(MySecondObject obj);
Source file
template<typename T>
void func_impl(T obj) { obj.foo(); }
void func(Myobject obj) { func_impl(obj); }
void func(MySecondObject obj) { func_impl(obj); }
This allows you to declare and define "real" (non-templated) functions for your users, but you can still implement all of them by delegating the work to the same template function. It is best practice to put the func_impl into an unnamed namespace (namespace /* no name here */ { /* code goes here */ }) which will make it internal to that translation unit (and also makes it clear that it's not intended to be seen/used by other code, without having to cross-reference the header).
(The following is already discussed at Why can templates only be implemented in the header file?).
An alternative approach is to declare the function template in the header, then define it in the source file and provide explicit instantiations for the types you want:
Header file
template<class T>
void func(T obj);
// Explicit instantiation declarations
extern template void func<Myobject>(Myobject obj);
extern template void func<MySecondObject>(MySecondObject obj);
Source file
template<typename T>
void func(T obj) { obj.foo(); }
// Explicit instantiation definitions
template void func<Myobject>(Myobject obj);
template void func<MySecondObject>(MySecondObject obj);
This approach is significantly more verbose, potentially confusing for users that are not template-affine, and trying to use it with the wrong type leads to linker errors instead of a nicer compiler error, so the first solution above can easily be the most appropriate.
Or you could define the entire template in the header file. There can be good reasons to avoid that though.
It seems that you want to have a template function with two explicit instantiations for MyObject and MySecondObject. Template function declaration should be in your header like that:
template<typename T>
void func(T obj);
Then in the corresponding source file you should provide generic implementation + explicit instantiations:
#include "header.hpp"
template<typename T>
void func(T obj)
{
obj.foo();
}
template void func<MyObject>(MyObject obj);
template void func<MySecondObject>(MySecondObject obj);
Then you can use that two versions in your code:
func(MyObject());
func(MySecondObject());
but call to func with another template parameter, e.g. func('c') will result in undefined reference.
cpp reference section "Explicit instantiation".
I would suggest defining the template specializations in the source file. It hides the template specializations in the current translation unit.
MyHeaderFile.h
template <typename T>
void func(T obj);
MyHeader.cpp
#include <iostream>
#include "MyHeaderFile.h"
template <typename T>
void func(T obj)
{
std::cout << "function name: " << __func__;
}
template void func<MySecondObject>(MySecondObject obj);
template void func<Myobject>(Myobject obj);
I hope this answer solves your problem.

C++ Pass lambda to a method in template class (header/source files issue)

I am having problems passing lambda expressions as a parameter to a method of a template class.
If I compile the following code:
main.cpp:
#include "a.h"
int main(int argc, char** argv) {
A<int> o;
o.a([&](){ });
}
a.h:
template <typename T>
class A {
public:
template <typename Lambda>
void a(const Lambda& f) {
f();
}
};
It works fine.
However I do not want to have my implementation of class A in a.h, I want to put the code it in a separate cpp file.
So I now have
main.cpp:
Unchanged
a.h:
template <typename T>
class A {
public:
template <typename Lambda>
void a(const Lambda& f);
};
a.cpp:
#include "a.h"
template <typename T>
template <typename Lambda>
void A<T>::a(const Lambda& f) {
f();
}
template class A<int>;
I now get the following error:
In file included from test.cpp:1:0:
a.h:7:7: error: 'void A<T>::a(const Lambda&) [with Lambda = main(int, char**)::__lambda0; T = int]', declared using local type 'const main(int, char**)::__lambda0', is used but never defined [-fpermissive]
void a(const Lambda& f);
^
Why is that? How can I fix it?
I noticed if I put everything in the same cpp file, it works. The problem only occurs when I want to split it this way which is necessary to keep my code organized.
Thank you for your answer
This is very strange because even this works:
main.cpp
#include "a.h"
int main(int argc, char** argv) {
A<int> o;
o.a([&](){ });
}
template <typename T>
template <typename Lambda>
void A<T>::a(const Lambda& f) {
f();
}
template class A<int>;
a.h
template <typename T>
class A {
public:
template <typename Lambda>
void a(const Lambda& f);
};
This works.
For some reason,
It just won't work when I separate the main() and the method implementation in two distinct cpp files.
Why?
OK I think I have found an explanation.
When you split the code of a template class between a .h and a .cpp,
You need to make template instantiation explicit.
The problem is that lambda expressions have no specified type, therefore it is not possible to write a template explicit instance of that function for a lambda type.
What I want is impossible:
Is it possible to explicitly specialize template to match lambda?
Generally you can't really put the implementation of template functions/classes in a .cpp file. The reason is that when the .cpp file is compiled separately the compiler has no way to know what template parameters will be passed to it. So I think for your situation it's best to leave everything in a header file.
As others have said it is not possible to separate a template class from its implementation in header and cpp files. However, if the class itself is not a template but one of the member functions accepts a lambda then you can separate the implementation. To do this, do not use template to declare the lambda. Use std::function instead.
Header file a.h.
#include <functional>
namespace xx {
struct A {
void square(int num, std::function<void(int)>consumer);
};
}
CPP file a.cpp.
namespace xx {
void A::square(int num, std::function<void(int)>consumer) {
consumer(num * num);
}
}
Use this as follows.
#include "a.h"
#include <assert.h>
int main() {
xx::A a;
a.square(10, [](int result) {
assert(result == 100);
});
return 0;
}

C++ template explicit instantiation

I have working code like this.
#include <iostream>
struct A{
template<typename T>
void foo(T val);
};
template<typename T> void A::foo(T val)
{
std::cout << val << std::endl;
}
// link template "against" int
template void A::foo(int val);
// #include header here
int main(){
A a;
a.foo(12);
}
Template is in separate CPP file, but linking works, because of explicit instantiation:
template void A::foo(int val);
Then I did some re-factoring, and code looks like this:
#include <iostream>
template<typename G>
struct A{
template<typename T>
void foo(T val);
};
template<typename G>
template<typename T> void A<G>::foo(T val)
{
std::cout << val << std::endl;
}
// link template "against" int - not working
//template<typename G>
//template void A<G>::foo(int val);
int main(){
A<float> a;
a.foo(12);
}
How can I "link" T=int, but keep G "unknown"?
It is called explicit instantiation.
You can't do this, because G is unknown and it is not a single type. It is rather a set of types.
You can not do this. To actually produce a code from a template (I guess that's what you call link), the compiler need to know all the template parameters.
So you are left with the standard options for template instantiation: either explicitly tell the compiler what T and G will be used, either let the compiler see full code for the template member wherever you use it (that is, include the code in header).
TL;DR you can't.
In your case I'd just specify the type you intend to use
template void A<float>::foo(int val);
or (rather bulky) explicitly instantiate all the types G could be used for.
There is no way you can explicitly instantiate the template if G cannot be deduced.
Notice that linking works not because this syntax is a linker command but because your compiler is producing code that is later found at link-time. See more here

extern template does not work for gcc?

C++11 introduced a feature called 'extern template' which indicates that template instance exists in other translate unit.(Am I right?)
This(http://www.youtube.com/watch?v=3annCCTx35o) lecture also tells that if you specify extern template and don't include instantiation, the linker will produce error.(around 2:25 in the video)
So, I've tried to build next code:
#include <iostream>
template<class T>
struct Foo
{
static constexpr int type_size = sizeof(T);
};
extern template struct Foo<int>;
int main()
{
std::cout<< Foo<int>::type_size << std::endl;
return 0;
}
I expected the build will fail because this file does not contain explicit instantiation nor specialization, but gcc just builds it up the result runs well.
What am I missing? Or, am I misunderstanding something? Or, does not gcc support extern template well?
Update
I've tried a class with non-inline function, and extern template works as expected!
#include <iostream>
template<class T>
struct Foo
{
static void print(T t);
};
template<class T>
void Foo<T>::print(T t) { std::cout << t << std::endl; }
extern template struct Foo<int>;
// template struct Foo<int>;
int main()
{
Foo<int>::print(1);
return 0;
}
Above source is not built without the commented line.
Thank you all guys!
if you specify extern template and don't include instantiation, the linker will produce error.
No, not necessarily. There is only a problem if you actually use the template. You're using a compile-time constant defined as a static member of that template, but that is replaced by the constant's value at compile-time. And after that replacement, there is no longer any use of the template, so there is no need for a definition of the template.

Declaration of specialized function in template class

The problem I am struggling with is the declaration of specialized template function inside template class (I keep class declaration in header file and define member functions in associated .C file).
I have template class representing Points. The header file is presented below:
//...
template<typename T, int dim=3> // T - coords. type, int dim - no. of dimensions
class Point {
public:
// ...
// function below sets val at the given position in array m_c and returns reference
template<int position> Point& set(T val);
private:
T m_c[dim]; // coordinates
};
//...
definition of function set is placed in .C file:
template<typename T, int dim> template<int position> Point<T, dim>& Point<T, dim>::set(T val){
// ...
return *this;
}
As I understand this is the most general form of its definition.
In main function I create Point with float as T and try to set some values in the array:
int main(int argc, char** argv) {
Point<float> p1;
p1.set<0>(3).set<1>(3.6).set<2>(3);
//...
}
In order to make this possible with definition of the member functions of template outside header file I need to inform compiler about specialization in .C file:
template class Point<float>;
and I need as well to declare usage of set function, which I try to accomplish this way
(and this piece of code is the problem):
template<> template<int> Point<float>& Point<float>::set(float);
That unfortunately doesn't do the job and I get errors:
/tmp/ccR7haA5.o: In function `main':
.../pdim.C:32: undefined reference to `Point<float, 3>& Point<float, 3>::set<0>(float)'
.../pdim.C:32: undefined reference to `Point<float, 3>& Point<float, 3>::set<1>(float)'
.../pdim.C:32: undefined reference to `Point<float, 3>& Point<float, 3>::set<2>(float)'
I would really appreciate an explanation from someone who may know how to cope with this problem. Thanks.
In order to provide the definition of a function template specialization in a different TU, you need an explicit instantiation declaration:
[Point.hpp]
template<typename T, int dim=3>
struct Point
{
template<int position> Point& set(T val);
};
// `extern` makes this an explicit instantiation _declaration_
extern template Point<float,3>& Point<float,3>::set<0>(float);
extern template Point<float,3>& Point<float,3>::set<1>(float);
extern template Point<float,3>& Point<float,3>::set<2>(float);
[Point.cpp]
#include <iostream>
#include "Point.hpp"
template<typename T, int dim>
template<int position>
Point<T,dim>& Point<T,dim>::set(T val)
{
// note: non-standard macro
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
// no `extern`: this is an explicit instantiation _definition_
// which instantiates the function template, and therefore requires the definition
// to be available in this TU
template Point<float,3>& Point<float,3>::set<0>(float);
template Point<float,3>& Point<float,3>::set<1>(float);
template Point<float,3>& Point<float,3>::set<2>(float);
[main.cpp]
#include "Point.hpp"
int main()
{
// in this TU, there's no definition for the function template
// hence, it cannot be instantiated
// however, we can use the explicit instantiations
Point<float,3>().set<0>(0);
Point<float,3>().set<1>(0);
Point<float,3>().set<2>(0);
// does not compile (linker error):
//Point<int,3>().set<0>(0);
//Point<float,4>().set<0>(0);
//Point<float,3>().set<4>(0);
}