I wrote a class template based on two types that is assigned a unique index based on its template parameters:
template<typename SK,typename T>
struct Component {
static uint const index;
};
The expectation is that for each new type, index
is incremented:
Component<X,A>::index; // 0
Component<X,B>::index; // 1
Component<Y,A>::index; // 0
Component<Y,B>::index; // 1
// ...etc
The complete code that assigns the indices is as follows:
using uint = unsigned int;
template<typename SK,typename T>
struct Component
{
static uint const index;
};
template<typename SK>
class ComponentCount
{
template<typename CSK,typename CT>
friend struct Component;
private:
template<typename T>
static uint next() {
return ComponentCount<SK>::get_counter();
}
static uint get_counter()
{
static uint counter = 0;
return counter++;
}
};
This works as expected in GCC (5.1) and MSVC with the following test:
// global scope
struct X {};
struct Y {};
int main()
{
// function scope
struct Z{};
uint x0 = Component<X,int>::index;
uint x1 = Component<X,double>::index;
uint x2 = Component<X,double>::index;
uint x3 = Component<X,std::string>::index;
uint x4 = Component<X,int>::index;
uint x5 = Component<X,int>::index;
std::cout << x0 << ", " << x1 << ", " << x2 << ", "
<< x3 << ", " << x4 << ", " << x5 << std::endl;
uint y0 = Component<Y,int>::index;
uint y1 = Component<Y,double>::index;
uint y2 = Component<Y,double>::index;
uint y3 = Component<Y,std::string>::index;
uint y4 = Component<Y,int>::index;
uint y5 = Component<Y,int>::index;
std::cout << y0 << ", " << y1 << ", " << y2 << ", "
<< y3 << ", " << y4 << ", " << y5 << std::endl;
uint z0 = Component<Z,int>::index;
uint z1 = Component<Z,double>::index;
uint z2 = Component<Z,double>::index;
uint z3 = Component<Z,std::string>::index;
uint z4 = Component<Z,int>::index;
uint z5 = Component<Z,int>::index;
std::cout << z0 << ", " << z1 << ", " << z2 << ", "
<< z3 << ", " << z4 << ", " << z5 << std::endl;
return 0;
}
The output is
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
However with Clang (3.6.1), the output differs:
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
5, 2, 2, 3, 5, 5
Specifically, the indices generated for function local types (ie. 'Z') do something strange. Its like they increment and reassign the index every time Component<Z,...>
is called.
Why does this happen? Is it a compiler bug? Are there any special considerations when using function local types with templates (post C++11)?
A complete example can be found here: http://coliru.stacked-crooked.com/a/7fcb989ae6eab476
== Edit ==
I decided to post the issue to clang's bugtracker, so if anyone else runs into this:
https://llvm.org/bugs/show_bug.cgi?id=24048
This looks like a bug to me. I am not aware of C++11 rules that should make a difference for function local types and global types.
If you dump the assembler, you can notice that while for X
and Y
the values get actually computed, for Z
they are precomputed. The guard variables for the counter static variable initialization are not generated at all.
.Ltmp87:
#DEBUG_VALUE: main:z5 <- 5
#DEBUG_VALUE: main:z4 <- 5
#DEBUG_VALUE: main:z3 <- 3
#DEBUG_VALUE: main:z2 <- 2
#DEBUG_VALUE: main:z1 <- 2
#DEBUG_VALUE: main:z0 <- 5
.loc 6 54 5 # main.cpp:54:5
movl std::cout, %edi
movl $5, %esi
.loc 6 74 5 # main.cpp:74:5
callq std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
I don't know if this is a bug in clang, but it seems that you have an issue with your current version of Component::template next.
To show this, I changed the access to the function member from "private" to "public". Then with the following code:
for(int i=0; i<5;++i)
std::cout<< ComponentCount<int>::next<int>() <<" ";
I get:
0 1 2 3 4
So maybe you should consider changing your implemention of next into something like this:
template<typename SK>
class ComponentCount
{
template<typename CSK,typename CT>
friend struct Component;
private:
template<typename T>
static uint next() {
static uint index = get_counter();
return index;
}
static uint get_counter()
{
static uint counter = 0;
return counter++;
}
};
With this I get the expected result on gcc, clang3.6 and VS2015
g++ (GCC) 5.1.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
clang version 3.6.0 (tags/RELEASE_360/final 235480) Target: x86_64-unknown-linux-gnu Thread model: posix
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
final code + runs of gcc & clang on coliru
[edit] typos
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With