19

I want to create a structure that allocates statically an array of 2^N bytes, but I don't want the users of this structure to specify this size as the exponent. Example:

my_stupid_array<char, 32> a1; // I want this!
my_stupid_array<char, 5> a2; // And not this...

How do I check if this template parameter is a power of two and warn the user with a nice message about this?

I've been able to check for this with a simple template:

template<int N>
struct is_power_of_two {
    enum {val = (N >= 1) & !(N & (N - 1))};
};

However, I'm unable to warn the user about this with a sane message. Any ideas?

EDIT

Fixed the ambiguous example.

EDIT

1 is a power of two indeed. Fixed that! :)

EDIT

Using BOOST_STATIC_ASSERT, I'm getting this compile error for this code with GCC:

template<int N>
struct is_power_of_two {
    enum {val = (N >= 1) & !(N & (N - 1))};
    BOOST_STATIC_ASSERT(val);
};

Error

..\main.cpp:29:1: error: invalid application of 'sizeof' to incomplete type 'boost::STATIC_ASSERTION_FAILURE<false>' 

http://ideone.com/cMfEf

EDIT

Oh, I get it. That was the message that I'm supposed to get when the assert fails. But that fails to give the user some sane message. :(

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
ivarec
  • 2,542
  • 2
  • 34
  • 57

3 Answers3

27

These days, with constexpr and the bit twiddling hacks you can just

constexpr bool is_powerof2(int v) {
    return v && ((v & (v - 1)) == 0);
}
sehe
  • 374,641
  • 47
  • 450
  • 633
23

static_assert to the rescue (C++11 only, uncomment BOOST_STATIC_ASSERT for C++03):

#include<iostream>
// #include <boost/static_assert.hpp>

template<int N>
struct is_power_of_two {
    enum {val = N && !(N & (N - 1))};
    static_assert(val, "should use a power of 2 as template parameter");
    // BOOST_STATIC_ASSERT(val); // without C++11 support, won't take a string message
};

int main()
{
        std::cout << is_power_of_two<2>::val << "\n";
        std::cout << is_power_of_two<3>::val << "\n";
}

Ideone output for C++11

Ideone output for C++03

UPDATE1: other idea (I know you don't want this, but it is a lot easier for large exponents):

template<int N>
make_power_of_two
{
    enum { val = 1 << N };
};

my_stupid_array<char, make_power_of_two<5>::val > a1; // size 2^5 = 32

UPDATE2: based on comments by @sehe in the chat, you can do this for constexpr functions as well

constexpr bool is_power_of_two(int x)
{
    return x && ((x & (x-1)) == 0);
}
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 1
    Beat me to it. Might as well mention BOOST_STATIC_ASSERT for C++03. – jrok May 14 '12 at 14:41
  • Damnit. Was just about to give [this link](http://www.boost.org/doc/libs/1_49_0/doc/html/boost_staticassert.html). I have to learn to type faster. – Eitan T May 14 '12 at 14:42
  • 1
    My embedded compiler won't do C++11 yet. I'll try the boost alternative and add this to the answer if it works. – ivarec May 14 '12 at 14:45
  • Very nice... Although strictly speaking 1 *is* a power of 2. :-) – Grimm The Opiner May 14 '12 at 15:01
  • 1
    BOOST_STATIC_ASSERT accepts only one parameter. Also, I'm getting a compile error that I'm trying to figure it out. Gonna update the question. – ivarec May 14 '12 at 15:05
  • @haole Updated the answer with Ideone output for BOOST_STATIC_ASSERT for C++03 compilers. The BOOST_STATIC_ASSERT_MSG variant takes a string message as well, but this only works for C++11. Note that the compile-time erorr is correct! (This is what static asserts do...) – TemplateRex May 14 '12 at 15:09
  • 1
    What you can always do with `BOOST_STATIC_ASSERT` to include a message is this: `BOOST_STATIC_ASSERT(val && "This is SO wrong!")`. (For those who wonder: a string literal in a boolean context evaluates the pointer to the literal which is always non-zero, i.e. true) – Arne Mertz Dec 13 '13 at 08:29
  • @ArneMertz thanks yes, that is the same as with regular `assert(cond && "message")`. I guess I should have phrased it slightly differently, since my main point was that `static_assert` requires a message (can be "", but still annoying to type). – TemplateRex Dec 13 '13 at 09:24
11

You can use static_assert to provide an error message:

template<int N>
struct is_power_of_two {
    static_assert((N > 1) & !(N & (N - 1)), "Template parameter must be a power of two.");
};
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249