views:

2085

answers:

7

I'm using a well known template to allow binary constants

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

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

So you can do something like binary<101011011>::value. Unfortunately this has a limit of 20 digits for a unsigned long long.

Does anyone have a better solution?

+4  A: 

C++0x has user-defined literals, which could be used to implement what you're talking about.

Otherwise, I don't know how to improve this template.

sysrqb
Can you give an example.
Unknown
sure, here is an example: http://stackoverflow.com/questions/537303/binary-literals/538101#538101
Johannes Schaub - litb
+4  A: 

The approaches I've always used, though not as elegant as yours:

1/ Just use hex. After a while, you just get to know which hex digits represent which bit patterns.

2/ Use constants and OR or ADD them. For example (may need qualifiers on the bit patterns to make them unsigned or long):

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

unsigned long x = b2 | b7

3/ If performance isn't critical and readability is important, you can just do it at runtime with a function such as "x = fromBin("101011011");".

4/ As a sneaky solution, you could write a pre-pre-processor that goes through your *.cppme files and creates the *.cpp ones by replacing all "0b101011011"-type strings with their equivalent "0x15b" strings). I wouldn't do this lightly since there's all sorts of tricky combinations of syntax you may have to worry about. But it would allow you to write your string as you want to without having to worry about the vagaries of the compiler, and you could limit the syntax trickiness by careful coding.

Of course, the next step after that would be patching GCC to recognize "0b" constants but that may be an overkill :-)

paxdiablo
funny you mentioned the last part. I was also using bitset<>(string(str)).to_ulong()
Unknown
I wonder what the situation is that makes using the 'binary template' better than just straightforward hex constants or 'or-ing' together enums with proper names for the bits if you're modeling hardware or communications protocols?
Michael Burr
+18  A: 

Does this work if you have a leading zero on your binary value? A leading zero makes the constant octal rather than decimal.

Which leads to a way to squeeze a couple more digits out of this solution - always start your binary constant with a zero! Then replace the 10's in your template with 8's.

Mark Ransom
Ooh, that's sneaky :-) Bravo.
paxdiablo
Thanks guys, you've made my night!
Mark Ransom
+2  A: 

You can add more non-type template parameters to "simulate" additional bits:

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

You can use this as before, e.g.:

binary<1001101>::value

But you can also use the following equivalent forms:

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

Basically, the extra parameter gives you another 20 bits to play with. You could add even more parameters if necessary.

Because the place value of the second number is used to figure out how far to the left the first number needs to be shifted, the second number must begin with a 1. (This is required anyway, since starting it with a 0 would cause the number to be interpreted as an octal number.)

j_random_hacker
+1  A: 
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;
};

Allowing:

Bin::O::I::I::O::O::value

much more verbose, but no limits (until you hit the size of an unsigned int of course).

Sneaky! but won't that be a little too much instead of just typing hex?
LiraNuna
Obvious extension would be `Bin::OOOO::IIOO::IIIO` which arguably is a lot easier to read, too.
MSalters
+1  A: 

Technically it is not C or C++, it is a GCC specific extension, but GCC allows binary constants as seen here:

 The following statements are identical:

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

Hope that helps. Some Intel compilers and I am sure others, implement some of the GNU extensions. Maybe you are lucky.

Amigable Clark Kant
A: 

A simple #define works very well:

#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)

Not my invention, I saw it on a forum a long time ago.

Stuart