tags:

views:

147

answers:

6

I'm trying to learn C++ by doing everything "the C++ way". I'm writing a program where I have all of these known values (at compile time). Here is my problem:

In my constructor I want to check to see if a passed value(an int) is one of 2,4,8,16 or 32 and throw an error elsewise. I've though about:

  1. making a C style array or ints
  2. creating a vector before by hand and interating through it to check
  3. making a list? I've never used lists before though.

What I really want to do is make a const vector in a separate header file, this doesn't seem possible though.

What is the most elegant way to do this check?

Also, similarly is there any way to make a vector with a set number of known values (at compile time) in a header? If I can do const std::string a_str("a string"); I don't see why I can't use a similar technique for vectors.

+7  A: 

What's wrong with:

if (!(n == 2 || n == 4 || n == 8 || n == 16 || n == 32))
{
    // no!
}

If you want the "C++ way", a static array should do, with find:

template <typename T, size_t N>
T* endof(T (&pArray)[N])
{
    return &pArray[0] + N;
}

static const int OkNumbers[] = {2, 4, 8, 16, 32};
static const int* OkNumbersEnd = endof(OkNumbers);
if (std::find(OkNumbers, OkNumbersEnd, n) == OkNumbersEnd)
{
    // no!
}

Modifying this list is simple, and I'm guessing the compiler may optimize this to my previous answer.

GMan
he wants to do it "the C++ way"
hhafez
This isn't C++ code? Anyone who set up a list just to check for a few numbers is doing too much work, I think.
GMan
+1 for KISS ---
Qberticus
I didn't say it isn't C++, he said he wants to do it "the C++ way" what ever that's supposed to mean. I agree it is too much work but he might just be doing it for an exercise
hhafez
I can has down-vote reason? :)
GMan
Instead of the `endof` method, try this: `OkNumbersEnd = OkNumbers + sizeof(OkNumbers) / (OkNumbers[0]);` which can be calculated at compile time and no need for a template.
Thomas Matthews
Your code is more verbose and mine is compile time as well. It's not like templates are a bad thing.
GMan
Not to mention yours can only be turned into a macro, which is less preferable to a function.
GMan
+6  A: 

It's a bit of a trick, but I believe this works:

if (n & (n-1) != 0)
{
   // not a power of two
}
Paul Tomblin
I figured he would only want the 5 numbers listed in the OP. Perhaps a range check would make it appropriate.
GMan
nice and elegant but doesn't work with excluding 64, 128 etc ...
hhafez
ndim
hhafez: Well, adding a `(n > 32)` comparison is trivial.
ndim
Chris Lutz
@ndim - It's not a perfect check, but it's probably more efficient (and easier to maintain) than testing against each specific value.
Chris Lutz
I guess it only works for `n>=1`, or if counting `0` as a power of two does not matter.
ndim
+1  A: 

If you are really looking for doing it with a vector and want nice assignment operations have a look at boost::assign.

But you just really don't want to do it with a vector ;)

Edit: I just saw your "at compile time". Consider why this can't be done: std::vector isn't a built in type. To have a mechanism to use somekind of smart assignment like that would require to built in support for it for the whole language and every user-defined type. Even if you don't need core language support and can do it with Templates this wouldn't be consistent with the overall style of the STL.

pmr
Is string a built in type?
devin
std::string is not a built-in type, and the code you gave in the question doesn't create a string at compile time. It creates a string at runtime and initializes it with a C-style string literal. std::string just happens to have that nice way of initialising it, which vector doesn't. C++0x will introduce initializer lists, which will let you do `std::vector<int> v = {1, 2, 3};` or `std::vector<std::string> v2 = {"1", "2", "3"};`
Steve Jessop
No. The constructor you are reffering to takes const char* as an argument. This pointer is taken from the string literal "a string". String literals are "built-in".
pmr
Yes, but when devin says "is string a built-in type", the answer must be "no". By "string", he either means "std::string" (which is not a built-in type), or else he means "string literal" (which is not a type at all). Everywhere in my above comment where I say "string" I mean "std::string", except where I say "C-style string literal".
Steve Jessop
A: 

Use a normal C array, my C is rusty but here goes

int array[] = {2,4,8,16,32};

/* now loop over the array and check */

for( i = 0; i< size_of_array ; i++) {
  if (array[i] == input_int) 
  /* you get the idea ..... */
hhafez
A: 

This might not fit the context of what you're trying to do, but you can use an enum.

Since this is compile time knowledge, I'll assume that these passed values are important at compile time. Using an enum, callers aren't trying to figure out what magic number to pass into your function.

typedef enum 
{
  Value1 = 2,
  Value2 = 4,
  Value4 = 8,
  Value5 = 16,
  Value6 = 32

} MyMagicType;

void MyFunction(MyMagicType theType)
{
...
}

The compiler will then enforce the value to be one of the above (well, unless you cast it, but that is a different matter) and throw an error should it not be one of the defined values.

Nathan
+2  A: 

If we're talking about bit twiddling sillyness, here's my try:

if ((n & 0x3E) != n || (n & n - 1) != 0)
  throw std::runtime_error("not a power of two less than or equal to 32");

Bit twiddling sillyness is VERY C/C++, but only "elegant" if by elegant you mean "as few processor cycles as absolutely possible and in as terse a syntax as possible". Use a dictionary lookup or explicit check (such as std::find in GMan's answer), otherwise.

Readability is almost always preferrable to this kind of wackyness.

Merlyn Morgan-Graham