Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr and endianness

A common question that comes up from time to time in the world of C++ programming is compile-time determination of endianness. Usually this is done with barely portable #ifdefs. But does the C++11 constexpr keyword along with template specialization offer us a better solution to this?

Would it be legal C++11 to do something like:

constexpr bool little_endian() {    const static unsigned num = 0xAABBCCDD;    return reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD; } 

And then specialize a template for both endian types:

template <bool LittleEndian> struct Foo  {   // .... specialization for little endian };  template <> struct Foo<false> {   // .... specialization for big endian }; 

And then do:

Foo<little_endian()>::do_something(); 
like image 406
Charles Salvia Avatar asked Oct 18 '09 02:10

Charles Salvia


People also ask

What is constexpr used for?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

Should I use constexpr or const?

const applies for variables, and prevents them from being modified in your code. constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc.

Is constexpr function static?

constexpr int a = 2; Static specifies the lifetime of the variable. A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.

Does rust have constexpr?

Basically C++'s constexpr is Rust's const , which can be directly defined within a block (no need for the callable trick).


1 Answers

New answer (C++20)

c++20 has introduced a new standard library header <bit>. Among other things it provides a clean, portable way to check the endianness.

Since my old method relies on some questionable techniques, I suggest anyone who uses it to switch to the check provided by the standard library.

Here's an adapter which allows to use the new way of checking endianness without having to update the code that relies on the interface of my old class:

#include <bit>  class Endian { public:     Endian() = delete;      static constexpr bool little = std::endian::native == std::endian::little;     static constexpr bool big = std::endian::native == std::endian::big;     static constexpr bool middle = !little && !big; }; 

Old answer

I was able to write this:

#include <cstdint>  class Endian { private:     static constexpr uint32_t uint32_ = 0x01020304;     static constexpr uint8_t magic_ = (const uint8_t&)uint32_; public:     static constexpr bool little = magic_ == 0x04;     static constexpr bool middle = magic_ == 0x02;     static constexpr bool big = magic_ == 0x01;     static_assert(little || middle || big, "Cannot determine endianness!"); private:     Endian() = delete; }; 

I've tested it with g++ and it compiles without warnings. It gives a correct result on x64. If you have any big-endian or middle-endian proccesor, please, confirm that this works for you in a comment.

like image 173
NO_NAME Avatar answered Sep 17 '22 13:09

NO_NAME