C++ strict-aliasing agnostic cast
Asked Answered
I

3

6

I've read lots of QAs about strict aliasing here in Stack Overflow but all they are pretty common and discussion always tends to refer to deep-deep details of C++ standard which are almost always are difficult to understand properly. Especially when standard do not say things directly but describes something in a muddy unclear way. So, my question is probably a possible duplicate of tonns of QAs here, but, please, just answer a specific question:

Is it a correct way to do a "nonalias_cast"?:

template<class OUT, class IN>
inline auto nonalias_cast(IN *data) {
    char *tmp = reinterpret_cast<char *>(data);
    return reinterpret_cast<OUT>(tmp);
}

float f = 3.14;
unsigned *u = nonalias_cast<unsigned *>(&f);
*u = 0x3f800000;
// now f should be equal 1.0

I guess the answer is no. But is there any nice workaround? Except disabling strict-aliasing flag of course. Union is not a handy option as well unless there is a way to fit a union hack inside nonalias_cast function body. memcpy is not an option here as well - data change should be synchronysed.

An impossible dream or an elusive reality?

UPD:

Okay, since we've got a negative answer on "is it possible?" question, I'd like to ask you an extra-question which do bothers me:

How would you resolve this task? I mean there is a plenty of practical tasks which more-less demand a "play with a bits" approach. For instance assume you have to write a IEEE-754 Floating Point Converter like this. I'm more concerned with the practical side of the question: how to have a workaround to reach the goal? In a least "pain in @#$" way.

Irrational answered 9/10, 2017 at 10:30 Comment(9)
by "nonalias cast" you mean a cast that does not violate strict aliasing rules?Coffey
It's not that the Standard forbids reinterpret_casts to change the type of an object. Rather, there is only one type of an object and a very limited number of other types that might be used to read it. You may not read from a float object with an expression that claims this object is an unsigned (not sure about forming that pointer, it might be misaligned).Baronetage
Concerning your union ideas: That cannot work either as it is UB tor read a union member other than the one last assigned to.Newland
@BaummitAugen, I know that. But I also know that it is a common workaround and almost any compiler will work as expected. Despite the fact that it is against the standard.Irrational
There would e.g. have to be an object attribute (and a pointer attribute) telling the compiler that the object rsp. pointee may be subject to aliasing. And this would have to be part of the proper type system if external functions were to be allowed as well. It would be a) non-standard and b) a lot of effort for such a fringe case.Paisano
@Oliv Well yes you can have multiple objects at the same address; at least the type of the complete object is unique. But you're allowed to read the value of an object with a number of types distinct from the type of that object, such as character types, cv-qualified versions of the type etc.Baronetage
@Baronetage Sorry I have read again your first comment, it seems i understood that you were saying the opposite of what you actualy wrote, my comment is like aiming to convince you you are right by beginning by a no....!!Colemancolemanite
Concerning IEEE-754 FP, see How to implement fast inverse sqrt without undefined behavior?. Short answer from there: "Use memcpy".Devisal
Funnily, neither clang-1 nor gcc-9/-10/-12 complain about the above code in any way. Options I used: -Wstrict-aliasing -O3 -Wall.Hoppe
C
6

As the other answers have correctly pointed out: This is not possible as you are not allowed to access the float object through an unsigned pointer and there is no cast that will remove that rule.

So how do you work around this issue? Don't access the object through an unsigned pointer! Use a float* or char* for passing the object around, as those are the only pointer types that are allowed under strict aliasing. Then when you actually need to access the object under unsigned semantics, you do a memcpy from the float* to a local unsigned (and memcpy back once you are done). Your compiler will be smart enough to generate efficient code for this.

Note that this means that you will have float* everywhere on your interfaces instead of unsigned*. And that is exactly what makes this work: The type system is aware of the correct data types at all times. Things only start to crumble if you try to smuggle a float through the type system as an unsigned*, which you'll hopefully agree is kind of a fishy idea in the first place.

Cailean answered 9/10, 2017 at 11:10 Comment(1)
Thank you, really nice example. Zero-cost memcpy is impressive.Irrational
N
5

Is it a correct way to do a "nonalias_cast"?

No.

But is there any nice workaround?

Again, no.

Reason for both is simply that &f is not the address of some object of type unsigned int, and no amount of casting on the pointer is going to change that.

Newland answered 9/10, 2017 at 10:40 Comment(0)
R
4

No, your nonalias_cast does not work, and cannot work.

Type aliasing rules are not (directly) about converting pointers. In fact, none of your conversions have undefined behaviour. The rules are about accessing an object of certain type, through a pointer of another type.

No matter how you convert the pointer, the pointed object is still a float object, and accessing it through an unsigned pointer violates type aliasing rules.


An impossible dream or an elusive reality?

In standard C++, it is impossible.

Redfield answered 9/10, 2017 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.