Reputation: 15407
It is well-known that static
variables of a routine, function, or method ("member function") exist for each unique instantiation.
In the common case, this is exactly one variable:
int f() {
static int i = 0;
return i++;
}
That is, there is a single variable i
that is created in .BSS/.DATA that "belongs" to the function f
. For templates, it's one per unique instantiation:
template <typename T> int f() {
static int i = 0;
return i++;
}
int g() {
return f<int>() + f<int>() + f<int>() + f<float>();
}
Here, there are two unique instantiations (f<int>
and f<float>
), and so there are two i
s in the .BSS/.DATA segment.
I want some way to make variables in template member functions live both per-instance and per-instantiation of their enclosing class. I am interested in achieving this EFFECT, by more-or-less any performant means necessary (possibly not involving static at all). How should I do this?
For example:
class Foo { public:
template <typename T> int f() {
static int i = 0;
return i++;
}
};
void g() {
Foo foo1;
Foo foo2;
/*
These will have values a=0, b=0, c=1. This happens because there are two
variables:
"Foo::f<float>()::i"
"Foo::f<double>()::i"
What I *want* is for there to be three variables:
"Foo::f<float>()::i" (foo1's copy)
"Foo::f<float>()::i" (foo2's copy)
"Foo::f<double>()::i" (foo1's copy)
So the result is a=0, b=0, c=0. How can I accomplish this effect (maybe
not using static)?
*/
int a = foo1.f<float>();
int b = foo1.f<double>();
int c = foo2.f<float>();
}
Upvotes: 2
Views: 98
Reputation: 13988
As in general one should avoid using typeid
an alternative might be variable template. Of course variable template may only be static so to store unique value for each instance one need variable template mapping instance -> value
.
#include <cassert>
#include <map>
struct Foo {
template <class T> static std::map<Foo *, int> m;
template <class T> int f() {
return m<T>[this]++;
}
};
template <class T> std::map<Foo *, int> Foo::m;
int main() {
Foo foo1;
Foo foo2;
assert(foo1.f<int>() == 0);
assert(foo1.f<int>() == 1);
assert(foo1.f<int>() == 2);
assert(foo1.f<float>() == 0);
assert(foo2.f<int>() == 0);
assert(foo2.f<int>() == 1);
assert(foo2.f<int>() == 2);
assert(foo2.f<float>() == 0);
}
It might also be a good idea to replace std::map
with std::unordered_map
(it will not require any additional hasher - example)
Approach can also be successfully applied before c++14 as a variable template can be simply replaced with inner structure template.
Warning
It have to be stated that by default an approach won't support deep copy cloning value for given instance.
To overcome the problem one can use a little bit modified technique still utilizing variable templates and mapping (still no RTTI required):
#include <cassert>
#include <unordered_map>
struct Foo {
template <class T>
static int label;
std::unordered_map<int *, int> m;
template <class T> int f() {
return m[&label<T>]++;
}
};
template <class T> int Foo::label;
int main() {
Foo foo1;
Foo foo2;
assert(foo1.f<int>() == 0);
assert(foo1.f<int>() == 1);
assert(foo1.f<int>() == 2);
assert(foo1.f<float>() == 0);
assert(foo2.f<int>() == 0);
assert(foo2.f<int>() == 1);
assert(foo2.f<int>() == 2);
assert(foo2.f<float>() == 0);
}
Upvotes: 1
Reputation: 523794
You could store a type map for each instance. Here I assume RTTI is enabled to use type_index
, if you cannot use RTTI, check out Boost.TypeIndex or Template metaprogram converting type to unique number for its replacement.
#include <unordered_map>
#include <typeindex>
#include <cstdio>
class Foo {
std::unordered_map<std::type_index, int> _values;
public:
template<typename T>
int f() {
int& i = _values[typeid(T)];
return i++;
}
};
int main() {
Foo foo1;
Foo foo2;
int a = foo1.f<float>();
int b = foo1.f<double>();
int c = foo2.f<float>();
foo1.f<float>();
int d = foo1.f<float>();
int e = foo1.f<double>();
int f = foo2.f<float>();
printf("%d %d %d %d %d %d\n", a, b, c, d, e, f);
// prints 0 0 0 2 1 1
}
Upvotes: 2
Reputation: 304182
The only way to have different instances of an object to give you different results is to have those objects have member variables with different values. The simplest approach is just to have a std::map
of std::type_info
:
struct TypeInfoCompare {
bool operator()(std::type_info const* lhs, std::type_info const* rhs) const {
return lhs->before(*rhs);
}
};
struct Foo {
template <class T>
int f() {
return m[&typeid(T)]++;
}
std::map<std::type_info const*, int, TypeInfoCompare> m;
};
This gives you a per-type counter that will be different for each instance of Foo
.
The std::map
could also be a std::unordered_map
, and use std::type_info::hash_code()
as the hash.
Upvotes: 3