|
Boost : |
From: Scott Schurr (scott_schurr_at_[hidden])
Date: 2005-07-13 13:12:42
Greetings,
I wrote in part:
> Prompted by some discussion on comp.sdt.c++.moderated, I have a
> small template that allows the user to declare an integer literal
> in binary.
>
> Usage looks like:
>
> #include "binary_int.hpp"
>
> unsigned int binary = binary_int<1001,0011,1011,0000>::b;
Thanks for all the responses. Here are answers to some of the
questions I got.
------------------------
Pavel Vozenilek asked:
> * How does it deal with bit values like 0111? This is octal value.
Right. There are specilizations for decimal and octal values.
This means there is some aliasing. A few non-binary decimal and
octal values leak through. For example 0111 (octal) is 73 decimal
and, for that matter, 0x49 hex. So the template accepts all of
these values. It's not perfect, but it's the best I could figure
out.
> * Does it check that the digits are 0 or 1 during compile time?
It doesn't check individual digits. It checks for specific numeric
values which would be good at representing a 4-bit binary value.
See the binary_nibble specializations in the appended code.
> * Would it work with hex values like:
> bits<unsigned, 0xF,0x8>::value;
No, not as it is currently implemented.
------------------------
Vladimir Prus responded:
> Why is that better than
>
> unsigned x = strtoul("1010010001000010000001", 0, 2);
>
> ? I believe that 'strtoul' is part of C99.
Not a bad approach. The binary_int template does the translation
at compile time rather than at run time. It does the check (as
best it can) for valid binary values at compile time as well.
-----------------------
Paul A Bristow replied:
> Can you post reference to discussion...
I'm not very clever with URLs but the discussion, according to
Google Groups, was titled "Power of 2 Enumerations" in comp.std.c++.
The discussion in question started with a post by David Abrahams
on November 1st, 2004 where he wrote:
> You can even write a simple template that makes these into
> compile-time constants:
>
> const feature_t FEATURE1 = binary<001>::value;
> const feature_t FEATURE2 = binary<010>::value;
> const feature_t FEATURE3 = binary<100>::value;
Paul also asked:
> and perhaps append the binary_int.hpp file as it sounds quite
> short?
Yes, the template is only 150 lines or so including comments.
I've appended it, in its current state, to the end of this
response.
I did some editing of the template in an attempt to make it
boost compatible, although I didn't put it in the Boost name
space. Even though it says it's a boost file, I'm fully aware
that it isn't. Sorry about that.
Thanks for everyone's attention.
Scott Schurr
binary_int.hpp
--------------------------------------------------------------
// Boost general library binary_int.hpp header file -------------------------//
// Copyright Credence Systems 2005, as submitted by Scott Schurr. Use,
// modification, and distribution are subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_BINARY_INT_HPP
#define BOOST_BINARY_INT_HPP
// Make a couple of templates to help catch bad binary_nibble parameters
template <bool>
struct binary_nibble_VALID_VALUES_BINARY_0000_THROUGH_1111;
template <>
struct binary_nibble_VALID_VALUES_BINARY_0000_THROUGH_1111<true> { };
// Write a 4-bit integer value as binary.
//
// Usage:
// unsigned int n = binary_nibble<1011>::b
//
// The tricky part is remembering to include the '::b' at the end.
// For example:
//
// #include "binary_int.h"
// ...
// int regValue = binary_nibble<1001>::b;
//
// Make a general binary_nibble type that causes a failure. Only
// the specializations are valid usages.
template <unsigned int v>
struct binary_nibble {
binary_nibble_VALID_VALUES_BINARY_0000_THROUGH_1111<false> t;
enum { b = 0 };
};
// Specialize the binary_nibble template for values we allow.
// The following values work for both decimal and octal.
template<> struct binary_nibble<0> { enum { b = 0x0 }; };
template<> struct binary_nibble<1> { enum { b = 0x1 }; };
// Decimal values.
template<> struct binary_nibble<10> { enum { b = 0x2 }; };
template<> struct binary_nibble<11> { enum { b = 0x3 }; };
template<> struct binary_nibble<100> { enum { b = 0x4 }; };
template<> struct binary_nibble<101> { enum { b = 0x5 }; };
template<> struct binary_nibble<110> { enum { b = 0x6 }; };
template<> struct binary_nibble<111> { enum { b = 0x7 }; };
template<> struct binary_nibble<1000> { enum { b = 0x8 }; };
template<> struct binary_nibble<1001> { enum { b = 0x9 }; };
template<> struct binary_nibble<1010> { enum { b = 0xA }; };
template<> struct binary_nibble<1011> { enum { b = 0xB }; };
template<> struct binary_nibble<1100> { enum { b = 0xC }; };
template<> struct binary_nibble<1101> { enum { b = 0xD }; };
template<> struct binary_nibble<1110> { enum { b = 0xE }; };
template<> struct binary_nibble<1111> { enum { b = 0xF }; };
// Octal values.
template<> struct binary_nibble<010> { enum { b = 0x2 }; };
template<> struct binary_nibble<011> { enum { b = 0x3 }; };
template<> struct binary_nibble<0100> { enum { b = 0x4 }; };
template<> struct binary_nibble<0101> { enum { b = 0x5 }; };
template<> struct binary_nibble<0110> { enum { b = 0x6 }; };
template<> struct binary_nibble<0111> { enum { b = 0x7 }; };
// The following helper template determines the behavior of the
// binary_int template when default arguments are passed in.
// A default argument (0ul-1) will leave the previously calculated
// value unchanged. A non-default argument multiplies the previously
// calculated value by 16 and adds in the new (non-default) argument.
template<unsigned int v>
struct binary_int_arg {
enum {
b = binary_nibble<v>::b,
mul = 16,
};
};
template<>
struct binary_int_arg<0ul-1> {
enum {
b = 0,
mul = 1,
};
};
// Declare a binary value in the following form:
//
// int i = binary_int<1100,1010,0001>::b
//
// Usage:
//
// unsigned int = binary_int<up to 16 binary_nibble>::b
//
// Write an integer as a binary value composed of up to 16 nibbles.
// The tricky part is remembering to include the '::b' at the end.
// For example:
//
// #include "binary_int.h"
// ...
// int regValue = binary_int<1000,1001,0011,0000>::b;
//
// One to 16 nibble-sized arguments are allowed, which allows for word
// lengths up to 64 bits. If it's not working for you make sure that
// you are including the closing '::b'.
template<unsigned int b15,
unsigned int b14 = 0ul-1,
unsigned int b13 = 0ul-1,
unsigned int b12 = 0ul-1,
unsigned int b11 = 0ul-1,
unsigned int b10 = 0ul-1,
unsigned int b9 = 0ul-1,
unsigned int b8 = 0ul-1,
unsigned int b7 = 0ul-1,
unsigned int b6 = 0ul-1,
unsigned int b5 = 0ul-1,
unsigned int b4 = 0ul-1,
unsigned int b3 = 0ul-1,
unsigned int b2 = 0ul-1,
unsigned int b1 = 0ul-1,
unsigned int b0 = 0ul-1>
struct binary_int {
private:
enum {
v15_ = binary_int_arg<b15>::b,
v14_ = (v15_ * binary_int_arg<b14>::mul) + binary_int_arg<b14>::b,
v13_ = (v14_ * binary_int_arg<b13>::mul) + binary_int_arg<b13>::b,
v12_ = (v13_ * binary_int_arg<b12>::mul) + binary_int_arg<b12>::b,
v11_ = (v12_ * binary_int_arg<b11>::mul) + binary_int_arg<b11>::b,
v10_ = (v11_ * binary_int_arg<b10>::mul) + binary_int_arg<b10>::b,
v9_ = (v10_ * binary_int_arg<b9>::mul) + binary_int_arg<b9>::b,
v8_ = (v9_ * binary_int_arg<b8>::mul) + binary_int_arg<b8>::b,
v7_ = (v8_ * binary_int_arg<b7>::mul) + binary_int_arg<b7>::b,
v6_ = (v7_ * binary_int_arg<b6>::mul) + binary_int_arg<b6>::b,
v5_ = (v6_ * binary_int_arg<b5>::mul) + binary_int_arg<b5>::b,
v4_ = (v5_ * binary_int_arg<b4>::mul) + binary_int_arg<b4>::b,
v3_ = (v4_ * binary_int_arg<b3>::mul) + binary_int_arg<b3>::b,
v2_ = (v3_ * binary_int_arg<b2>::mul) + binary_int_arg<b2>::b,
v1_ = (v2_ * binary_int_arg<b1>::mul) + binary_int_arg<b1>::b,
v0_ = (v1_ * binary_int_arg<b0>::mul) + binary_int_arg<b0>::b,
};
public:
enum { b = v0_ };
};
#endif // BOOST_BINARY_INT_HPP
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk