Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type punning, char[] and dereferencing

I have a structure which aims at storing user defined data (i.e. from a plugin). It has a such a char[] with a given maximum size to store that data.

struct A
{
    // other members omitted
    // data meant to be type punned, only contains PODs
    char data[256];
};

Then there's a sample user structure that has a static function to cast itself from A.

struct B
{
    int i;
    double d;

    static B& FromA_ref(A& a)
    {
        // static_assert that sizeof(B) < sizeof(A::data)
        return * reinterpret_cast<B*>(a.data);
    }
};

I compile with g++ -O3 -std=c++0x -Wall -o test test.cpp (GCC 4.6.1).

This triggers a dereferencing type-punned pointer will break strict-aliasing rules warning. I thought that would be ok since I have used a char[] as storage which I thought would follow the same rules as char*. I find it strange that it doesn't. Don't you ? Well, ... I can't change it right now, so let's move on.

Now let's consider the following method:

struct B
{
    ....
    static B* FromA_ptr(A& a)
    {
        // static_assert that sizeof(B) < sizeof(A::data)
        return reinterpret_cast<B*>(a.data);
    }
}

Since I am not dereferencing anything here GCC doesn't output any warning. Neither does it when I use my pointer to B later on.

A a;
auto b = B::FromA_ptr(a);
b->i = 2; // no warnings.

But is it safe to do so ? I feel like I have been working my way around the problem rather than solving it. For me -> is still dereferencing the variable somehow.

Alternatively, are there any better way to achieve the effect ? I.E. get a modifiable reference (or pointer) casted from a storage inside another struct ? (Union won't work since the set of types stored are not known when A is defined and some may be added via plug-ins, memcpy will force me to copy the data back and forth though it seems to be the only safe way so far)

like image 614
J.N. Avatar asked Oct 08 '22 05:10

J.N.


1 Answers

The answer is no, it is not safe (see this SO question)

GCC will assume that the pointers cannot alias. For instance, if you assign through one then read from the other, GCC may, as an optimisation, reorder the read and write - I have seen this happen in production code, it is not pleasant to debug.

attribute((may_alias)) on the types used is probably the closest you can get to disabling the assumption for a particular section of code.

like image 93
Attila Avatar answered Oct 12 '22 11:10

Attila