Aditya
Aditya

Reputation: 117

Compile time comparison of template parameter

I have a requirement that, I should use a specific class if an integer passed as one of the template parameter is greater than a certain value. Otherwise, I should get a compile time error...

It is something like the following:

enum Time { Day, Week, Month };

template<Time t, int length>
class Timer
{
}

Now, I have to restrict instantiating Timer in such a way that -

Timer<Day,8> , Timer<Day,9> etc should work, but length cannot be less than 8 when used with Day.

Similarly, length cannot be less than 10 when used with Week and so on...

Can someone please help me out with how this can be achieved at compile-time?

Upvotes: 5

Views: 8004

Answers (5)

All of the other answers go for metaprogramming to detect the condition, I would on the other hand keep things simple:

template<Time t, int length>
class Timer
{
    static_assert( (t == Day && length > 7) 
                 ||(t == Week && length > 10)
                 ||(t == Month && length > 99), "Invalid parameters"
};

The compiler will trigger the assertion if the conditions are not met, and it is quite simple to verify by the error message and/or looking at the line.

Using SFINAE tools to disable versions of the type does also achieve the same result: the code will not compile, but at the cost of making the error messages more complex to read: what does it mean that Timer<Day,5> is not a type? surely it is, it is the instantiation of Timer<Time,int>!

EDIT: The above static_assert is implemented in C++0x, in compilers without C++0x you can implement static_assert as a macro:

#define static_assert( cond, name ) typedef char sassert_##name[ (cond)? 1 : -1 ];

This simple macro does not accept a string literal as second argument, but rather a single word. Usage would be:

static_assert( sizeof(int)==4, InvalidIntegerSize ) )

And error messages would require a bit of human parsing, as the compiler will complain (if the condition is not met) that the size of the sassert_InvalidIntegerSize is negative.

Upvotes: 7

iammilind
iammilind

Reputation: 69988

enum Time { Day, Week, Month };
template<Time T> struct Length;
template<> struct Length<Day> { static const int value = 8 ; };
template<> struct Length<Week> { static const int value = 9; };
template<> struct Length<Month> { static const int value = 10; };

template<bool b> struct Limit;
template<> struct Limit<true> { typedef bool yes; };

#define COMPILE_ASSERT(V) typedef typename Limit<(V)>::yes checked

template<Time t, int length>
class Timer
{
  COMPILE_ASSERT(length >= Length<t>::value);
};

See demo here.

Upvotes: 0

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361412

You can do this:

template<bool> 
struct rule;

template<> 
struct rule<true> {};

template<Time Tm, int Len>
struct constraint;

//Rule for Day     
template<int Len>
struct constraint<Day, Len> : rule<(Len>= 8)>
{};

template<Time Tm, int Len>
class Timer : constraint<Tm, Len>
{
   //your code
};

Test code:

int main() {
        Timer<Day, 7> timer1; //error 
        Timer<Day, 8> timer2; //okay
        return 0;
}

Online demos:


Similarly, you can add rules for Week and Month as:

//Rule for Week
template<int Len>
struct constraint<Week, Len> : rule<(Len>= 10)>
{};

//Rule for Month
template<int Len>
struct constraint<Month, Len> : rule<(Len>= 100)>
{};

Upvotes: 3

Matthieu M.
Matthieu M.

Reputation: 299810

The idea for this kind of validation is generally to hand off the work to a dedicated helper class, which you specialize for each kind of parameters.

template <typename T, size_t V>
class Foo
{
  static_assert(helper<T,V>::value, "Wrong Parameters");
};

There are two ways to perform the validation:

// Require full specialization
template <typename T, size_t V> struct helper: std::false_type {};

template <>
struct helper<Bar,0>: std::true_type {};

template <>
struct helper<Bar,4>: std::true_type {};


// Property like
template <typename T, size_t V> struct helper: std::false_type {};

template <size_t V>
struct helper<Bar, V>: std::integral_constant<bool, V <= 8> {};

Of course, those suppose C++0x facilities. In C++03, you'll have to provide the simple true_type, false_type and integral_constant classes yourself.

Upvotes: 2

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145269

Pass the result of length >= 8 as a bool template argument to helper template. Provide specialization for true only. Having said that, this sounds like homework, so I'll leave the coding to you.

Cheers & hth.

Upvotes: 6

Related Questions