constexpr和endianness

时间:2009-10-18 02:01:59

标签: c++ c++11 endianness

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;
}

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

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

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

然后做:

Foo<little_endian()>::do_something();

9 个答案:

答案 0 :(得分: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;
};

我用g ++测试了它,它在没有警告的情况下进行编译。它在x64上给出了正确的结果。 如果您有任何big-endian或中端的procceor,请在评论中确认这对您有用。

答案 1 :(得分: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

答案 2 :(得分:6)

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

答案 3 :(得分:5)

这是一个非常有趣的问题。

我不是语言律师,但您可以用联盟替换reinterpret_cast。

const union {
    int int_value;
    char char_value[4];
} Endian = { 0xAABBCCDD };

constexpr bool little_endian()
{
   return Endian[0] == 0xDD;
}

答案 4 :(得分:3)

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

//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版本:

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_ENDIAN, "This platform has big endian.");
    //static_assert(!IS_UNKNOWN, "Error: Unsupported endian!");
}

答案 5 :(得分:1)

这可能看起来像作弊,但你总是可以包含endian.h ... BYTE_ORDER == BIG_ENDIAN是一个有效的constexpr ......

答案 6 :(得分:0)

即将发布的C ++ 20中有std::endian

#include <type_traits>

constexpr bool little_endian() noexcept
{
    return std::endian::native == std::endian::little;
}

答案 7 :(得分:0)

最简单的方法就是更改函数中的转换:

constexpr bool little_endian()
{
    return 0xDD == (const uint8_t&)0xAABBCCDD;
}

如前所述。

答案 8 :(得分:-3)

如果您的目标是确保编译器在编译时将little_endian()优化为常量true或false,而不将其任何内容汇总到可执行文件中或在运行时执行,并且仅生成来自两个Foo模板中的“正确”模板,我担心你会感到失望。

我也不是语言律师,但我认为constexpr就像inlineregister:一个关键字,它提醒编译器编写者存在潜在的优化。然后由编译器编写者决定是否利用它。语言规范通常要求行为,而不是优化。

另外,您是否真的尝试过各种C ++ 0x投诉编译器,看看会发生什么?我猜他们中的大多数会窒息你的双模板,因为如果用false调用它们将无法确定使用哪一个。