代码之家  ›  专栏  ›  技术社区  ›  Charles Salvia

constexpr和endianness

  •  23
  • Charles Salvia  · 技术社区  · 15 年前

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

    然后专门化两种endian类型的模板:

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

    然后这样做:

    Foo<little_endian()>::do_something();
    
    7 回复  |  直到 7 年前
        1
  •  12
  •   Martin v. Löwis    15 年前

    假设 N2116 是被合并的措辞,那么你的例子是不正确的(注意C++中没有“合法/非法”的概念”。[decl.constexpr]/3的建议文本显示

    • 其功能体应为形式的复合语句。 { return expression; } 其中表达式是一个势常数表达式(5.19);

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

    编辑 :可以通过将num移出函数来克服此限制。那么,函数的格式仍然不正确,因为表达式需要是一个潜在的常量表达式,该表达式定义为

    表达式是一个潜在的常量表达式,如果它是一个常量 替换所有出现的函数参数时的表达式 通过适当类型的任意常量表达式。

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

    订阅运算符[]和类成员访问。和 操作员, & * 一元运算符和指针强制转换(动态强制转换除外,5.2.7)可用于创建 地址常量表达式,但这些运算符不能访问对象的值。

    编辑 以上文字来源于C++ 98。显然,C++0x允许常数表达式的允许。表达式涉及数组引用的左值到右值转换,该转换在常量表达式中被禁止,除非

    它应用于有效积分类型的左值 到已初始化的非易失性常量变量或静态数据成员 使用常量表达式

    我不清楚 (&num)[0] “指”一个常量变量,或者是否只是一个文本 num “指”这样一个变量。如果 (&Num)〔0〕 参考该变量,则不清楚 reinterpret_cast<const unsigned char*> (&num)[0] 仍然“指” 号码 .

        2
  •  13
  •   Piotr Siupa    7 年前

    我能写下:

    #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上给出了正确的结果。 如果您有任何大尾数法或中尾数法,请在评论中确认这对您有效。

        3
  •  6
  •   Richard Smith    13 年前

    不能在编译时使用 constexpr . reinterpret_cast 被[expr.const]p2明确禁止,正如iain建议从工会的非活跃成员处阅读一样。

        4
  •  5
  •   iain    15 年前

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

    我不是语言律师,但你也许可以用一个工会来取代重新解释的角色。

    const union {
        int int_value;
        char char_value[4];
    } Endian = { 0xAABBCCDD };
    
    constexpr bool little_endian()
    {
       return Endian[0] == 0xDD;
    }
    
        5
  •  3
  •   Andrew F.    7 年前

    我的第一个职位。只是想共享一些我正在使用的代码。

    //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!");
    }
    
        6
  •  1
  •   Christopher Smith    11 年前

    这看起来像是作弊,但你可以一直包括endian.h…byte_order==big_endian是有效的constexpr…

        7
  •  -3
  •   Bob Murphy    15 年前

    如果您的目标是确保编译器优化 little_endian() 在编译时变为常量true或false,而其任何内容都不会在可执行文件中结束或在运行时执行,并且只从“correct”中生成代码 Foo 模板,我担心你会失望。

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

    而且,你是否真的在各种C++0X抱怨编译器上尝试了这个,看看会发生什么?我猜他们中的大多数人可能会被你的双重模板扼杀,因为如果用 false .