views:

65

answers:

5

I have a following templated struct:

template<int Degree>
struct CPowerOfTen {
enum { Value = 10 * CPowerOfTen<Degree - 1>::Value };
};

template<>
struct CPowerOfTen<0> {
    enum { Value = 1 };
};

which is to be used like this:

const int NumberOfDecimalDigits = 5;
const int MaxRepresentableValue = CPowerOfTen<NumberOfDecimalDigits>::Value - 1;
// now can use both constants safely - they're surely in sync

now that template requires Degree to be non-negative. I'd like to enforce a compile-time assert for that.

How do I do that? I tried to add a destructor to CPowerOfTen:

~CPowerOfTen() {
    compileTimeAssert( Degree >= 0 );
 }

but since it is not called directly Visual C++ 9 decides not to instantiate it and so the compile-time assert statement is not evaluated at all.

How could I enforce a compile-time check for Degree being non-negative?

+4  A: 

You can use a uint. You won't get a compile time error but at least it will be self-documenting.

DanDan
Exactly. Non-negative numbers are only one failure. What about CPowerOfTen<10000>? This should not work anyway.
harper
NEVER use uint to enforce that a value be nonnegative. In this case, should -1 be passed, it will be converted to some gigantic value and passed as a template argument, your compiler will hang, and you won't know what to do. Fortunately, the upcoming C++0x provides static assert with a custom message to report in case of assertion failure
Armen Tsirunyan
+3  A: 

You can use BOOST_STATIC_ASSERT macro. Or implement your own, the simplest way of forcing a failure is performing a typedef of an array of N elements, where N is positive/negative depending on the argument.

The problem with that approach is that it will produce a failure, but will try to perform the recursion nonetheless. Take a look at boost::enable_if_c to use SFINAE to fail instantiating the template if the argument is negative.

David Rodríguez - dribeas
+4  A: 
template<bool> struct StaticCheck;
template<> struct StaticCheck<true> {};

template<int Degree> 
struct CPowerOfTen : StaticCheck<(Degree > 0)> { 
    enum { Value = 10 * CPowerOfTen<Degree - 1>::Value }; 
}; 

template<> 
struct CPowerOfTen<0> { 
    enum { Value = 1 }; 
}; 

Edit: without infinite recursion.

// Help struct
template<bool, int> struct CPowerOfTenHelp;

// positive case    
template<int Degree> 
struct CPowerOfTenHelp<true, Degree> { 
    enum { Value = 10 * CPowerOfTenHelp<true, Degree - 1>::Value }; 
}; 

template<> 
struct CPowerOfTenHelp<true, 0> { 
    enum { Value = 1 }; 
}; 

// negative case
template<int Degree> 
struct CPowerOfTenHelp<false, Degree> {}

// Main struct
template<int Degree> 
struct CPowerOfTen : CPowerOfTenHelp<(Degree >= 0), Degree> {};
Alexey Malistov
Not stopping the compile-time recursion is a big problem, though?
visitor
+3  A: 

How about implementing a STATIC_CHECK macro?

template<bool> struct CompileTimeError;
template<> struct CompileTimeError<true> {}; //specialized only for true

#define STATIC_CHECK(expr)  (CompileTimeError<(expr) != 0>())

Inside main()

 const int NumberOfDecimalDigits = -1;
 STATIC_CHECK(NumberOfDecimalDigits > 0); // Error : invalid use of incomplete type struct CompileTimeError<false>

 const int MaxRepresentableValue = CPowerOfTen<NumberOfDecimalDigits>::Value - 1; 
Prasoon Saurav
The drawback of this solution is that the check is separate from the `struct`.
sharptooth
A: 

You can forward the implementation to a class also accepting a bool parameter indicating whether the result can be calculated.

#include <limits>
template <int Degree, bool InRange>
struct PowerOfTenImpl
{
    enum {Value = 10 * PowerOfTenImpl<Degree - 1, InRange>::Value};
};

template <>
struct PowerOfTenImpl<0, true>
{
    enum {Value = 1};
};

template <int Degree>
struct PowerOfTenImpl<Degree, false>
{
};

template<int Degree>
struct CPowerOfTen {
    enum { Value = PowerOfTenImpl<Degree, Degree >= 0 && 
      Degree <= std::numeric_limits<int>::digits10>::Value };
};

int main()
{
    const int a = CPowerOfTen<4>::Value;
    const int b = CPowerOfTen<1000>::Value;
    const int c = CPowerOfTen<-4>::Value;
}
visitor