Reputation: 43
I'm implementing an ECS framework for my game engine and investigating ways to identify component types at runtime. This means I can dynamically group components of a similar type contiguously in memory. For example, I can have two separate arrays for Position
and Velocity
components that my systems can loop over.
I currently use typeid()
, but I came across an article that uses static const
variables to generate unique IDs for each type. The general idea can be summarized in the code snippet below:
#include <iostream>
struct Position { float x, y; };
struct Velocity { float dx, dy; };
template <typename T>
int test(int x) {
static const int y = x;
return y;
}
int main(int argc, char **argv) {
std::cout << test<Position>(1) << " ";
std::cout << test<Position>(2) << " ";
std::cout << test<Velocity>(3) << " ";
return 0;
}
This outputs 1 1 3
, but I would expect that since it's attempting to modify a constant (specifically at the second call), it would fail to compile. Why does this work?
Upvotes: 4
Views: 730
Reputation: 5222
Static variables are only initialized once.
The second (and consequent) time the initialization is skipped.
Here also you have 2 different functions test
. 1 for each unique template parameter type.
An example, for a simple function call of Your code:
auto f() { return test<Position>(1); }
This will result in the following assembler code, where You can see that there is a check to verify if the variable has been already initialized or return the already set value. If it has not been set, then it will be set in a thread-safe manner.
f():
movzx eax, BYTE PTR guard variable for int test<Position>(int)::y[rip]
test al, al // <----- check if intialized
je .L13 // <----- if not initialized go to .L13
// otherwise return the value
mov eax, DWORD PTR int test<Position>(int)::y[rip]
ret
.L13:
sub rsp, 8
mov edi, OFFSET FLAT:guard variable for int test<Position>(int)::y
call __cxa_guard_acquire
test eax, eax
jne .L14
mov eax, DWORD PTR int test<Position>(int)::y[rip]
add rsp, 8
ret
.L14:
mov DWORD PTR int test<Position>(int)::y[rip], 1
mov edi, OFFSET FLAT:guard variable for int test<Position>(int)::y
call __cxa_guard_release
mov eax, DWORD PTR int test<Position>(int)::y[rip]
add rsp, 8
ret
Upvotes: 7
Reputation: 1579
Here, static const int y = x;
y
is const
means it is non-mutable
and static
is storage duration in program.
Compiler throws error if you try to modify y
directly in subsequent statement and it is undefined behavior if tried to modify indirectly.
From c++ reference,
const object - an object whose type is const-qualified, or a non-mutable subobject of a const object. Such object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.
Upvotes: 3