constexpr和endianness

Cha*_*via 26 c++ endianness c++11

在C++编程领域不时出现的一个常见问题是字节序的编译时确定.通常这是通过几乎不可移植的#ifdef来完成的.但是C++ 11 constexpr关键字和模板专业化是否为我们提供了更好的解决方案?

做以下事情是合法的C++ 11:

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

然后专门为两种endian类型设置模板:

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

template <>
struct Foo<false>
{
  // .... specialization for big endian
};
Run Code Online (Sandbox Code Playgroud)

然后做:

Foo<little_endian()>::do_something();
Run Code Online (Sandbox Code Playgroud)

NO_*_*AME 14

我能写出来:

#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;
};
Run Code Online (Sandbox Code Playgroud)

我用g ++测试了它,它编译时没有警告.它在x64上给出了正确的结果.如果您有任何big-endian或中端的procceor,请在评论中确认这适用于您.

  • 我明白,但是这样的演员有什么好处呢?为什么不只使用 uint8_t 而不使用 const 和 ref 呢? (2认同)
  • 这是行不通的。转换为 const uint8_t&amp; 会通过将 uint32_ 的值截断为 8 位来创建临时值。这将始终声称每个目标都是小端字节序。(尽管现在这在实践中可能足够准确!) (2认同)

Mar*_*wis 12

假设N2116是被合并的措辞,那么你的例子是不正确的(注意在C++中没有"合法/非法"的概念).[decl.constexpr]/3的拟议文本说

  • 它的函数体应该是{ return expression; } 表达式是潜在常量表达式的复合语句 (5.19);

您的函数违反了该要求,因为它还声明了一个局部变量.

编辑:可以通过在函数外部移动num来克服此限制.因此,函数仍然不能很好地形成,因为表达式需要是一个潜在的常量表达式,定义为

如果所有出现的函数参数都被相应类型的任意常量表达式替换,则表达式是一个常量表达式.

IOW,reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD 必须是一个不变的表达.但是,它不是:&num将是一个地址常量表达式(5.19/4).但是,对于常量表达式,不允许访问此类指针的值:

下标运算符[]和类成员访问.和运算符,&*一元运算符以及指针强制转换(dynamic_casts,5.2.7除外)可用于创建地址常量表达式,但不能通过使用这些运算符来访问对象的值.

编辑:上面的文字来自C++ 98.显然,C++ 0x更容许它允许常量表达式.该表达式涉及数组引用的左值到右值的转换,除非使用常量表达式,否则它将被禁止

它应用于有效整数类型的左值,它指的是用常量表达式初始化的非易失性const变量或静态数据成员

我不清楚(&num)[0]"引用"一个const变量,或者只是一个文字num"引用"这样一个变量.如果(&num)[0]引用该变量,则不清楚是否reinterpret_cast<const unsigned char*> (&num)[0]仍然"引用" num.

  • 最后引用的段落似乎不是最新的c ++ 0x草案(n2960)的一部分.草案说如果`num`不是线程的变量或数据成员或自动存储持续时间,那么`&num`是一个常量表达式(读:如果`num`是没有"thread_local"说明符的本地静态或命名空间范围变量,然后`&num`是一个常量表达式).但是`reinterpret_cast`使它成为一个非常量表达式,因为它构成了指针类型到文字类型的转换(注意指针类型本身是文字类型). (2认同)
  • 不,这不值得怀疑.可以肯定的是,这是不允许的.措辞很清楚. (2认同)

Ric*_*ith 8

使用时无法在编译时确定字节序constexpr.reinterpret_cast[expr.const] p2明确禁止,iain建议从联盟的非活跃成员中读取.


小智 6

我的第一篇文章。只是想分享一些我正在使用的代码。

//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;
    };
};
Run Code Online (Sandbox Code Playgroud)

构造函数版本:

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!");
}
Run Code Online (Sandbox Code Playgroud)


mag*_*ras 6

std::endian在即将到来的C ++ 20。

#include <type_traits>

constexpr bool little_endian() noexcept
{
    return std::endian::native == std::endian::little;
}
Run Code Online (Sandbox Code Playgroud)