Go Down

Topic: A function to handle multiple datatypes (Read 6 times) previous topic - next topic

michael_x

#30
Nov 02, 2012, 12:10 pm Last Edit: Nov 02, 2012, 12:13 pm by michael_x Reason: 1

Just one other quick question:
How might I modify the above code to send the bytes MSB first?


You can modify any part of your for ( ; ; ) { }  construct. ?!
Or do you want to "turn around" every single byte bit by bit  ? ( MSB = Most Significant Bit  ? )  

Edit: luckily there are no smileys containing space characters

Chris Parish


Or do you want to "turn around" every single byte bit by bit


Ignore the whole thing about MSB first. It isn't relevant. My mistake.

Quote
Honestly, this is why you shouldn't fiddle with pointers if you don't understand what you are doing.


Whilst I will happily admit that my knowledge of pointers is far from encyclopaedic the code to which you refer is a simple bit wise XOR operation.
The whole point of the bit stuffing is to allow me to use a customised data packet for transmission over the serial port. The packet uses the value 0x7E to signify the start and the finish of the packet. However, this means that if the packet payload data contains the value 0x7E then we have to modify it so that the receiver does not think it had reached the end of the packet, thereby causing data loss. So what we do is to replace 0x7E with 0x7D and then follow it with the original value XOR'd with 0x20 giving 0x5E. We also follow any other occurrence of 0x7D with 0x7D XOR'd with 0x20.
Upon receiving the packet any occurrence of 0x7D is discarded and the following byte XOR'd with 0x20 to obtain the original value, thereby "unstuffing" the bytes.
Honestly, this is why you shouldn't fiddle with bitwise operations if you don't understand what you're doing.  ;)

pYro_65



The point is, you don't want to defeat type checking.
Yes. Sometimes you do.

Why do you think compiler-writers built that in?
Equally, why do you think they built in void pointer?

Type checking is very useful, a great invention. Sometimes however, it is nice to be able to side-step it if it gets in your way. C allows that.


Nick's advice is sound. It is a serious flaw expecting C++ behaviour to reflect that of C. You do not want to break type checking by using a void*, there are uses for these types but destroying type safety is not one of them.

The strict aliasing rule must be adhered to, you cannot assume your compiler ( or even subsequent versions of it ) will always provide working 'undefined behaviour'.

Even in the function by Chris Parish,

Code: [Select]
const byte* p = (const byte*)(const void*)&value;

In this line, the void* cast explicitly breaks the strict aliasing rule, whereas simply casting to char* ( or byte* )  wouldn't.

pico


You do not want to break type checking by using a void*, there are uses for these types but destroying type safety is not one of them.


Well sorry, but actually, I do want to break type checking in some cases. And I do. And that is precisely what void* is for, both in C and C++ -- to work with a pointer without a type.

As for "type safety", as we have already discussed, whether or not the gun is "safe" or not depends on whether you can shoot straight.

C is a sharp language. Assembly even sharper. When dealing with sharp objects, it pays to know what you are doing, and be to be careful. Otherwise, accidents can and will happen.

But if sharp objects make you nervous, you can always choose to use the safety scissors instead. Choices are good.
WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

jraskell


Well whist you guys were fighting I tried this and it seems to work. Thanks

Code: [Select]

template <class T> void sendAnything(const T& value)
{
   const byte* p = (const byte*)(const void*)&value;
   unsigned int i;
   for (i = 0; i < sizeof(value); i++) {
     if (*p == 0x7E || *p == 0x7D) { //byte stuffing
       Serial.write(0x7D);
       Serial.write(*p++ ^ 0x20);
     } else {
       Serial.write(*p++);
     }
   }
}



Casting a template argument in a generic function to a void pointer is a bad practice.  You've created a function that allegedly sends anything.  It will work with the intrinsic types and it will work with some class types, but it will definitely not send anything (although it will always send something, whether it makes sense or not).

The question that begs asking is, what data type are you attempting to transmit that requires casting to a void pointer (and then to a byte pointer)?

Your template function should be relying on either an overloaded Serial.write() method for sending the passed in data, or the user passing in the data in a form that is compatible with the Serial.write() method.  You're hiding a void cast inside a generic function.  Just to reiterate, bad practice.  

Your function would basically allow all sorts of nonsense code to compile.  ie:

sendAnything(Serial);
sendAnything(someComplexList);
etc.

A more appropriate function name would be sendAnythingViaVoid_EvenIfItDoesntMakeSense().

Quote
Well sorry, but actually, I do want to break type checking in some cases. And I do. And that is precisely what void* is for, both in C and C++ -- to work with a pointer without a type.


The cases to break type safety are when there are no better options available.  This is NOT one of those cases.

Quote
But if sharp objects make you nervous, you can always choose to use the safety scissors instead. Choices are good.

Choices are indeed good.  Veiled petty insults... another story.


Go Up