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 <cstdint>
#include <iostream>
/**
* 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.
*/
constexpr hl_endianness getEndianOrder() {
return
(0x00 == endianValues[0]) // If Little Endian Byte Order,
? HL_LITTLE_ENDIAN // return 0 for little endian.
: (0x03 == endianValues[0]) // Else if Big Endian Byte Order,
? HL_BIG_ENDIAN // return 1 for big endian.
: (0x02 == endianValues[0]) // Else if PDP Endian Byte Order,
? HL_PDP_ENDIAN // return 2 for pdp endian.
: HL_UNKNOWN_ENDIAN; // Else return -1 for wtf endian.
}
#define HL_ENDIANNESS getEndianOrder()
/*
* Test program
*/
int main() {
#if defined _WIN32 || defined _WIN64
static_assert(
HL_ENDIANNESS == HL_LITTLE_ENDIAN,
"Aren't Windows programs Little-Endian?"
);
#endif
constexpr hl_endianness endianness = HL_ENDIANNESS;
std::cout << "This machine is: ";
switch (endianness) {
case HL_LITTLE_ENDIAN:
std::cout << "LITTLE";
break;
case HL_BIG_ENDIAN:
std::cout << "BIG";
break;
case HL_PDP_ENDIAN:
std::cout << "PDP";
break;
case HL_UNKNOWN_ENDIAN:
default:
std::cout << "UNKNOWN";
}
std::cout << " endian" << std::endl;
}
Edit:
I can try dereferencing the pointer offset to the endianValues array but I'm still not sure if it will end up defaulting to 0 (the first explicitly set value in the array).
constexpr hl_endianness getEndianOrder() {
return
(0x00 == *endianValues) // If Little Endian Byte Order,
? HL_LITTLE_ENDIAN // return 0 for little endian.
: (0x03 == *endianValues) // Else if Big Endian Byte Order,
? HL_BIG_ENDIAN // return 1 for big endian.
: (0x02 == *endianValues) // Else if PDP Endian Byte Order,
? HL_PDP_ENDIAN // return 2 for pdp endian.
: HL_UNKNOWN_ENDIAN; // Else return -1 for wtf endian.
}