Define a global variable template? - c++

Usually, we do declare but not define a global variable in the header file. However, we define templates in it. Then the issue arises: is it possible to define a global variable template?
template <uint8_t PinCode>
uint8_t BitMask = digitalPinToBitMask(PinCode);
In reality, a global variable template:
instantizes only if at least one compile unit (CU) requires it.
causes no redefinition errors if multiple CUs require the same instance.
is shared among CUs, i.e., if one CU changes the value of an instance, other CUs requiring the same instance will be affected.
calls the initializer only once for each kind of required instance.

After longer research, I finally found the most elegant solution. The definition style depends on whether you want to initialize the variable.
If the variable does not need initialization, you only need a header file:
template <int TimerCode>
int TCCRB;
Yes, it is and must be so simple. Don't add "static" or "extern" keywords like we usually do for variables in a header file. It will pass compilation and work as a global variable template among all CUs. Same instances will share the same actual variable, i.e., changes in one CU will affect other CUs, as long as they have the same template arguments.
As we know, if you define a variable with no necessary keywords like "static" or "extern" in a header file, it will cause a redefinition error if the header is included in more than one CUs. "static" tell the compiler to copy this variable for each CU as individual variables, so changes in one CU won't affect that in another CU. Instead, "extern" tells the compiler that this variable here is only a declaration. It's defined and owned in only one of the CUs, and other CUs should only keep a reference or symbol to it. So that changes in one CU WILL affect other CUs.
However, a variable template is neither a static definition nor an extern declaration. It's a special instruction to the compiler: find all cases of references to this template, combine those of the same template arguments, and generate one definition automatically for each unique instance! This time the compiler takes the responsibility to avoid redefinition: it scans all CUs and then generates unique definitions itself!
This automatic definition is so convenient if you don't want to give the variable an initial value, and if there are too many possible instances to be listed one by one. However, if you do want an initial value, you'll have to define it yourself, and you'll need an individual CU to own these variables to avoid redefinition:
//In the header file:
template <int TimerCode>
extern int TCCRA;
//In the CPP file:
template <>
int TCCRA<1> = 2;
template <>
int TCCRA<2> = 5;
//Naturally you have to provide initial values for all possible instances …
For this case, the "extern" keyword is necessary because you explicitly defined all valid instances in your specially provided CU, and other CUs must refer to this definition. The compiler shouldn't generate a random definition itself. This is very much like a normal global variable definition, only adding some template grammars. However, the user can only use instances provided in your CPP file. This is also very natural because only for known instances can you provide an initial value.
I found very few tutorials on this topic on the Internet. I hope my experience will help more people ~

The standard defines many global constants which are templatized such as the type traits type_trait_v constants.
They are defined as such in the header
template<class T>
inline constexpr some_type some_variable = ...;
In your example you could do something like
template<uint8_t Pin>
inline constexpr uint8_t BitMask = digitalPinToMask(Pin);
For this to work then digitalPinToMask must be invocable in a constexpr context.

(Not to be confused with C++11 Extern Templates)
Ok, so after a long while battling compilers and linkers, the best solution I came up with for declaring a global variable that is a template with extern linkage is:
// lib.hpp
#pragma once
namespace lib {
template <typename T>
struct _GlobalTemplateExternImpl {
std::array<T, 2> value;
};
template <typename T>
inline auto &global_template_value = _GlobalTemplateExternImpl<T>::value;
}
// lib.cpp
#include "lib.hpp"
template
struct lib::_GlobalTemplateExternImpl<int>;
template <>
std::array<int, 2> lib::_GlobalTemplateExternImpl<int>::value = {}; // NOTE: the assignment (`= {}`) is important to do
template
struct lib::_GlobalTemplateExternImpl<float>;
template <>
std::array<float, 2> lib::_GlobalTemplateExternImpl<float>::value = { 7.0f, 66.6f };
// main.cpp
#include "lib.hpp"
#include <iostream>
int main() {
using namespace lib;
std::cout << global_template_value<int>[0] << "\n";
std::cout << global_template_value<int>[1] << "\n";
std::cout << global_template_value<float>[0] << "\n";
std::cout << global_template_value<float>[1] << "\n";
return 0;
}
And since C++ needs silly acronyms, I shall dub this the "Implicitly Externed Template Global Pattern" (IETGP) since the keyword extern is nowhere to be found.

Related

Do template specialized class with static methods occupy storage?

(I am sorry for the messy title. I will gladly accept suggestions to improve it.)
I will try to be as straightforward as possible. I have the folowing code:
file1.hpp
template <class val_t>
struct MatOps;
file2.hpp:
#include "file1.hpp"
template <> struct MatOps<float>{
static void method1(){
// Do something
}
static void method2(){
// Do something
}
static void method3(){
// Do something
}
}
File3.hpp:
#include "file1.hpp"
template <> struct MatOps<double>{
static void method1(){
// Do something different
}
static void method2(){
// Do something different
}
static void method3(){
// Do something different
}
}
main.cpp
#include "file2.hpp"
#include "file3.hpp"
int main(){
float a,b,c,d;
MatOps<float>::method1(a,b,...);
MatOps<float>::method2(c,d,...);
return 0;
}
Questions:
I am not using the explicit specialization MatOps<double>. However, is MatOps<double> actually instantiated? Or more roughly: does the inclusion of file3.hpp occupy any storage whatsoever?
I am not using MatOps<float>::method3(), but I am using the other methods in the class. Since I am explicitely using MatOps<float>, does the compiler generate code for MatOps<float>::method3()?
Rationale: I have been asked to follow some guidelines in the MISRA C++:2003 standard. Although obsolete, I have been encouraged to use whatever is reasonable in there. In particular, there is a rule that reads:
Header files should be used to declare objects, functinos, inline functions, function templates, typedefs, macros, classes, and class templates and shall not contain or produce definitions of objects or functions (or fragments of functions or objects) that occupy storage.
A header file is considered to be any file that is included via the #include directive, regardless of name or suffix.
My code is heavily templated and hence I can include any files according to this rule. My problem comes when I do full specializations (I only do two of them: the ones listed in file2.hpp and file3.hpp). What are full template specializations? Is code generated for them even if they are not used? Ultimately, do they occupy storage?
To answer your first question, I quote the following from cppreference.com:
A class template by itself is not a type, or an object, or any other
entity. No code is generated from a source file that contains only
template definitions. In order for any code to appear, a template must
be instantiated: the template arguments must be provided so that the
compiler can generate an actual class (or function, from a function
template).
Inclusion of file3.hpp will not result in code generation by itself.
As for the second part, again from the same page,
When code refers to a template in context that requires a completely
defined type, or when the completeness of the type affects the code,
and this particular type has not been explicitly instantiated,
implicit instantiation occurs. For example, when an object of this
type is constructed, but not when a pointer to this type is
constructed.
This applies to the members of the class template: unless the member is
used in the program, it is not instantiated, and does not
require a definition.
Unless you are doing an explicit instantiation of your class template, individual member functions of your class template will not get instantiated, i.e., the compiler will not generate code for MatOps<float>::method3().

Any potential pitfall of using inline static data member?

C++17 introduced inline variable, and an inline static data member can be defined in the class definition with an initializer. It does not need an out-of-class definition. For example,
struct X {
inline static int n = 1;
};
Given this, I see no reason not to always use inline static data members, for the neat syntax. Any pitfall of doing this? Note that I don't mind slower compilation.
Not a pitfall, but here's one reason not to use an inline: if the initial value of the variable is not just a trivial constant, but something more complicated:
struct X {
inline static int n = and_then_more(figure_out_where_n_comes_from());
};
Now, the declaration of figure_out_where_n_comes_from() and and_then_more() must be pulled into the header file, now.
Also, whatever figure_out_where_n_comes_from() returns must also be declared. It could be some horribly overcomplicated class, which then gets passed to and_then_more(), as a parameter, to finally compute the initial value for n.
And everything that #includes the header file where X is declared must now include all the header files for all of these dependencies.
But without an inline, all you have is:
struct X {
static int n;
};
And you need to deal with all these dependencies only in one translation unit that instantiates X::x. Nothing else that #includes only X's header file cares about it.
In other words: information hiding. If it's necessary to reimplement where the initial value of n comes from, you get to recompile only one translation unit, instead of your entire source code.

Template class's static variable initialization, c++

consider the following code:
//header.h
template<class T>
class A
{
static int x;
};
template<class T>
int A<T>::x = 0;
//source1.cpp
#include "header.h"
void f(){} // dummy function
//main.cpp
#include "header.h"
int main(){}
In this case code compiles perfectly without errors, but if I remove the template qualifier from class
class A
{
static int x;
};
int A::x = 0;
In this case compiler erred with multiple definition of x. Can anybody explain this behavior?
And when the template class's static variable is initialized / instantiated??
Compiler will remove duplicate template instantiations on its own. If you turn your template class into regular one, then its your duty to make sure only one definition of static variable exists (otherwise linker error will appear). Also remember that static data members are not shared between instatiations of templates for different types. With c++11 you can control instatiations on your own using extern templates: using extern template (C++11).
As for the point of instatiation for static members:
14.6.4.1 Point of instantiation [temp.point]
1 For a function template specialization, a member function template specialization, or a specialization for a
member function or static data member of a class template, if the specialization is implicitly instantiated
because it is referenced from within another template specialization and the context from which it is referenced
depends on a template parameter, the point of instantiation of the specialization is the point of
instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization
immediately follows the namespace scope declaration or definition that refers to the specialization.
so point of instatiation should be ie. right after main() if you use your type for the first time inside main().
Templates as the name suggest are the the fragments of code that will be used several times for different parameters. For templates the compiler is to ensure if their methods and static fiels definition are linked only ones. So if you create a static field with its default value the compiler is obliged to provide single memory cell (for the same template parameter set) even though the template class header is included several times. Unfortunetly non-template classes you need to be managed by yourself.
As for the second question, I believe the standard does not state when the static fields need to be initialized, each compiler can implement then it in its own manner.
It is necessary to instantiate/initialize static members in cpp files not in headers. Static members are property of class not property of objects, so if you include header file in more cpp files, it looks like you are initializing it more times.
Answer to this question is more complex. Template is not one class. It is instantiating on demand. It means that every different use of template is one standalone "template instance". For example if you use A<int> and A<float> than you will have 2 different classes therefore you would need to initialize A<int>::x and A<float>::x.
For more info see this answer: https://stackoverflow.com/a/607335/1280316
A class (be it a template or not) can (and should) be declared in any compilation unit that referes to it.
A static field initialization does defines a variable, and as such it should exist only in one compilation unit -> that's the reason why you get an error when class A is not a template.
But when you declare a template, nothing is really created untill you instantiate it. As you never instantiate the template, the static field is never defined and you get no error.
If you had two different instantiations in source1.cpp (say A<B>) and main.cpp (say A<C>) all will still be fine : you would get A<B>::x in source1 and A<C>::x in main => two different variables since A<B> and A<C> are different classes.
The case where you instantiate same class in different compilation units is trickier. It should generate an error, but if it did, you could hardly declare special fields in templates. So it is processed as a special case by the compiler as it is explained in this other answer to generate no errors.

Is there a scope shared between instances of a template class?

Besides the global scope, of course.
I have a custom assertion class with a macro to cache a reference to __FILE__:
#define DEFINE_THIS_FILE \
static const char THIS_FILE__[] = __FILE__
For source code, using the macro is no big deal since each source has its own scope. However, templated classes can't use source code, so I'm forced to do all my ASSERT() calls in the declaration/definition given by TemplateClass.h
If I use my macro outside of the class definition e.g. Singleton
DEFINE_THIS_FILE;
namespace NE
{
template<typename T>
class Singleton
{
...
}
}
Then the macro winds up in the same scope of any code that #includes Singleton, and the compiler throws a redefinition error for THIS_FILE__. (Of course, this only happens if the other code also uses the DEFINE_THIS_FILE macro.)
If I put the macro inside the declaration, the compiler won't complain but the linker won't be able to find THIS_FILE__ for any given instance of templated class e.g. Singleton:
namespace NE
{
template<typename T>
class Singleton
{
constexpr DEFINE_THIS_FILE; // constexpr modifier required in this case
...
}
}
I assume the linker error I get,
Undefined symbols for architecture x86_64:
"NE::Singleton<NE::NonTemplateType>::THIS_FILE__"
is caused by the template instances not existing in the same scope as where THIS_FILE__ was first defined, Singleton.h
OT: Is there a way to get all instances of my templated type Singleton to share a scope (global is unacceptable) so that all instances can use this static const macro?
Edit1
Further testing confirms: Using the macro DEFINE_THIS_FILE inside each templated method containing an ASSERT() will compile and run correctly....
In this case, the instances aren't sharing a scope, but have a static const char THIS_FILE__ defined for each method. This works, but I suspect that it uses as much or more ROM than a std::assert (with its its implied allocation of __FILE__).
I'll settle for this until an answer to OT comes about :)
Edit2
Silly me. Instead of using the workaround listed in edit above, I might as well create another macro, UNCACHED_ASSERT(argsToCheck) that directly uses __FILE__ instead of a const static representation.
Multiple ASSERTions per method could still benefit from the caching, though.
Still need an answer to OT, though.
You can place your declaration in unnamed namespace
#define DEFINE_THIS_FILE namespace { static const char THIS_FILE__[] = __FILE__; }

Using static variable along with templates

I have a template class defined in a header file like this. Here I have defined a static variable as well:
#ifndef TEST1_H_
#define TEST1_H_
void f1();
static int count;
template <class T>
class MyClass
{
public:
void f()
{
++count;
}
};
#endif
And I have defined main() function in a different cpp file like this:
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
I have implemented function f1() in a different cpp file like this:
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
When I compiled this using VC6, I got the output as "F1:0 Main:2". How is this possible? Also, in general how should I handle if I want to use static variables along with templates?
You're getting two copies of the same variable because you've declared a static variable in a header file. When you declare a global variable static this way, you're saying it's local to the compilation unit (the .o file). Since you include the header in two compilation units, you get two copies of count.
I think what you really want here is a static template member variable associated with each instance of the template class. It would look like this:
template <class T>
class MyClass
{
// static member declaration
static int count;
...
};
// static member definition
template<class T> int MyClass<T>::count = 0;
This will get you a count for each instantiation of your template. That is, you'll have a count for MyClass<int>, MyClass<foo>, MyClass<bar>, etc. f1() would now look like this:
void f1() {
MyClass<int> a;
a.f();
cout<<"F1: " << MyClass<int>::count <<"\n";
}
If you want a count for all instantiations of MyClass (regardless of their template parameters), you do need to use a global variable.
However, you probably don't want a global variable directly because you run the risk of using it before it gets initialized. You can get around this by making a global static method that returns a reference to your count:
int& my_count() {
static int count = 0;
return count;
}
Then accessing it from within your class like this:
void f() {
++my_count();
}
This will ensure that count gets initialized before it's used, regardless of which compilation unit you access it from. See the C++ FAQ on static initialization order for more details.
Putting the static declaration in a header file will cause each .cpp file to get its own version of the variable. So the two cout statements are printing different variables.
Were you expecting "F1:1 Main:1"? You instantiated MyClass<int> in two separate translation units (i.e. two object files), and the linker saw that there was a duplicate template instantiation, so it discarded the instantiation that was in f1's object file.
Are you passing /OPT:ICF or /OPT:REF to the VC6 linker? That might be related to the duplicate template instantiation removal (or not; duplicate template instantiations might be a special case, compared to ordinary duplicate functions). GCC seems to do something similar on some platforms.
Anyway, I wouldn't rely on this behavior being consistent across compilers. Also, changing the order of object files on the linker command line might affect which instantiation gets discarded.
There is another solution, you can create a shared parent class and put this static variable in it, then make your template class inherit it privately, here's an example:
class Parent
{
protected:
static long count;
};
long Parent::count = 0;
template<typename T>
class TemplateClass: private Parent
{
private:
int mKey;
public:
TemplateClass():mKey(count++){}
long getKey(){return mKey;}
}
int main()
{
TemplateClass<int> obj1;
TemplateClass<double> obj2;
std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl;
std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl;
return 0;
}
Output will be:
Object 1 key is: 0
Object 2 key is: 1
I think this is actually undefined behaviour.
According to C++14 [basic.def.odr]/6:
There can be more than one definition of a [...] member function of a class template [...] in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
each definition of D shall consist of the same sequence of tokens; and
in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3), except that a name can refer to a non-volatile
const object with internal or no linkage if the object has the same literal type in all definitions of D, and the object is initialized with a constant expression (5.19), and the object is not odr-used, and the object has the same value in all definitions of D; [...]
The problem is that in the first .cpp file, the name count within f1 refers to a different object than the name count within f1 in the second .cpp file, thus violating the condition that corresponding names should refer to the same entity.
They are different objects because of the static specifier which says that each translation unit gets its own object with that name.