C++ 二进制常量/字面值

14

我正在使用一个广为人知的模板来允许二进制常量。

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

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

你可以像这样执行 binary<101011011>::value。不幸的是,这对于无符号长整型有 20 位数字的限制。

有没有更好的解决方案?


3
我认为这个限制是由于在无符号长整型中可以存储的十进制小数位数有限制,因为它基本上是将“十进制”数101011011转换成二进制数,是吗? - paxdiablo
1
可能是二进制字面量?的重复问题。 - M.J. Rayburn
更好的解决方案是针对什么?不清楚您想要做什么或者问题出在哪里。您想要以十进制声明吗?您想要使用 uint128_t 吗?还是其他什么? - jww
哦,等等,不好意思。你想让代码在编译时运行,就像文字评估一样,这样就不会减慢程序的速度,这就是为什么你使用模板的原因。是的,我不知道。显然,你比我更清楚需要解决这个问题的内容。我对预处理器几乎一无所知。 - M.J. Rayburn
@jww 他正在尝试使用二进制表示法来初始化一个整数,而不是十进制表示法,就像C++内置的长整型特性一样,允许您在十六进制或八进制中初始化一个整数。无符号长长整型 n = 0xFFFFFFFFFFFFFFFF 和无符号长长整型 n = 07777777777777777777771。显然,这已经是GCC编译器的一个特性了,并且最近被添加到C++14标准中。无符号长长整型 n = 0b1111111111111111111111111111111111111111111111111111111111111111。... - M.J. Rayburn
显示剩余6条评论
7个回答

25

如果您的二进制值具有前导零,这种方法是否有效? 前导零使常量成为八进制而不是十进制。

这导致了一种方法来从这个解决方案中挤出更多数字 - 总是以零开头的二进制常量! 然后用8替换模板中的10。


2
哦,这很狡猾 :-) 好极了。 - paxdiablo

5

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

1/ 只需使用十六进制。过一段时间后,您就会知道哪些十六进制数字代表哪些位模式。

2/ 使用常量并进行OR或ADD操作。例如(可能需要在位模式上添加限定符以使它们成为无符号或长整型):

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

unsigned long x = b2 | b7

3/ 如果性能不是很重要,而可读性很重要,你可以使用像“x = fromBin(“101011011”);”这样的函数来在运行时转换。

4/ 作为一个诡秘的解决方案,您可以编写一个预处理器,通过处理*.cppme文件并用其等效的"0x15b"字符串替换所有类似于"0b101011011"的字符串来创建*.cpp文件。我不会轻易这样做,因为您可能需要担心各种棘手的语法组合。但是,这将允许您按照自己的意愿编写字符串,而无需担心编译器的变幻莫测,您可以通过仔细编码来限制语法的复杂性。

当然,在那之后的下一步将是修补GCC以识别"0b"常量,但这可能有些过度了:-)


有趣的是你提到了最后一部分。我也在使用bitset<>(string(str)).to_ulong()。 - Unknown
2
我想知道在建模硬件或通信协议时,使用“二进制模板”比直接使用十六进制常量或将枚举与位的适当名称进行“或运算”的情况更好的原因是什么? - Michael Burr
实际上,GCC支持0b常量。 - Prof. Falken
没错,使用略微不那么“优雅”的代码,但是摆脱垃圾样板代码总是更好的选择。因此,如果C++不是你的朋友(没有本地0b01),最好使用清晰易懂的代码。 - Yury

4

C++0x拥有用户定义字面量,可以用于实现您所说的内容。

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


当然,这里有一个例子:https://dev59.com/iXRB5IYBdhLWcg3wuZU1#538101 - Johannes Schaub - litb

4
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::value

更加冗长,但没有限制(当然,直到达到无符号整数的大小限制)。


难道这不会有点过分吗?只是输入十六进制不就好了吗? - LiraNuna
4
显然的延伸是 Bin::OOOO::IIOO::IIIO,这可能更易于阅读。 - MSalters

3

从技术上讲,它不是C或C ++,而是GCC特定的扩展,但GCC允许使用二进制常量,如此处所示

 The following statements are identical:

 i =       42;
 i =     0x2a;
 i =      052;
 i = 0b101010;

希望这能有所帮助。一些英特尔编译器和其他编译器实现了一些GNU扩展。也许你很幸运。

3

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

// 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开头,该数字将被解释为八进制数。)


2
一个简单的 #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)

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接