Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 915
Aditya Avatar asked Jun 30 '11 06:06

Aditya


People also ask

Are templates compile-time or runtime?

All the template parameters are fixed+known at compile-time. If there are compiler errors due to template instantiation, they must be caught at compile-time!

What is compile-time parameter?

Compile time parameters are parameters utilized during the compilation of a template. These parameters can only be used via the SparkleFormation library. It is for this reason that compile time parameters are generally discouraged from use.

How long template parameters will be valid?

3. What is the validity of template parameters? Explanation: Template parameters are valid inside a block only i.e. they have block scope.

Are template functions slower?

Heavy use of templates can however lead to long compile times. Mostly right, but increased code size due to multiple instantiations of a template function can increase instruction cache misses, and slow down your program.


4 Answers

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.

like image 81
David Rodríguez - dribeas Avatar answered Oct 01 '22 14:10

David Rodríguez - dribeas


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.

like image 32
Cheers and hth. - Alf Avatar answered Oct 01 '22 14:10

Cheers and hth. - Alf


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:

  • http://www.ideone.com/tSBhG ( Timer<Day, 7> timer1 : commented )
  • http://www.ideone.com/mbN6m ( Timer<Day, 7> timer1 : not commented )

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)>
{};
like image 38
Nawaz Avatar answered Oct 01 '22 13:10

Nawaz


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.

like image 34
Matthieu M. Avatar answered Oct 01 '22 12:10

Matthieu M.