Let's decompose how it works:
This is the function declaration. The important point is that P
is unrelated to T
. So, we need to pay more attention, as some bad things might occur...
template <typename P>
P* Write(P* pData)
{
Let's represent the memory pointed by pData
. For the sake of the explanation, I will assume that pData
points to a sufficiently large area in the memory (or else Write
will probably result in a segfault), and that Vector3<T>
is only the size of 3 T
s. In the following, I will take T = float
, with sizeof(float) = 4
, but my explanations will still be valid with other types. So, here sizeof(Vector3<float>) = 12
.
So, here is the memory, |-|
is one byte:
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
^
|
pData : P*
We interpret pData
as a pointer to a Vector3<T>
. This is bad style in general, as P
can be anything.
Vector3<T>* pVector = (Vector3<T>*) pData;
The following line:
*pVector++ = *this;
can be divided in:
*pVector = *this;
pVector++;
*pVector = *this;
assign the contents of the vector (*this
) to the data. The memory is now:
pVector : Vector3<float>*
|
v
|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-|
^^^^^^^^^^^^^^^^^^^^^^^^^
copied content of the vector
pVector++;
increments the vector by 1 * sizeof(Vector3<float>)
, so it now points to the non-yet-written memory:
pVector : Vector3<float>*
|
v
|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-|
pVector
is casted back to P*
and returned.
return (P*) pVector;
}
This enables to chain writes, as a write to the returned pointer will not overwrite the first write:
char data[2 * sizeof(Vector3<float>)];
v2.Write(v1.Write(data));
// now data is:
// contents of v2
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// |X1|X1|X1|X1|Y1|Y1|Y1|Y1|Z1|Z1|Z1|Z1|X2|X2|X2|X2|Y2|Y2|Y2|Y2|Z2|Z2|Z2|Z2
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// contents of v1
Now, some flaws:
This function can be useful for io, and is equivalent to a memcpy
on the vector. However, it has a lot of flaws, the major one being the template parameter. Writing directly to memory for such operations should use char
, and not something else. Writing in memory to overwrite the content of a class (ie, not io) is a very bad practice, (assignment operators are made for such a task, and they are safer). Also, the following example is not self-describing:
vec.Write<MyTimerClass>(pointer); // Does not make sense if you are reading this line for the first time
Secondly, there are problems in copying memory. Here, in the explanations, I assumed Vector3
was a simple type. It is not always the case. If it had virtual functions and members of different sizes, the layout in memory would have been implementation-defined, for example:
X, Y, Z padding
------------------------ ----
|P|P|P|P|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|a|a|.|.
-------- ----
vtable some
pointer other
member
And this is not reliable for io. See this SO question (the accepted answer is: "Here is how it works for msvc"...). And for multiple virtual inheritance it becomes a true nightmare.
By the way, the secure implementation of such a method would have been something like:
template<typename IOBuffer>
bool Write(IOBuffer& buffer)
{
return buffer << X << Y << Z;
}
or better:
virtual bool Write(IOBufferInterface& buffer)
{
return buffer << X << Y << Z;
}
It is way more simple to understand, maintain, debug, etc...