Issue
The (legacy) code looks roughly like this.
#define MAKEID(a,b,c,d) (((UInt32)a)<<24 | ((UInt32)b)<<16
| ((UInt32)c)<<8 | ((UInt32)d) )
#define ID_FORM MAKEID('F','O','R','M')
//...
struct S {
int id;
int x;
double d;
// other stuff
};
//...
S s;
socket.read(&s, sizeof(s)); // read network data over struct
if (s.id == ID_FORM) { }
The code reads a stream of characters into the struct. The S.id is known to be a 4 character constant such as 'FORM' or 'DATA' in stream (network) order, which determines the layout of the rest of the struct. All comparisons are integer using predefined constants.
The MAKEID macro is big-endian because it places the first (character) argument at the most significant byte, which is also the lowest memory address. A little endian version of the macro would look like this, placing the first (character) argument at the least significant byte, which is now the lowest memory address.
#define MAKEID(a,b,c,d) (((UInt32)d)<<24 | ((UInt32)c)<<16
| ((UInt32)b)<<8 | ((UInt32)a) )
The question is how to rewrite it so that it works equally well on big-endian and little-endian architectures.
No, I do not want to write both macros and choose which one with an #ifdef. There is no other endian dependency anywhere in the code and I'm not keen to introduce one here. Portable is the way to go.
No, I do not want to write a function. This constant is used in places where a function cannot go. I wrote a portable function that initialised a union, and the code won't compile.
Any kind of portable macro or template to do compile-time initialisation is what I'm looking for.
In answer to a comment, this is the real code. It's part of a network protocol and the other end takes care of the endianism in most cases. This happens to be an exception where the other end generates in network byte order and this end was historically written big-endian as a 4 byte character constant like 'FORM'. I need a point solution and not one that propagates the idea of endianism to elsewhere in the code.
Solution
I set this same question to the programmers who work for me. Between us we came up with the following 4 solutions. We shall go with the macro, and perhaps convert the code over to use one of the functions as time permits.
unsigned int MakeId(char a, char b, char c, char d) {
char x[4] = { a, b, c, d };
return *(int*)x;
}
unsigned int MakeId(char a, char b, char c, char d) {
union {
char x[4];
int i;
} u = { a, b, c, d };
return u.i;
}
unsigned int MakeId(const char* s) { return *(int*)s; }
#define MAKEID(s) *(int*)(s);
#define FORM_ID MAKEID("Form")
On this occasion the formidable minds of Stack Overflow did not deliver.
Answered By - david.pfx Answer Checked By - Timothy Miller (PHPFixing Admin)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.