We're playing some code golf at work. The purpose is to keep the signature of to_upper and return all arguments to upper. One of my colleague proposes this ~~ugly~~ brillant code:
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
std::string operator+(std::string_view& a, int const& b) {
std::string res;
for (auto c : a) {
res += (c - b);
}
return (res);
}
struct Toto {
std::string data;
};
struct Result {
std::string a;
std::string b;
};
std::unique_ptr<Toto> to_upper(std::string_view input_a,
std::string_view input_b) {
auto* res = new Result;
res->a = (input_a + 32);
res->b = (input_b + 32);
auto* void_res = reinterpret_cast<void*>(res);
auto* toto_res = reinterpret_cast<Toto*>(void_res);
return std::unique_ptr<Toto>(toto_res);
}
int main() {
std::unique_ptr<Toto> unique_toto_res = to_upper("pizza", "ananas");
auto* toto_res = unique_toto_res.release();
auto* res = reinterpret_cast<Result*>(toto_res);
std::cout << res->a << std::endl;
std::cout << res->b << std::endl;
return 0;
}
Is this use of reinterpret_cast is fine in terms of portability and UB?
We think that it's ok because we just trick the compiler on types, but maybe there's something we missed.
std::string operator+(std::string_view& a, int const& b)
It might not be exactly disallowed, but defining an operator overload for a standard class in the global namespace is just asking for ODR violations. If you use any libraries and if everyone else thinks this will just be fine, then someone else may also define that overload. So, this is a bad idea.
auto* void_res = reinterpret_cast<void*>(res);
This is entirely unnecessary. You get exactly the same result by reinterpret casting directly to Toto*.
Valid (and portable)
Assuming that lower and upper case are 32 apart isn't an assumption that is portable to all character encodigs. The function also doesn't work as one might expect for characters outside the range of a...z.
Now about the main question. reinterpret_cast a pointer (or reference) to another itself never has UB. It's all about how you use the resulting pointer (or reference).
The example is a bit precarious while the unique pointer owns the reinterpreted pointer because if an exception is thrown, then it would attempt to delete it which would result in UB. But I don't think an exception can be thrown, so it should be OK. Otherwise, you just reinterpret cast back, which is explicitly well defined by the standard in the case the alignment requirement of the intermediate type isn't stricter than the original (which applies to this example).
The program does leak memory.
The only problem here is you have a memory leak. You never delete the pointer after you call release.
You are allowed to use reinterpret_cast to cast an object to an unrelated type. You are just not allowed to access that unrelated type. Going from Result* to Toto* and then back to Result* is okay, and you only access the Result object through a Result*.
When doing T* to U* and then back to T* both T and U need to be object types and U cannot have a stricter alignment then T. In this case both Result and Toto have the same alignment so you are okay. This is detailed in [expr.reinterpret.cast]/7
Related
I have some input which can be simple value or container, packed in std::any. I don't want to use exceptions, so I call noexcept variadic any_cast method which return pointer or nullptr to value of any.
I can verify any cast available with typeid() but I don't want to use it and want to find some alternative. Some typetraits methods, such as decltype, declval or so on. Or simply using std::optional.
But optional seems is still damp and unstable in this case. MSVC compiler the program breaks at runtime in the depths of the std::optional source code.
#include <optional>
#include <any>
#include <utility>
int main() {
int input = 1;
std::initializer_list<int> input2;
input2 = {1,2,3};
std::any any1 = input;
std::any any2 = input2;
std::optional o1 = *std::any_cast<int>(&any1);
std::optional p2 = *std::any_cast<int>(&any2);
// **std::forward<int & __ptr64>**(...) in _Optional_destruct_base return nullptr.
}
Actually, typetraits check will be the best way to test any_cast possibility. But I still confused with c++ metaprogramming.
If you want to stick with std::optional and don't want to use pointers, I think you could write your own function that performs the pointer check for you and return the proper std::optional, something like:
template <typename T>
std::optional<T> get_v_opt(const std::any & a)
{
if(const T * v = std::any_cast<T>(&a))
return std::optional<T>(*v);
else
return std::nullopt;
}
Here in a usage example of how to use such a function:
int main()
{
std::any a(42);
std::optional opt_int = get_v_opt<int>(a);
std::optional opt_str = get_v_opt<std::string>(a);
if(opt_int.has_value())
std::cout << "a is an int with value: " << opt_int.value() << '\n';
else
std::cout << "a is not an int\n";
if(opt_str.has_value())
std::cout << "a is a string with value: " << opt_str.value() << '\n';
else
std::cout << "a is not a string\n";
return 0;
}
Output (for this example):
a is an int with value: 42
a is not a string
Never dereference a pointer without knowing it is non null.
This in general applies to all nullable types; in C++ these are generally types that support unary * and ->.
std::optional o1 = std::any_cast<int>(&any1)?*std::any_cast<int>(&any1):std::optional<int>();
for example.
any dereference of a null nullable type is UB, which can work, crash, or do absolutely anything else as far as the C++ standard is concerned.
But also consider std variant if your set of stored types is closed (does not change).
Pointers can be declared like this:
int
a = 1,
*b = &a, // 1st order pointer
**c = &b, // 2nd order pointer
***d = &c, // 3rd order pointer
****e = &d, // 4th order pointer
*****f = &e, // 5th order pointer
*** n-stars *** f; // n-th order pointer
Here, we need to know at compile-time the order of the pointer when we are declaring it.
Is it possible at all to declare a pointer, whose order is only known at run time? Linked to this question is whether is it possible to query at run-time the order of an arbitrary pointer?
int order = GET_ORDER_OF_PTR(f) // returns 5
int /* insert some syntax here to make ptr a pointer of order (order + 1) */ ptr = &f;
Note:
I already know this (generally) might not be a good idea. Still want to know if it's doable :)
In runtime you cannot - because C++ is statically typed. During compilation it is possible with templates, e.g.
template<typename T, int order> struct P
{
typedef typename P<T, order-1>::pointer* pointer;
};
template<typename T> struct P<T, 0>
{
typedef T pointer;
};
Then P<int, 3>::pointer is equivalent to int***.
You can't. C++ is statically typed, so the order of a pointer (which is part of its type) must be known at compile time.
Not in C or C++ because they're statically typed languages (i.e. the type of the values you store in a variable are known at compile time and fixed).
You can emulate this kind of possibility by defining a C++ class
template<typename T>
struct NPtr {
int order;
void *p; // order 0 -> T*, otherwise NPtr<T>*
NPtr(NPtr *ptr) : islast(ptr->order+1), p(ptr) {}
NPtr(T *final) : islast(0), ptr(final) {}
NPtr<T>& nptr() {
assert(order > 0);
return *(NPtr<T>*)p;
}
T& final() {
assert(order == 0);
return *(T*)p;
}
};
A TPtr<int> instance can either be a pointer to an integer (when order=0) or a pointer to another TPtr<int> instance.
The semantic equivalent of what you want is a linked list, with a number of node determined at runtime:
#include <iostream>
union kind_of_pointer
{
int data;
kind_of_pointer *next;
kind_of_pointer(int val) : data(val) {}
kind_of_pointer(kind_of_pointer* ptr) : next(ptr) {}
operator int()
{
return data;
}
kind_of_pointer& operator *()
{
return *next;
}
};
int main(void)
{
kind_of_pointer dyn_ptr{new kind_of_pointer{new kind_of_pointer{new kind_of_pointer{42}}}};
int*** static_ptr = new int**{new int *{new int{42}}};
std::cout << ***dyn_ptr << std::endl;
std::cout << ***static_ptr << std::endl;
}
I find this funny, interesting and horrible :)
You can do this:
int *f = nullptr;
decltype(f) *newptr = 0;
As for the
whether is it possible to query at run-time the order of an arbitrary
pointer?
part: no, you most definitely can not, unless you write your own wrapper-class that stores the order of the pointer. Pointer is, basically, a number: the address in memory.
This gives you at least one problem: you can't follow the pointer "all the way through" (which you would have to do to check if the thing your pointer is pointing at is itself a pointer) without potentially causing a segfault or reading "not your application's memory" (which is often prohibited by OS and will cause your application to be aborted; not sure about weather you can prevent this or not).
For example, NULL (or 0) can be cast to any pointer type, so is itself a pointer. But does it point to another pointer? Let's find out... BAM! SEGFAULT.
Oh, wait, there's another problem: you can cast (with c-style cast or with reinterpret_cast) pointer of any type to pointer of any other type. So, say, a might be pointing to a pointer, but was cast to a different type. Or a might have a type of "pointing to a pointer", but actually isn't pointing to one.
P.S. Sorry for using the verb "point" so freely.
... we need to know at compile-time the order of the pointer when we are declaring it.
Yes it is possible at compile time to determine the order of the pointer type being declared; based on the type (possibly via typedef) or the variable, if C++11 can be used (via decltype()).
#include <iostream>
using namespace std;
template <typename P>
struct ptr_order
{
static const int order = 0;
};
template <typename P>
struct ptr_order<P*>
{
static const int order = ptr_order<P>::order + 1;
};
int main()
{
typedef int*** pointer;
cout << ptr_order<pointer>::order << endl; // outputs 3
// could also use decltype if available...
// int*** p;
// ptr_order<decltype(p)>::order is also 3
}
The runtime calculation of the order of the pointer is not possible, since C++ is statically typed.
I need a safe way to alias between arbitrary POD types, conforming to ISO-C++11 explicitly considering 3.10/10 and 3.11 of n3242 or later.
There are a lot of questions about strict aliasing here, most of them regarding C and not C++. I found a "solution" for C which uses unions, probably using this section
union type that includes one of the aforementioned types among its
elements or nonstatic data members
From that I built this.
#include <iostream>
template <typename T, typename U>
T& access_as(U* p)
{
union dummy_union
{
U dummy;
T destination;
};
dummy_union* u = (dummy_union*)p;
return u->destination;
}
struct test
{
short s;
int i;
};
int main()
{
int buf[2];
static_assert(sizeof(buf) >= sizeof(double), "");
static_assert(sizeof(buf) >= sizeof(test), "");
access_as<double>(buf) = 42.1337;
std::cout << access_as<double>(buf) << '\n';
access_as<test>(buf).s = 42;
access_as<test>(buf).i = 1234;
std::cout << access_as<test>(buf).s << '\n';
std::cout << access_as<test>(buf).i << '\n';
}
My question is, just to be sure, is this program legal according to the standard?*
It doesn't give any warnings whatsoever and works fine when compiling with MinGW/GCC 4.6.2 using:
g++ -std=c++0x -Wall -Wextra -O3 -fstrict-aliasing -o alias.exe alias.cpp
* Edit: And if not, how could one modify this to be legal?
This will never be legal, no matter what kind of contortions you perform with weird casts and unions and whatnot.
The fundamental fact is this: two objects of different type may never alias in memory, with a few special exceptions (see further down).
Example
Consider the following code:
void sum(double& out, float* in, int count) {
for(int i = 0; i < count; ++i) {
out += *in++;
}
}
Let's break that out into local register variables to model actual execution more closely:
void sum(double& out, float* in, int count) {
for(int i = 0; i < count; ++i) {
register double out_val = out; // (1)
register double in_val = *in; // (2)
register double tmp = out_val + in_val;
out = tmp; // (3)
in++;
}
}
Suppose that (1), (2) and (3) represent a memory read, read and write, respectively, which can be very expensive operations in such a tight inner loop. A reasonable optimization for this loop would be the following:
void sum(double& out, float* in, int count) {
register double tmp = out; // (1)
for(int i = 0; i < count; ++i) {
register double in_val = *in; // (2)
tmp = tmp + in_val;
in++;
}
out = tmp; // (3)
}
This optimization reduces the number of memory reads needed by half and the number of memory writes to 1. This can have a huge impact on the performance of the code and is a very important optimization for all optimizing C and C++ compilers.
Now, suppose that we don't have strict aliasing. Suppose that a write to an object of any type can affect any other object. Suppose that writing to a double can affect the value of a float somewhere. This makes the above optimization suspect, because it's possible the programmer has in fact intended for out and in to alias so that the sum function's result is more complicated and is affected by the process. Sounds stupid? Even so, the compiler cannot distinguish between "stupid" and "smart" code. The compiler can only distinguish between well-formed and ill-formed code. If we allow free aliasing, then the compiler must be conservative in its optimizations and must perform the extra store (3) in each iteration of the loop.
Hopefully you can see now why no such union or cast trick can possibly be legal. You cannot circumvent fundamental concepts like this by sleight of hand.
Exceptions to strict aliasing
The C and C++ standards make special provision for aliasing any type with char, and with any "related type" which among others includes derived and base types, and members, because being able to use the address of a class member independently is so important. You can find an exhaustive list of these provisions in this answer.
Furthermore, GCC makes special provision for reading from a different member of a union than what was last written to. Note that this kind of conversion-through-union does not in fact allow you to violate aliasing. Only one member of a union is allowed to be active at any one time, so for example, even with GCC the following would be undefined behavior:
union {
double d;
float f[2];
};
f[0] = 3.0f;
f[1] = 5.0f;
sum(d, f, 2); // UB: attempt to treat two members of
// a union as simultaneously active
Workarounds
The only standard way to reinterpret the bits of one object as the bits of an object of some other type is to use an equivalent of memcpy. This makes use of the special provision for aliasing with char objects, in effect allowing you to read and modify the underlying object representation at the byte level. For example, the following is legal, and does not violate strict aliasing rules:
int a[2];
double d;
static_assert(sizeof(a) == sizeof(d));
memcpy(a, &d, sizeof(d));
This is semantically equivalent to the following code:
int a[2];
double d;
static_assert(sizeof(a) == sizeof(d));
for(size_t i = 0; i < sizeof(a); ++i)
((char*)a)[i] = ((char*)&d)[i];
GCC makes a provision for reading from an inactive union member, implicitly making it active. From the GCC documentation:
The practice of reading from a different union member than the one most recently written to (called “type-punning”) is common. Even with -fstrict-aliasing, type-punning is allowed, provided the memory is accessed through the union type. So, the code above will work as expected. See Structures unions enumerations and bit-fields implementation. However, this code might not:
int f() {
union a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}
Similarly, access by taking the address, casting the resulting pointer and dereferencing the result has undefined behavior, even if the cast uses a union type, e.g.:
int f() {
double d = 3.0;
return ((union a_union *) &d)->i;
}
Placement new
(Note: I'm going by memory here as I don't have access to the standard right now).
Once you placement-new an object into a storage buffer, the lifetime of the underlying storage objects ends implicitly. This is similar to what happens when you write to a member of a union:
union {
int i;
float f;
} u;
// No member of u is active. Neither i nor f refer to an lvalue of any type.
u.i = 5;
// The member u.i is now active, and there exists an lvalue (object)
// of type int with the value 5. No float object exists.
u.f = 5.0f;
// The member u.i is no longer active,
// as its lifetime has ended with the assignment.
// The member u.f is now active, and there exists an lvalue (object)
// of type float with the value 5.0f. No int object exists.
Now, let's look at something similar with placement-new:
#define MAX_(x, y) ((x) > (y) ? (x) : (y))
// new returns suitably aligned memory
char* buffer = new char[MAX_(sizeof(int), sizeof(float))];
// Currently, only char objects exist in the buffer.
new (buffer) int(5);
// An object of type int has been constructed in the memory pointed to by buffer,
// implicitly ending the lifetime of the underlying storage objects.
new (buffer) float(5.0f);
// An object of type int has been constructed in the memory pointed to by buffer,
// implicitly ending the lifetime of the int object that previously occupied the same memory.
This kind of implicit end-of-lifetime can only occur for types with trivial constructors and destructors, for obvious reasons.
Aside from the error when sizeof(T) > sizeof(U), the problem there could be, that the union has an appropriate and possibly higher alignment than U, because of T.
If you don't instantiate this union, so that its memory block is aligned (and large enough!) and then fetch the member with destination type T, it will break silently in the worst case.
For example, an alignment error occurs, if you do the C-style cast of U*, where U requires 4 bytes alignment, to dummy_union*, where dummy_union requires alignment to 8 bytes, because alignof(T) == 8. After that, you possibly read the union member with type T aligned at 4 instead of 8 bytes.
Alias cast (alignment & size safe reinterpret_cast for PODs only):
This proposal does explicitly violate strict aliasing, but with static assertions:
///#brief Compile time checked reinterpret_cast where destAlign <= srcAlign && destSize <= srcSize
template<typename _TargetPtrType, typename _ArgType>
inline _TargetPtrType alias_cast(_ArgType* const ptr)
{
//assert argument alignment at runtime in debug builds
assert(uintptr_t(ptr) % alignof(_ArgType) == 0);
typedef typename std::tr1::remove_pointer<_TargetPtrType>::type target_type;
static_assert(std::tr1::is_pointer<_TargetPtrType>::value && std::tr1::is_pod<target_type>::value, "Target type must be a pointer to POD");
static_assert(std::tr1::is_pod<_ArgType>::value, "Argument must point to POD");
static_assert(std::tr1::is_const<_ArgType>::value ? std::tr1::is_const<target_type>::value : true, "const argument must be cast to const target type");
static_assert(alignof(_ArgType) % alignof(target_type) == 0, "Target alignment must be <= source alignment");
static_assert(sizeof(_ArgType) >= sizeof(target_type), "Target size must be <= source size");
//reinterpret cast doesn't remove a const qualifier either
return reinterpret_cast<_TargetPtrType>(ptr);
}
Usage with pointer type argument ( like standard cast operators such as reinterpret_cast ):
int* x = alias_cast<int*>(any_ptr);
Another approach (circumvents alignment and aliasing issues using a temporary union):
template<typename ReturnType, typename ArgType>
inline ReturnType alias_value(const ArgType& x)
{
//test argument alignment at runtime in debug builds
assert(uintptr_t(&x) % alignof(ArgType) == 0);
static_assert(!std::tr1::is_pointer<ReturnType>::value ? !std::tr1::is_const<ReturnType>::value : true, "Target type can't be a const value type");
static_assert(std::tr1::is_pod<ReturnType>::value, "Target type must be POD");
static_assert(std::tr1::is_pod<ArgType>::value, "Argument must be of POD type");
//assure, that we don't read garbage
static_assert(sizeof(ReturnType) <= sizeof(ArgType),"Target size must be <= argument size");
union dummy_union
{
ArgType x;
ReturnType r;
};
dummy_union dummy;
dummy.x = x;
return dummy.r;
}
Usage:
struct characters
{
char c[5];
};
//.....
characters chars;
chars.c[0] = 'a';
chars.c[1] = 'b';
chars.c[2] = 'c';
chars.c[3] = 'd';
chars.c[4] = '\0';
int r = alias_value<int>(chars);
The disadvantage of this is, that the union may require more memory than actually needed for the ReturnType
Wrapped memcpy (circumvents alignment and aliasing issues using memcpy):
template<typename ReturnType, typename ArgType>
inline ReturnType alias_value(const ArgType& x)
{
//assert argument alignment at runtime in debug builds
assert(uintptr_t(&x) % alignof(ArgType) == 0);
static_assert(!std::tr1::is_pointer<ReturnType>::value ? !std::tr1::is_const<ReturnType>::value : true, "Target type can't be a const value type");
static_assert(std::tr1::is_pod<ReturnType>::value, "Target type must be POD");
static_assert(std::tr1::is_pod<ArgType>::value, "Argument must be of POD type");
//assure, that we don't read garbage
static_assert(sizeof(ReturnType) <= sizeof(ArgType),"Target size must be <= argument size");
ReturnType r;
memcpy(&r,&x,sizeof(ReturnType));
return r;
}
For dynamic sized arrays of any POD type:
template<typename ReturnType, typename ElementType>
ReturnType alias_value(const ElementType* const array,const size_t size)
{
//assert argument alignment at runtime in debug builds
assert(uintptr_t(array) % alignof(ElementType) == 0);
static const size_t min_element_count = (sizeof(ReturnType) / sizeof(ElementType)) + (sizeof(ReturnType) % sizeof(ElementType) != 0 ? 1 : 0);
static_assert(!std::tr1::is_pointer<ReturnType>::value ? !std::tr1::is_const<ReturnType>::value : true, "Target type can't be a const value type");
static_assert(std::tr1::is_pod<ReturnType>::value, "Target type must be POD");
static_assert(std::tr1::is_pod<ElementType>::value, "Array elements must be of POD type");
//check for minimum element count in array
if(size < min_element_count)
throw std::invalid_argument("insufficient array size");
ReturnType r;
memcpy(&r,array,sizeof(ReturnType));
return r;
}
More efficient approaches may do explicit unaligned reads with intrinsics, like the ones from SSE, to extract primitives.
Examples:
struct sample_struct
{
char c[4];
int _aligner;
};
int test(void)
{
const sample_struct constPOD = {};
sample_struct pod = {};
const char* str = "abcd";
const int* constIntPtr = alias_cast<const int*>(&constPOD);
void* voidPtr = alias_value<void*>(pod);
int intValue = alias_value<int>(str,strlen(str));
return 0;
}
EDITS:
Assertions to assure conversion of PODs only, may be improved.
Removed superfluous template helpers, now using tr1 traits only
Static assertions for clarification and prohibition of const value (non-pointer) return type
Runtime assertions for debug builds
Added const qualifiers to some function arguments
Another type punning function using memcpy
Refactoring
Small example
I think that at the most fundamental level, this is impossible and violates strict aliasing. The only thing you've achieved is tricking the compiler into not noticing.
My question is, just to be sure, is this program legal according to the standard?
No. The alignment may be unnatural using the alias you have provided. The union you wrote just moves the point of the alias. It may appear to work, but that program may fail when CPU options, ABI, or compiler settings change.
And if not, how could one modify this to be legal?
Create natural temporary variables and treat your storage as a memory blob (moving in and out of the blob to/from temporaries), or use a union which represents all your types (remember, one active element at a time here).
Assume that in my code I have to store a void* as data member and typecast it back to the original class pointer when needed. To test its reliability, I wrote a test program (linux ubuntu 4.4.1 g++ -04 -Wall) and I was shocked to see the behavior.
struct A
{
int i;
static int c;
A () : i(c++) { cout<<"A() : i("<<i<<")\n"; }
};
int A::c;
int main ()
{
void *p = new A[3]; // good behavior for A* p = new A[3];
cout<<"p->i = "<<((A*)p)->i<<endl;
((A*&)p)++;
cout<<"p->i = "<<((A*)p)->i<<endl;
((A*&)p)++;
cout<<"p->i = "<<((A*)p)->i<<endl;
}
This is just a test program; in actual for my case, it's mandatory to store any pointer as void* and then cast it back to the actual pointer (with help of template). So let's not worry about that part. The output of the above code is,
p->i = 0
p->i = 0 // ?? why not 1
p->i = 1
However if you change the void* p; to A* p; it gives expected behavior. WHY ?
Another question, I cannot get away with (A*&) otherwise I cannot use operator ++; but it also gives warning as, dereferencing type-punned pointer will break strict-aliasing rules. Is there any decent way to overcome warning ?
Well, as the compiler warns you, you are violating the strict aliasing rule, which formally means that the results are undefined.
You can eliminate the strict aliasing violation by using a function template for the increment:
template<typename T>
void advance_pointer_as(void*& p, int n = 1) {
T* p_a(static_cast<T*>(p));
p_a += n;
p = p_a;
}
With this function template, the following definition of main() yields the expected results on the Ideone compiler (and emits no warnings):
int main()
{
void* p = new A[3];
std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl;
advance_pointer_as<A>(p);
std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl;
advance_pointer_as<A>(p);
std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl;
}
You have already received the correct answer and it is indeed the violation of the strict aliasing rule that leads to the unpredictable behavior of the code. I'd just note that the title of your question makes reference to "casting back pointer to the original class". In reality your code does not have anything to do with casting anything "back". Your code performs reinterpretation of raw memory content occupied by a void * pointer as a A * pointer. This is not "casting back". This is reinterpretation. Not even remotely the same thing.
A good way to illustrate the difference would be to use and int and float example. A float value declared and initialized as
float f = 2.0;
cab be cast (explicitly or implicitly converted) to int type
int i = (int) f;
with the expected result
assert(i == 2);
This is indeed a cast (a conversion).
Alternatively, the same float value can be also reinterpreted as an int value
int i = (int &) f;
However, in this case the value of i will be totally meaningless and generally unpredictable. I hope it is easy to see the difference between a conversion and a memory reinterpretation from these examples.
Reinterpretation is exactly what you are doing in your code. The (A *&) p expression is nothing else than a reinterpretation of raw memory occupied by pointer void *p as pointer of type A *. The language does not guarantee that these two pointer types have the same representation and even the same size. So, expecting the predictable behavior from your code is like expecting the above (int &) f expression to evaluate to 2.
The proper way to really "cast back" your void * pointer would be to do (A *) p, not (A *&) p. The result of (A *) p would indeed be the original pointer value, that can be safely manipulated by pointer arithmetic. The only proper way to obtain the original value as an lvalue would be to use an additional variable
A *pa = (A *) p;
...
pa++;
...
And there's no legal way to create an lvalue "in place", as you attempted to by your (A *&) p cast. The behavior of your code is an illustration of that.
As others have commented, your code appears like it should work. Only once (in 17+ years of coding in C++) I ran across something where I was looking straight at the code and the behavior, like in your case, just didn't make sense. I ended up running the code through debugger and opening a disassembly window. I found what could only be explained as a bug in VS2003 compiler because it was missing exactly one instruction. Simply rearranging local variables at the top of the function (30 lines or so from the error) made the compiler put the correct instruction back in. So try debugger with disassembly and follow memory/registers to see what it's actually doing?
As far as advancing the pointer, you should be able to advance it by doing:
p = (char*)p + sizeof( A );
VS2003 through VS2010 never give you complaints about that, not sure about g++
I've been reading about strict aliasing quite a lot lately. The C/C++ standards say that the following code is invalid (undefined behavior to be correct), since the compiler might have the value of a cached somewhere and would not recognize that it needs to update the value when I update b;
float *a;
...
int *b = reinterpret_cast<int*>(a);
*b = 1;
The standard also says that char* can alias anything, so (correct me if I'm wrong) compiler would reload all cached values whenever a write access to a char* variable is made. Thus the following code would be correct:
float *a;
...
char *b = reinterpret_cast<char*>(a);
*b = 1;
But what about the cases when pointers are not involved at all? For example, I have the following code, and GCC throws warnings about strict aliasing at me.
float a = 2.4;
int32_t b = reinterpret_cast<int&>(a);
What I want to do is just to copy raw value of a, so strict aliasing shouldn't apply. Is there a possible problem here, or just GCC is overly cautious about that?
EDIT
I know there's a solution using memcpy, but it results in code that is much less readable, so I would like not to use that solution.
EDIT2
int32_t b = *reinterpret_cast<int*>(&a); also does not work.
SOLVED
This seems to be a bug in GCC.
If you want to copy some memory, you could just tell the compiler to do that:
Edit: added a function for more readable code:
#include <iostream>
using std::cout; using std::endl;
#include <string.h>
template <class T, class U>
T memcpy(const U& source)
{
T temp;
memcpy(&temp, &source, sizeof(temp));
return temp;
}
int main()
{
float f = 4.2;
cout << "f: " << f << endl;
int i = memcpy<int>(f);
cout << "i: " << i << endl;
}
[Code]
[Updated Code]
Edit: As user/GMan correctly pointed out in the comments, a full-featured implementation could check that T and U are PODs. However, given that the name of the function is still memcpy, it might be OK to rely on your developers treating it as having the same constraints as the original memcpy. That's up to your organization. Also, use the size of the destination, not the source. (Thanks, Oli.)
Basically the strict aliasing rules is "it is undefined to access memory with another type than its declared one, excepted as array of characters". So, gcc isn't overcautious.
If this is something you need to do often, you can also just use a union, which IMHO is more readable than casting or memcpy for this specific purpose:
union floatIntUnion {
float a;
int32_t b;
};
int main() {
floatIntUnion fiu;
fiu.a = 2.4;
int32_t &x = fiu.b;
cout << x << endl;
}
I realize that this doesn't really answer your question about strict-aliasing, but I think this method makes the code look cleaner and shows your intent better.
And also realize that even doing the copies correctly, there is no guarantee that the int you get out will correspond to the same float on other platforms, so count any network/file I/O of these floats/ints out if you plan to create a cross-platform project.