Easy: use a pointer to void.
Guessing you didn't read the whole thread above. Turns out that people seem to have VERY strong opinions as to the use of void pointers to allow you to cast between incompatible types.
I agree there are strong opinions, but for good cause.
Aggression and arguments shouldn't be necessary; this isn't opinion; it is fact. And to follow analogies, its similar to arguing: Its legal to drive across the traffic lights while they are red, simply because you can see no cars.
If you are not considering why we posted our concerns, how about asking for a proof why it is safe. C/C++ is built upon a set of rules; if it is allowed, the standard will state it.
I have been doing some reading of my own and I feel that the void pointer is the best way for me to achieve what I want as I want to totally disregard the type of data and just see it as a stream of bytes.
Good decision.
Not a good decision, this is also wrong, as stated before. Arranged into a different scenario, still, the code below is not valid. There are no if's, or but's, it is just how C++ rolls.
void PrintIntFromStream( void *v_Stream ){
int *i_StreamPtr = ( int* ) v_Stream;
Serial.print( "Int aliased from stream: " );
Serial.println( *i_StreamPtr );
return;
}
If you read my post above, I mentioned casting directly to char* is fine, because it is allowed and would fix your code. However the void* breaks the
strict aliasing rule and therefore subsequent casts are
undefined behaviour.
A large part of the problem is the
strict aliasing rule is an addition to the standard, therefore material based on the standard before the addition are rampant throughout the net. Also it is interpreted differently between C and C++, so trying to port broken ideas in C only make things worse in C++.
Take a moment to read this:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a sub aggregate or contained union), or
— a character type.
This is an actual excerpt from the standard, if you managed to grasp all that, you may have noticed the last line. This is the rule on which I based my fix for your function. The one basic aliasing tool is char, for reasons such as endianess and alignment ( there is method in their madness ).
There is nothing in that list that explicitly mentions a cast to incompatible types
including void* types. The reason your code works ( if it does ) like my example above, is it relies on undefined behaviour. What that actually means is, the code you have written has not met any conditions for optimisations that rely on the
strict aliasing rule. When one can be applied the compiler is free to disregard interactions with aliased data. I say 'disregard' with a notion that there are some circumstances where you reverse the optimising capacity of the compiler.
You may say, "'optimisations', this isn't part of the discussion", but guess what, the
strict aliasing rule is pretty much specifically for them. This is why you don't get aliasing compile errors, as some of the optimisations are linker based, after the compiler has done its thing. This rule actually matters!!!
For clarity:
Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)
Now for the fun part.
Using Arduino IDE 1.5
- AVR Compiler version 4.3.2
- Optimisation flags 'Os'
This flag is the compile for size option ( i.e ) removing dead code. It also includes all 'O2' optimisations which include... <drum roll>
-fstrict-aliasing compiler specification which enforces the notion that you obey the rule. The only way to deal with aliased incompatible types safely is through non-portable attributes... Or use C++ properly.