I'm toying with a general Bitmask class, based on Type-safe Bitmasks in C++, and here is a minimum example highligthning my issue (Compiler Explorer link here):
#include <type_traits>
#include <cstdint>
#include <iostream>
template<class EnumType,
// Ensure that Bitmask can only be used with enums
typename = std::enable_if_t<std::is_enum_v<EnumType>>>
class Bitmask
{
// Type to store bitmask. Should possibly be bigger
using underlying_type = std::underlying_type_t<EnumType>;
public:
constexpr Bitmask(EnumType option) : m_mask(bitmaskValue(option))
{std::cout << "Bitmask " << (int)m_mask << "\n";}
private:
// m_mask holds the underlying value, e.g. 2 to the power of enum value underlying_type
m_mask{0};
static constexpr underlying_type bitmaskValue(EnumType o)
{ return 1 << static_cast<underlying_type>(o); }
explicit constexpr Bitmask(underlying_type o) : m_mask(o) {}
};
enum class Option : uint8_t
{
a, b, c, d, e, f, g, h, i
};
enum class Option2 : int
{
a, b, c, d, e, f, g, h, i
};
int main()
{
Bitmask<Option> b1{Option::a};
Bitmask<Option> b2{Option::h};
Bitmask<Option> b3{Option::i};
Bitmask<Option2> b4{Option2::i};
}
// Output
Bitmask 1
Bitmask 128
Bitmask 0
Bitmask 256
In short, the Bitmask type works fine as long as the underlying type has more bits than the highest enum value used. If not, the bitmaskValue function will overflow and not give desired result, as shown in output, where b3 gets a value of 0, not 256.
I of course understand that an uint8_t cannot store more than 8 different bits, so what I want is some way to make this a compiler error, either when declaring Bitmask<Option>, or when instatiating a Bitmask with a too high value.
I tried changing the bitmaskValue method to:
static constexpr underlying_type bitmaskValue(EnumType o) {
if constexpr (std::numeric_limits<underlying_type>::digits >= static_cast<underlying_type>(o)) {
return 1 << static_cast<underlying_type>(o);
} else {
// some error
}
}
...but then I get the error that 'o' is not a constant expression.
Can anyone helpt me in the correct direction here? For the record, our project is currently using gcc 9.2/c++17, though I hope we can soon upgrade to gcc 11.1/c++20.
Thanks.
As Davis Herring already pointed out you cannot check for the overflow on a non-constant value at runtime (constexpr marked functions may also be executed at runtime). Thus to get the desired functionality one could check for the overflow inside the declaration of a template with the value to test as non-type template parameter.
The basic idea is that with brace initialization with a constant value, the compiler will generate a compilation error when the cast is narrowing, e.g. int8_t{128}; will fail since 128 cannot be represented with int8_t.
template<class EnumType>
class Bitmask
{
using underlying_type = std::underlying_type_t<EnumType>;
public:
explicit constexpr Bitmask(underlying_type v) : m_mask{v} {}
constexpr Bitmask(EnumType option) : m_mask{0x1 << static_cast<underlying_type>(option)}{}
template <EnumType option> // <- enum value as non-type template parameter
static constexpr auto make()
{
return Bitmask{underlying_type{0x1 << static_cast<underlying_type>(option)}};
}
underlying_type m_mask{0};
};
enum class Option : uint8_t
{
a, b, c, d, e, f, g, h, i
};
Bitmask<Option>::make<Option::f>(); // ok
Bitmask<Option>::make<Option::h>(); // ok
// Bitmask<Option>::make<Option::i>(); // will fail to build
https://godbolt.org/z/117baa4q4
If you control the enum definition as well, the simplest way is just to require an enumerated constant with a fixed name at the end (ie, so it is the largest real value + 1), such as EnumType::MAX_ENUM_VALUE.
Then you can refuse to instantiate if
EnumType::MAX_ENUM_VALUE > std::numeric_limits<underlying_type>::digits + 1
with a static assert, or an enable_if, or whatever. Obviously this will also fail to instantiate if the enumerated constant is missing, so it should be pretty obvious to the user how to fix it.
Note this isn't really a compile-time check for overflow, but for the possibility of overflow. So, it is on the strict side if you only use a subset of your enum's values - but it is consistent with the goal of type safety.
Related
Is there a way to check in C++11 if an enum is continuous?
It is fully valid to give an enum values which are not. Is there maybe a feature like a type trait in C++14, C++17 or maybe C++20 to check is the enum is continuous? This to be used in a static_assert.
A small example follows:
enum class Types_Discontinuous {
A = 10,
B = 1,
C = 100
};
enum class Types_Continuous {
A = 0,
B = 1,
C = 2
};
static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous"); // Passes
This is not possible in pure C++, because there is no way to enumerate the enum values, or discover the number of the values and minimum and maximum values. But you could try using the help of your compiler to implement something close to what you want. For example, in gcc it is possible to enforce a compilation error if a switch statement does not handle all values of an enum:
enum class my_enum {
A = 0,
B = 1,
C = 2
};
#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
// Check that we know all enum values. Effectively works as a static assert.
switch (t)
{
// Intentionally no default case.
// The compiler will give an error if not all enum values are listed below.
case my_enum::A:
case my_enum::B:
case my_enum::C:
break;
}
// Check that the enum is continuous
auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}
#pragma GCC diagnostic pop
Obviously, this is specialized for a given enum, but definition of such functions can be automated with preprocessor.
For a number of enums you can probably hack your way through this using the Magic Enum library. For example:
#include "magic_enum.hpp"
template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
// make sure we're actually testing an enum
if constexpr (!std::is_enum_v<Enum>)
return false;
else {
// get a sorted list of values in the enum
const auto values = magic_enum::enum_values<Enum>();
if (std::size(values) == 0)
return true;
// for every value, either it's the same as the last one or it's one larger
auto prev = values[0];
for (auto x : values) {
auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
if (x != prev && x != next)
return false;
else
prev = x;
}
return true;
}
}
Note that this is indeed, as the library name implies, "magic" – the library functions on a number of compiler-specific hacks. As such it doesn't really meet your requirement of "pure C++", but is probably as good as we can get until we have reflection facilities in the language.
I'd love to see an answer on this. I've been needing it as well.
Unfortunately, I don't think this is possible using the existing utilities. If you want to implement a type trait on this, you need support from your compiler, so writing a template for it doesn't sound feasible.
I've already extended the enumeration with a specific tag to indicate it is contiguous and immediately gives you the size: enum class constructor c++ , how to pass specific value?
Alternatively, you can write your own trait:
template<T> struct IsContiguous : std::false_type {};
This needs to be specialized whenever you define an contiguous enum where you want to use this. Unfortunately, this requires some maintenance and attention if the enum gets changed.
All enum's are continuous. 0 is always allowed; the highest value allowed is the highest enumerator rounded up to the next 1<<N -1 (all bits one), and all values in between are allowed too. ([dcl.enum] 9.7.1/5). If there are negative enumerators defined, the lowest value allowed is similarly defined by rounding down the lowest enumerator.
The enumerators defined in the enum are constant expressions with a value in range and the correct type, but you can define additional constants outside the enum which have the same properties:
constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)
I must send some data over a network where the package parts are not byte aligned. All packages are 8 byte long and an example package type might look like this:
union Packed
{
struct
{
uint64_t a : 5;
uint64_t b : 10;
bool c : 1;
};
uint64_t raw;
};
So the first 5 bits are field a the next 10 bits are field b and the last bit is field c. Now I need a send function that can send this and possibly other package types. The function should accept the fields as parameters. The low-level send function accepts a single uint64_t.
Edit As pointed out in the comments, it is not safe to read from raw after writing to a, b or c. To make it clear: this is also something i would like to change, but i included it at the top, because the union is used for all of my attempts.
My requirements are:
An overflow should be detected at compile time
The syntax should not be too bulky (subjective, but i will show what i want)
My first attempt
void send_struct(const Packed& packed)
{
raw_send(packed.raw);
}
int main()
{
send_struct({1000, 2, true}); // conversion changes value
Packed packed = {1000, 2, true}; // conversion changes value
send_struct(packed);
}
The warnings are generated which satisfies my first requirement, but i don't like the syntax: The curly braces look superfluous and manually creating a struct first is cumbersome.
With some warnings enable i even have to use two layers of curly braces, because the struct is nested inside the union.
Second attempt
template <typename ...Args>
void send_var(Args... args)
{
Packed packed {args...};
raw_send(packed.raw);
};
int main()
{
send_var(1000u, 2u, true);
}
Here, i like the syntax, but no warnings are generated, presumably because the bit width is lost somewhere.
Third attempt
struct A
{
uint64_t data : 5;
};
struct B
{
uint64_t data : 10;
};
void send_separate(A a, B b, bool c)
{
Packed packed {a.data, b.data, c};
raw_send(packed.raw);
}
int main()
{
send_separate({1000u}, {2u}, true); // conversion changes value
send_separate(1000u, 2u, true); // compile error
}
The first usage is ugly again: too many curly braces and the second one does not compile, because the structs cannot be implicitly constructed with a single value.
Question
How can i implement a function and a safe package definition such that the following function call compiles and shows a warning, because the value 1000 does not fit into 5 bit.
send(1000u, 2u, true);
I actually only care about the call site. The function and union definitions may be more complicated.
Edit 2
Using variables for the function parameters must should work, too.
uint64_t a, b;
send(a, b, true); // compiles, but may generate a warning
send(a & 0x1f, b & 0x3ff, true); // compiles preferably without a warning
The software will be used on linux only, is compiled with gcc or clang using at lease these warnings: -Wall -Wextra -pedantic plus the flag that allows anonymous structs and unions.
What are pros/cons of usage bitsets over enum flags?
namespace Flag {
enum State {
Read = 1 << 0,
Write = 1 << 1,
Binary = 1 << 2,
};
}
namespace Plain {
enum State {
Read,
Write,
Binary,
Count
};
}
int main()
{
{
unsigned int state = Flag::Read | Flag::Binary;
std::cout << state << std::endl;
state |= Flag::Write;
state &= ~(Flag::Read | Flag::Binary);
std::cout << state << std::endl;
} {
std::bitset<Plain::Count> state;
state.set(Plain::Read);
state.set(Plain::Binary);
std::cout << state.to_ulong() << std::endl;
state.flip();
std::cout << state.to_ulong() << std::endl;
}
return 0;
}
As I can see so far, bitsets have more convinient set/clear/flip functions to deal with, but enum-flags usage is a more wide-spreaded approach.
What are possible downsides of bitsets and what and when should I use in my daily code?
Both std::bitset and c-style enum have important downsides for managing flags. First, let's consider the following example code :
namespace Flag {
enum State {
Read = 1 << 0,
Write = 1 << 1,
Binary = 1 << 2,
};
}
namespace Plain {
enum State {
Read,
Write,
Binary,
Count
};
}
void f(int);
void g(int);
void g(Flag::State);
void h(std::bitset<sizeof(Flag::State)>);
namespace system1 {
Flag::State getFlags();
}
namespace system2 {
Plain::State getFlags();
}
int main()
{
f(Flag::Read); // Flag::Read is implicitly converted to `int`, losing type safety
f(Plain::Read); // Plain::Read is also implicitly converted to `int`
auto state = Flag::Read | Flag::Write; // type is not `Flag::State` as one could expect, it is `int` instead
g(state); // This function calls the `int` overload rather than the `Flag::State` overload
auto system1State = system1::getFlags();
auto system2State = system2::getFlags();
if (system1State == system2State) {} // Compiles properly, but semantics are broken, `Flag::State`
std::bitset<sizeof(Flag::State)> flagSet; // Notice that the type of bitset only indicates the amount of bits, there's no type safety here either
std::bitset<sizeof(Plain::State)> plainSet;
// f(flagSet); bitset doesn't implicitly convert to `int`, so this wouldn't compile which is slightly better than c-style `enum`
flagSet.set(Flag::Read); // No type safety, which means that bitset
flagSet.reset(Plain::Read); // is willing to accept values from any enumeration
h(flagSet); // Both kinds of sets can be
h(plainSet); // passed to the same function
}
Even though you may think those problems are easy to spot on simple examples, they end up creeping up in every code base that builds flags on top of c-style enum and std::bitset.
So what can you do for better type safety? First, C++11's scoped enumeration is an improvement for type safety. But it hinders convenience a lot. Part of the solution is to use template-generated bitwise operators for scoped enums. Here is a great blog post which explains how it works and also provides working code : https://www.justsoftwaresolutions.co.uk/cplusplus/using-enum-classes-as-bitfields.html
Now let's see what this would look like :
enum class FlagState {
Read = 1 << 0,
Write = 1 << 1,
Binary = 1 << 2,
};
template<>
struct enable_bitmask_operators<FlagState>{
static const bool enable=true;
};
enum class PlainState {
Read,
Write,
Binary,
Count
};
void f(int);
void g(int);
void g(FlagState);
FlagState h();
namespace system1 {
FlagState getFlags();
}
namespace system2 {
PlainState getFlags();
}
int main()
{
f(FlagState::Read); // Compile error, FlagState is not an `int`
f(PlainState::Read); // Compile error, PlainState is not an `int`
auto state = FlagState::Read | FlagState::Write; // type is `FlagState` as one could expect
g(state); // This function calls the `FlagState` overload
auto system1State = system1::getFlags();
auto system2State = system2::getFlags();
if (system1State == system2State) {} // Compile error, there is no `operator==(FlagState, PlainState)`
auto someFlag = h();
if (someFlag == FlagState::Read) {} // This compiles fine, but this is another type of recurring bug
}
The last line of this example shows one problem that still cannot be caught at compile time. In some cases, comparing for equality may be what's really desired. But most of the time, what is really meant is if ((someFlag & FlagState::Read) == FlagState::Read).
In order to solve this problem, we must differentiate the type of an enumerator from the type of a bitmask. Here's an article which details an improvement on the partial solution I referred to earlier : https://dalzhim.github.io/2017/08/11/Improving-the-enum-class-bitmask/
Disclaimer : I'm the author of this later article.
When using the template-generated bitwise operators from the last article, you will get all of the benefits we demonstrated in the last piece of code, while also catching the mask == enumerator bug.
Some observations:
std::bitset< N > supports an arbitrary number of bits (e.g., more than 64 bits), whereas underlying integral types of enums are restricted to 64 bits;
std::bitset< N > can implicitly (depending on the std implementation) use the underlying integral type with the minimal size fitting the requested number of bits, whereas underlying integral types for enums need to be explicitly declared (otherwise, int will be used as the default underlying integral type);
std::bitset< N > represents a generic sequence of N bits, whereas scoped enums provide type safety that can be exploited for method overloading;
If std::bitset< N > is used as a bit mask, a typical implementation depends on an additional enum type for indexing (!= masking) purposes;
Note that the latter two observations can be combined to define a strong std::bitset type for convenience:
typename< Enum E, std::size_t N >
class BitSet : public std::bitset< N >
{
...
[[nodiscard]]
constexpr bool operator[](E pos) const;
...
};
and if the code supports some reflection to obtain the number of explicit enum values, then the number of bits can be deduced directly from the enum type.
scoped enum types do not have bitwise operator overloads (which can easily be defined once using SFINAE or concepts for all scoped and unscoped enum types, but need to be included before use) and unsoped enum types will decay to the underlying integral type;
bitwise operator overloads for enum types, require less boilerplate than std::bitset< N > (e.g., auto flags = Depth | Stencil;);
enum types support both signed and unsigned underlying integral types, whereas std::bitset< N > internally uses unsigned integral types (shift operators).
FWIIW, in my own code I mostly use std::bitset (and eastl::bitvector) as private bit/bool containers for setting/getting single bits/bools. For masking operations, I prefer scoped enum types with explicitly defined underlying types and bitwise operator overloads.
Do you compile with optimization on? It is very unlikely that there is a 24x speed factor.
To me, bitset is superior, because it manages space for you:
can be extended as much as wanted. If you have a lot of flags, you may run out of space in the int/long long version.
may take less space, if you only use just several flags (it can fit in an unsigned char/unsigned short - I'm not sure that implementations apply this optimization, though)
(Ad mode on)
You can get both: a convenient interface and max performance. And type-safety as well. https://github.com/oliora/bitmask
Often I have some compile-time constant number that is also the upper limit of possible values assumed by the variables. And thus I'm interested in choosing the smallest type that can accomodate those values. For example I may know that variables will fit into <-30 000, 30 000> range, so when looking for a suitable type I would start with signed short int. But since I'm switching between platforms and compilers I would like a compile-time assert checking whether the constant upper values really fit within those type. BOOST_STATIC_ASSERT( sizeof(T) >= required_number_of_bytes_for_number ) works fine but the problem is:
How to automatically determine the number of bytes required for storing a given compile-time constant, signed or unsigned? I guess a C macro could do this job? Could anyone write it for me?
I might use std::numeric_limits::max() and min() instead of computing the bytes but then I would have to switch to run-time assert :(
Now that this is tagged with c++, I suggest using Boost.Integer for appropriate type selection. boost::int_max_value_t< MyConstant >::least would give the type you are looking for.
You may use the following code. It works only for positive 8/16/32/64bit integers. But you may do the appropriate changes for negative values as well.
template <typename T, T x> class TypeFor
{
template <T x>
struct BitsRequired {
static const size_t Value = 1 + BitsRequired<x/2>::Value;
};
template <>
struct BitsRequired<0> {
static const size_t Value = 0;
};
static const size_t Bits = BitsRequired<x>::Value;
static const size_t Bytes = (Bits + 7) / 8;
static const size_t Complexity = 1 + BitsRequired<Bytes-1>::Value;
template <size_t c> struct Internal {
};
template <> struct Internal<1> {
typedef UCHAR Type;
};
template <> struct Internal<2> {
typedef USHORT Type;
};
template <> struct Internal<3> {
typedef ULONG Type;
};
template <> struct Internal<4> {
typedef ULONGLONG Type;
};
public:
typedef typename Internal<Complexity>::Type Type;
};
TypeFor<UINT, 117>::Type x;
P.S. this compiles under MSVC. Probably some adjustment should be done to adopt it for gcc/mingw/etc.
How about you avoid the problem:
BOOST_STATIC_ASSERT((1LL << (8*sizeof(T))) >= number);
How about BOOST_STATIC_ASSERT(int(60000)==60000) ? This will test whether 60000 fits in an int. If int is 16 bits, int(60000) is 27232. For the comparison, this will then be zero-extended back to a 32 bits long, and fail reliably.
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();
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.
It is not possible to determine endianness at compile time using constexpr (before C++20). reinterpret_cast is explicitly forbidden by [expr.const]p2, as is iain's suggestion of reading from a non-active member of a union. Casting to a different reference type is also forbidden, as such a cast is interpreted as a reinterpret_cast.
Update:
This is now possible in C++20. One way (live):
#include <bit>
template<std::integral T>
constexpr bool is_little_endian() {
for (unsigned bit = 0; bit != sizeof(T) * CHAR_BIT; ++bit) {
unsigned char data[sizeof(T)] = {};
// In little-endian, bit i of the raw bytes ...
data[bit / CHAR_BIT] = 1 << (bit % CHAR_BIT);
// ... corresponds to bit i of the value.
if (std::bit_cast<T>(data) != T(1) << bit)
return false;
}
return true;
}
static_assert(is_little_endian<int>());
(Note that C++20 guarantees two's complement integers -- with an unspecified bit order -- so we just need to check that every bit of the data maps to the expected place in the integer.)
But if you have a C++20 standard library, you can also just ask it:
#include <type_traits>
constexpr bool is_little_endian = std::endian::native == std::endian::little;
Assuming N2116 is the wording that gets incorporated, then your example is ill-formed (notice that there is no concept of "legal/illegal" in C++). The proposed text for [decl.constexpr]/3 says
its function-body shall be a compound-statement of the form
{ return expression; }
where expression is a potential constant expression (5.19);
Your function violates the requirement in that it also declares a local variable.
Edit: This restriction could be overcome by moving num outside of the function. The function still wouldn't be well-formed, then, because expression needs to be a potential constant expression, which is defined as
An expression is a potential constant expression if it is a constant
expression when all occurrences of function parameters are replaced
by arbitrary constant expressions of the appropriate type.
IOW, reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD would have to be a constant expression. However, it is not: &num would be a address constant-expression (5.19/4). Accessing the value of such a pointer is, however, not allowed for a constant expression:
The subscripting operator [] and the class member access . and
operators, the & and * unary operators, and pointer casts (except dynamic_casts, 5.2.7) can be used in the creation of an
address constant expression, but the value of an object shall not be accessed by the use of these operators.
Edit: The above text is from C++98. Apparently, C++0x is more permissive what it allows for constant expressions. The expression involves an lvalue-to-rvalue conversion of the array reference, which is banned from constant expressions unless
it is applied to an lvalue of effective integral type that refers
to a non-volatile const variable or static data member initialized
with constant expressions
It's not clear to me whether (&num)[0] "refers to" a const variable, or whether only a literal num "refers to" such a variable. If (&num)[0] refers to that variable, it is then unclear whether reinterpret_cast<const unsigned char*> (&num)[0] still "refers to" num.
There is std::endian in the upcoming C++20.
#include <bit>
constexpr bool little_endian() noexcept
{
return std::endian::native == std::endian::little;
}
My first post. Just wanted to share some code that I'm using.
//Some handy defines magic, thanks overflow
#define IS_LITTLE_ENDIAN ('ABCD'==0x41424344UL) //41 42 43 44 = 'ABCD' hex ASCII code
#define IS_BIG_ENDIAN ('ABCD'==0x44434241UL) //44 43 42 41 = 'DCBA' hex ASCII code
#define IS_UNKNOWN_ENDIAN (IS_LITTLE_ENDIAN == IS_BIG_ENDIAN)
//Next in code...
struct Quad
{
union
{
#if IS_LITTLE_ENDIAN
struct { std::uint8_t b0, b1, b2, b3; };
#elif IS_BIG_ENDIAN
struct { std::uint8_t b3, b2, b1, b0; };
#elif IS_UNKNOWN_ENDIAN
#error "Endianness not implemented!"
#endif
std::uint32_t dword;
};
};
Constexpr version:
namespace Endian
{
namespace Impl //Private
{
//41 42 43 44 = 'ABCD' hex ASCII code
static constexpr std::uint32_t LITTLE_{ 0x41424344u };
//44 43 42 41 = 'DCBA' hex ASCII code
static constexpr std::uint32_t BIG_{ 0x44434241u };
//Converts chars to uint32 on current platform
static constexpr std::uint32_t NATIVE_{ 'ABCD' };
}
//Public
enum class Type : size_t { UNKNOWN, LITTLE, BIG };
//Compare
static constexpr bool IS_LITTLE = Impl::NATIVE_ == Impl::LITTLE_;
static constexpr bool IS_BIG = Impl::NATIVE_ == Impl::BIG_;
static constexpr bool IS_UNKNOWN = IS_LITTLE == IS_BIG;
//Endian type on current platform
static constexpr Type NATIVE_TYPE = IS_LITTLE ? Type::LITTLE : IS_BIG ? Type::BIG : Type::UNKNOWN;
//Uncomment for test.
//static_assert(!IS_LITTLE, "This platform has little endian.");
//static_assert(!IS_BIG, "This platform has big endian.");
//static_assert(!IS_UNKNOWN, "Error: Unsupported endian!");
}
That is a very interesting question.
I am not Language Lawyer, but you might be able to replace the reinterpret_cast with a union.
const union {
int int_value;
char char_value[4];
} Endian = { 0xAABBCCDD };
constexpr bool little_endian()
{
return Endian[0] == 0xDD;
}
This may seem like cheating, but you can always include endian.h... BYTE_ORDER == BIG_ENDIAN is a valid constexpr...
Here is a simple C++11 compliant version, inspired by #no-name answer:
constexpr bool is_system_little_endian(int value = 1) {
return static_cast<const unsigned char&>(value) == 1;
}
Using a default value to crank everything on one line is to meet C++11 requirements on constexpr functions: they must only contain a single return statement.
The good thing with doing it (and testing it!) in a constexpr context is that it makes sure that there is no undefined behavior in the code.
On compiler explorer here.
If your goal is to insure that the compiler optimizes little_endian() into a constant true or false at compile-time, without any of its contents winding up in the executable or being executed at runtime, and only generating code from the "correct" one of your two Foo templates, I fear you're in for a disappointment.
I also am not a language lawyer, but it looks to me like constexpr is like inline or register: a keyword that alerts the compiler writer to the presence of a potential optimization. Then it's up to the compiler writer whether or not to take advantage of that. Language specs typically mandate behaviors, not optimizations.
Also, have you actually tried this on a variety of C++0x complaint compilers to see what happens? I would guess most of them would choke on your dual templates, since they won't be able to figure out which one to use if invoked with false.