Consider a class 'B' that contains simple char
member vars in a certain order.
class B {
char x1;
char x2;
char x3;
char x4;
}
I have a buffer A
that already contains data in the same order as defined in B
. Another process has already loaded A
with the data.
char A[4];
Is it possible to construct an object of type B
that contains the data of A
, without the constructor copying the data? That is, I want to "overlay" a B
object onto the A
buffer so I can use B
methods on the data, without incurring the overhead of a copy, or the allocation of memory.
Assuming there is a solution to question 1, would there be any reason I couldn't also define a class D
that derives from B
and which has methods that reference the member vars of B
, but which itself contains no new member variables?
Since an A
is not a B
there is no legal way to treat an A
as a B
. That said, if A
is a standard layout class then you should be able to cast it and it will "Work" but it wont be legal. For example
struct A
{
char data[6] = "hi 0/";
int a = 10;
int b = 20;
};
struct B
{
char x1;
char x2;
char x3;
char x4;
char x5;
char x6;
};
std::ostream& operator <<(std::ostream& os, B& b)
{
return os << b.x1 << b.x2 << b.x3 << b.x4 << b.x5 << b.x6;
}
int main()
{
A a;
B* b = reinterpret_cast<B*>(&a);
std::cout << *b;
}
This works since the array and the members occupy the same section of memory in each class but this isn't guaranteed. There could be padding after x1
in B
which would mean not all members will be mapped to the array.
Now, if you rework B
to have an array instead of separate members like
struct A
{
char data[6] = "hi 0/";
int a = 10;
int b = 20;
};
struct B
{
char data[6];
};
Then you could uses a Union to hold both A
and B
and since B
has the same common initial sequence as A
it is legal to use b
. That could look like
union Converter
{
Converter() : a{} {}
A a;
B b;
};
std::ostream& operator <<(std::ostream& os, B& b)
{
return os << b.data;
}
int main()
{
Converter c;
std::cout << c.b;
}
And now the cast is gone and we have a guarantee from the standard that this is safe
As unpleasant as it is, there is no legal (Standard-wise) way to achieve that. Instead, you have one illegal, but usually working one (used across plethora of places) or legal, but optimization-dependent.
Regardless of method, this assumes that there is no padding for B members (add [[gnu::packed]]
for gcc, or something else for your compiler to B definition to ensure no padding is happening).
First would be illegal one - you can alias the type. This violates strict aliasing rule, but is known to work on many platforms and compilers. Code sample:
const B* b = reinterpret_cast<const B*>(&a[0]);
The second option is to rely on compiler's optimizer. It is often powerful enough to realize there is no need to actually copy the data, and will just use original values. It doesn't happen all the time, and if you rely on this technique in performance-critical section, you better check the generated code and recheck it with every compiler upgrade.
This code assumes an optimization:
B b;
memcpy(&b, &a[0], sizeof(b));
// use b in non-modifying way
// Compilers usually will not issue a copy here, YMMV
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