patterncppMinor
Checking endianness at compile-time
Viewed 0 times
compilecheckingtimeendianness
Problem
I've tried to make a portable way of ensuring endian-specific code gets generated at compile time using C++11, however I only have a computer with Windows on it to test at the moment. Because of this, I'm a bit limited in the amount of places where I can test my code. Also, would anyone be able to offer some best-practices or tips different ways to improve this? My intention is to use this in a small math library where serialization is a pretty high priority.
The function itself is fairly simple. It checks the values of an array for whichever byte comes first. It then returns a constant value, representing the target machine's endianness, through an enumeration. If everything works correctly, then this code can replace any runtime checks that either I or anyone else uses for endian checking.
```
/*
* A simple compile-time endian test
* g++ -std=c++11 -Wall -Werror -Wextra -pedantic -pedantic-errors endian.cpp -o endian
*
* This can be used with specialized template functions, classes, and class
* methods in order better tailor code and reduce reliance on runtime
* checking systems.
*/
#include
#include
/**
* hl_endianness
*
* This enumeration can be placed into templated objects in order to generate
* compile-time code based on a program's target endianness.
*
* The values placed in this enum are used just in case the need arises in
* order to manually compare them against the number order in the
* endianValues[] array.
*/
enum hl_endianness : uint32_t {
HL_LITTLE_ENDIAN = 0x03020100,
HL_BIG_ENDIAN = 0x00010203,
HL_PDP_ENDIAN = 0x01000302,
HL_UNKNOWN_ENDIAN = 0xFFFFFFFF
};
/**
* A constant array used to determine a program's target endianness. The
* values
* in this array can be compared against the values placed in the
* hl_endianness enumeration.
*/
static constexpr uint8_t endianValues[4] = {0, 1, 2, 3};
/**
* A simple function that can be used to help determine a program's endianness
* at compile-time
The function itself is fairly simple. It checks the values of an array for whichever byte comes first. It then returns a constant value, representing the target machine's endianness, through an enumeration. If everything works correctly, then this code can replace any runtime checks that either I or anyone else uses for endian checking.
```
/*
* A simple compile-time endian test
* g++ -std=c++11 -Wall -Werror -Wextra -pedantic -pedantic-errors endian.cpp -o endian
*
* This can be used with specialized template functions, classes, and class
* methods in order better tailor code and reduce reliance on runtime
* checking systems.
*/
#include
#include
/**
* hl_endianness
*
* This enumeration can be placed into templated objects in order to generate
* compile-time code based on a program's target endianness.
*
* The values placed in this enum are used just in case the need arises in
* order to manually compare them against the number order in the
* endianValues[] array.
*/
enum hl_endianness : uint32_t {
HL_LITTLE_ENDIAN = 0x03020100,
HL_BIG_ENDIAN = 0x00010203,
HL_PDP_ENDIAN = 0x01000302,
HL_UNKNOWN_ENDIAN = 0xFFFFFFFF
};
/**
* A constant array used to determine a program's target endianness. The
* values
* in this array can be compared against the values placed in the
* hl_endianness enumeration.
*/
static constexpr uint8_t endianValues[4] = {0, 1, 2, 3};
/**
* A simple function that can be used to help determine a program's endianness
* at compile-time
Solution
Just a couple points:
-
Endian-ness is not generally based on the Operating System but on the processor. For example, Intel x86 processors are little-endian regardless of it running Windows or Linux.
-
Your code will always return
then
Don't you think it would be shocking if
The standard way is to use a union. Something like this:
Be aware that
-
Not clear to me why you need to use fancy values for
-
A related question answered on Stack Overflow (Detecting endianness programmatically in a C++ program)
-
Endian-ness is not generally based on the Operating System but on the processor. For example, Intel x86 processors are little-endian regardless of it running Windows or Linux.
-
Your code will always return
HL_LITTLE_ENDIAN. Why? Because ifstatic constexpr uint8_t endianValues[4] = {0, 1, 2, 3};then
endianValues[0] == 0 will always be true! Suppose you hadchar x[4] = {'c','o','d', 'e'};Don't you think it would be shocking if
x[0] == 'e' instead of x[0] == 'c'?The standard way is to use a union. Something like this:
union endian_tester {
uint32_t n;
uint8_t p[4];
};
const endian_tester sample = {0x01020304}; // this initializes .n
constexpr hl_endianness getEndianOrder() {
return
(0x04 == sample.p[0]) // If Little Endian Byte Order,
? HL_LITTLE_ENDIAN
: (0x01 == sample.p[0]) // Else if Big Endian Byte Order,
? HL_BIG_ENDIAN
: (0x02 == sample.p[0]) // Else if PDP Endian Byte Order,
...(etc)...Be aware that
constexpr isn't fully supported in my version of Visual Studio 2013 Express.-
Not clear to me why you need to use fancy values for
HL_LITTLE_ENDIAN, HL_BIG_ENDIAN, etc. You can use 1, 2, etc instead of 0x03020100, 0x00010203, etc.-
A related question answered on Stack Overflow (Detecting endianness programmatically in a C++ program)
Code Snippets
static constexpr uint8_t endianValues[4] = {0, 1, 2, 3};char x[4] = {'c','o','d', 'e'};union endian_tester {
uint32_t n;
uint8_t p[4];
};
const endian_tester sample = {0x01020304}; // this initializes .n
constexpr hl_endianness getEndianOrder() {
return
(0x04 == sample.p[0]) // If Little Endian Byte Order,
? HL_LITTLE_ENDIAN
: (0x01 == sample.p[0]) // Else if Big Endian Byte Order,
? HL_BIG_ENDIAN
: (0x02 == sample.p[0]) // Else if PDP Endian Byte Order,
...(etc)...Context
StackExchange Code Review Q#45675, answer score: 4
Revisions (0)
No revisions yet.