views:

482

answers:

5

The following fragment returns the next highest power of two for an (assumed unsigned) integer of type T. It does this by shifting the bits repeatedly.

For all intents and purposes the unsigned type i used in the bit shifting loop is sufficiently large to represent a (nominally) 65536 bit number. Practically therefore it's fine to leave it 'as is'.

template <class T>
T supremum_2(T k) {
  if (k == T(0)) return T(1);
  k--;  
  for (int i=1; i<sizeof(T)*8; i++) k |= k >> i;
  return k+1;
}

To do a professional job, the type of the loop counter should be chosen at compile time so that it guarantees to be able to span up to sizeof(T)*8 without overflow.

Can this be done at compile time using std::numeric_traits? If so how?

Conceptually I would like to be able to write something like:

typedef unsigned_type_that_can_represent<sizeof(T)*8> counter_type;

...
...

for (counter_type i(1); i<sizeof(T)*8; i<<=1) k = k | k >> i;
...

Based on the discussions below I have decided to add the following context.

Put another way:

How can we guarantee to select efficient (only as big as they need to be) and suitable types for the internal workings of template code at compile time? If we find ourselves using concrete types in template code we may be making inadvertent assumptions about the types of the templates through a potentially opaque route.

For example, if we were to stick with (say) an int for the counter, all will work fine until someone uses the template code with their bigint library. This could represent integers with more binary digits than can be represented by an int. Should we therefore make the type unsigned long long? Surely that just delays the problem (albeit for a long time)? There are shades of "640K - big enough for everybody" or static array sizes about this solution.

The obvious, but somewhat inefficient, choice in this instance is to set the type of the counter to be the same as the type of the number k. It is (in principle) inefficient since we only demand that the counter be able to represent a number corresponding to the number of bits of k. It may be that for other situations this is the wrong thing to assume.

What about the general case? It looks as though meta-programming is an appropriate approach. How to keep this 'sane'? Perhaps, formally, the requirement is for a compile-time function to map (potentially derived) abstract type requirements on to types.

Perhaps it's a job for YABL (Yet Another Boost Library)!

[Apologies for rambling]

A: 

Check up on static asserts, that might be a solution to your problem.

#include <climits>
#include <cwchar>
#include <limits>
#include <boost/static_assert.hpp>

namespace my_conditions {

   BOOST_STATIC_ASSERT(std::numeric_limits<int>::digits >= 32);
   BOOST_STATIC_ASSERT(WCHAR_MIN >= 0);

} // namespace my_conditions
Robert Gould
A: 

You can use meta proramming:

template <bool g, typename T, typename E>
struct IF {
    typedef T RET;
};

template < typename T, typename E>
struct IF<false, T, E> {
    typedef E RET;
};

// if sizeof(int) < sizeof(long) then use long else use int
IF< sizeof(int)<sizeof(long), long, int>::RET i;
Artem Barger
A: 

This can be done using a traits implementation. Something like this:

// base type for template
template <class T>
struct counter_type
{
};

// specialisation for unsigned integer
template <>
struct  counter_type <unsigned>
{
  typedef unsigned long long value_type; // or whatever
};

template <class T>
T supremum_2(T k) {
  if (k == T(0)) return T(1);
  k--;
  /* Determined by repeated bit shifting. */
  for (counter_type<T>::value_type i=1; i<sizeof(T)*8; i<<=1) k = k | k >> i;
  return k+1;
}

Please forgive me if I get the syntax wrong, I always have to look up template syntax. The general idea is here.

1800 INFORMATION
+1  A: 

I believe you wanted to write your loop as

for (int i=1; i<sizeof(T)*8; i++) k |= k >> i;
return k+1;

An int can at least store values up to 2^15-1, which is more than enough for this. Nonetheless, here is how i do it

template<int N, bool B8  = (N>8), 
                bool B16 = (N>16), 
                bool B32 = (N>32)>
struct select_t;

template<int N>
struct select_t<N, false, false, false> { typedef unsigned char type; };

template<int N>
struct select_t<N, true, false, false> { typedef unsigned short type; };

template<int N>
struct select_t<N, true, true, false> { typedef unsigned long type; };

int main() { select_t<32>::type i = 0; } // unsigned long

You could do it with unsigned long long too, if you happen to have that type available in your compiler.

Johannes Schaub - litb
I would have used <T,S=sizeof(T)> as a template argument and specialized based on that.
Robert Gould
yeah. I did it like that first (divided by CHAR_BIT). But it turned out i had to do repeated ones for 3 and 4. And if he adds long long, he needs to repeat for all 5, 6, 7 and 8 :) So i figured i better do it with op> :)
Johannes Schaub - litb
If my understanding is correct, this will resolve to a type based on the minimum number of bits required - e.g. 32.My problem is a little more subtle than this, as the argument to your template parameter I think there needs to be something else which tells us the minimum number of bits required to represent a given integer. e.g.select_t<num_bits_to_represent<8*sizeof(T)> >::type i(0);that way, if T was (say) a char we would only need to have a counter that would be capable of representing up to the number 8 (4-bits) so the counter type could be some (fictitious) type 'tiny'.
Edward Grace
You don't need to check anything, because 8*sizeof(T) fits in an int (and even in an unsigned char) everywhere, even for long long. Why do you want to make that this complicated? As your loop is incorrect as written, please correct it, so that we have any chance at understanding what you mean.
Johannes Schaub - litb
I've modified my loop as per your suggestion."Why do you want to make that this complicated?"Oh, I don't - in practice I'd make the counter type T, that way we're safe even if T is a big int (limited by computer memory). My question is a more abstract one regarding template programming. How can we (sanely) guarantee to select efficient (only as big as they need to be) and suitable types for the internal workings of template code at compile time? If we find ourselves using concrete types in template code we may be making inadvertent assumptions about the types of the templates => bugs!
Edward Grace
+1  A: 

In fact, you're right.

But in reality, if your T would be 128 bit, the highest number of shift operations would be 127, neatly fitting a char...

So aren't you over-engineering the loop variable type a bit? (May be due to the shift operator i.s.o. plain increment, as pointed out by litb)

xtofl