【问题标题】:C++ binary constant/literalC++ 二进制常量/字面量
【发布时间】:2010-10-16 12:48:11
【问题描述】:

我正在使用一个众所周知的模板来允许二进制常量

template< unsigned long long N >
struct binary
{
  enum { value = (N % 10) + 2 * binary< N / 10 > :: value } ;
};

template<>
struct binary< 0 >
{
  enum { value = 0 } ;
};

因此您可以执行类似二进制::value 的操作。不幸的是,这对于 unsigned long long 有 20 位数字的限制。

谁有更好的解决方案?

【问题讨论】:

  • 20 的限制可能取决于编译器。这取决于它将容忍多少模板递归。一些现代编译器将允许您传入一个参数来设置最大模板递归深度。
  • 我虽然限制是由于您可以存储在无符号长长中的十进制位数,因为它基本上采用 十进制 数字 101011011 并将其转换为二进制,是吗?
  • Pax:是的,至少对于我正在使用的 GCC。
  • Binary literals?的可能重复

标签: c++ templates binary constants


【解决方案1】:

如果您的二进制值有前导零,这是否有效?前导零表示常量是八进制而不是十进制。

这会导致从该解决方案中挤出更多数字 - 始终以零开始您的二进制常量!然后将模板中的 10 替换为 8。

【讨论】:

  • 哦,这很鬼鬼祟祟 :-) 太棒了。
【解决方案2】:

我一直使用的方法,虽然不如你的优雅:

1/ 只使用十六进制。过了一会儿,你就会知道哪些十六进制数字代表哪些位模式。

2/ 使用常量和 OR 或 ADD 它们。例如(可能需要位模式上的限定符以使其无符号或长):

#define b0  0x00000001
#define b1  0x00000002
: : :
#define b31 0x80000000

unsigned long x = b2 | b7

3/ 如果性能不重要且可读性很重要,您可以在运行时使用诸如“x = fromBin("101011011");”之类的函数来完成。

4/ 作为一个偷偷摸摸的解决方案,您可以编写一个预处理器,通过您的 *.cppme 文件并通过将所有“0b101011011”类型的字符串替换为等效的“0x15b”字符串来创建 *.cpp 文件)。我不会轻易做到这一点,因为您可能不得不担心各种棘手的语法组合。但它可以让您根据需要编写字符串,而不必担心编译器的变幻莫测,并且您可以通过仔细编码来限制语法技巧。

当然,之后的下一步将是修补 GCC 以识别“0b”常量,但这可能有点矫枉过正:-)

【讨论】:

  • 你提到了最后一部分很有趣。我也在使用 bitset(string(str)).to_ulong()
  • 我想知道是什么情况使得使用“二进制模板”比仅仅使用简单的十六进制常量或“或-ing”枚举与位的专有名称(如果您正在建模硬件或通信)更好协议?
  • 其实GCC支持0b常量。
  • 是的,使用稍微少一些“优雅”的代码总是更好,但要摆脱垃圾样板。因此,如果 C++ 不是您的朋友(没有原生 0b01),最好使用清晰易懂的代码。
【解决方案3】:

C++0x 有user-defined literals,可以用来实现你所说的。

否则,我不知道如何改进这个模板。

【讨论】:

【解决方案4】:

您可以添加更多非类型模板参数来“模拟”额外的位:

// Utility metafunction used by top_bit<N>.
template <unsigned long long N1, unsigned long long N2>
struct compare {
    enum { value = N1 > N2 ? N1 >> 1 : compare<N1 << 1, N2>::value };
};

// This is hit when N1 grows beyond the size representable
// in an unsigned long long.  It's value is never actually used.
template<unsigned long long N2>
struct compare<0, N2> {
    enum { value = 42 };
};

// Determine the highest 1-bit in an integer.  Returns 0 for N == 0.
template <unsigned long long N>
struct top_bit {
    enum { value = compare<1, N>::value };
};

template <unsigned long long N1, unsigned long long N2 = 0>
struct binary {
    enum {
        value =
            (top_bit<binary<N2>::value>::value << 1) * binary<N1>::value +
            binary<N2>::value
    };
};

template <unsigned long long N1>
struct binary<N1, 0> {
    enum { value = (N1 % 10) + 2 * binary<N1 / 10>::value };
};

template <>
struct binary<0> {
    enum { value = 0 } ;
};

你可以像以前一样使用它,例如:

binary<1001101>::value

但您也可以使用以下等价形式:

binary<100,1101>::value
binary<1001,101>::value
binary<100110,1>::value

基本上,额外的参数给你另外 20 位可以使用。如有必要,您可以添加更多参数。

因为第二个数字的位值用于计算第一个数字需要向左移动多远,所以第二个数字必须以 1 开头。(无论如何这是必需的,因为它以 0 开头会导致该数字被解释为八进制数。)

【讨论】:

    【解决方案5】:
    template<unsigned int p,unsigned int i> struct BinaryDigit 
    {
      enum  { value = p*2+i };
      typedef BinaryDigit<value,0> O;
      typedef BinaryDigit<value,1> I;
    };
    struct Bin
    {
      typedef BinaryDigit<0,0> O;
      typedef BinaryDigit<0,1> I;
    };
    

    允许:

    Bin::O::I::I::O::O::值

    更详细,但没有限制(当然,直到你达到 unsigned int 的大小)。

    【讨论】:

    • 偷偷摸摸!但这不会有点太多而不是只输入十六进制吗?
    • 明显的扩展名是Bin::OOOO::IIOO::IIIO,这也可以说更容易阅读。
    【解决方案6】:

    从技术上讲,它既不是 C 也不是 C++,它是 GCC 特定的扩展,但 GCC 允许 binary constants,如 here 所示:

     The following statements are identical:
    
     i =       42;
     i =     0x2a;
     i =      052;
     i = 0b101010;
    

    希望对您有所帮助。一些英特尔编译器,我相信还有其他编译器,实现了一些 GNU 扩展。也许你很幸运。

    【讨论】:

      【解决方案7】:

      一个简单的#define 效果很好:

      #define HEX__(n) 0x##n##LU
      
      #define B8__(x) ((x&0x0000000FLU)?1:0)\
                     +((x&0x000000F0LU)?2:0)\
                    +((x&0x00000F00LU)?4:0)\
                     +((x&0x0000F000LU)?8:0)\
                     +((x&0x000F0000LU)?16:0)\
                     +((x&0x00F00000LU)?32:0)\
                     +((x&0x0F000000LU)?64:0)\
                     +((x&0xF0000000LU)?128:0)
      
      #define B8(d) ((unsigned char)B8__(HEX__(d)))
      #define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
      #define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) + ((unsigned long)B8(db2)<<16) + ((unsigned long)B8(db3)<<8) + B8(dlsb))
      
      B8(011100111)
      B16(10011011,10011011)
      B32(10011011,10011011,10011011,10011011)
      

      不是我的发明,很久以前在论坛上看到的。

      【讨论】:

        猜你喜欢
        • 2010-10-10
        • 2022-01-25
        • 1970-01-01
        • 2010-10-06
        • 2010-11-17
        • 2013-05-17
        • 1970-01-01
        • 1970-01-01
        • 2014-03-10
        相关资源
        最近更新 更多