More C++ Idioms/Int-To-Type

Int-To-Type

edit

Intent

edit
  • To treat an integral constant as a type at compile-time.
  • To achieve static call dispatch based on constant integral values.

Also Known As

edit

Integral constant wrappers

Motivation

edit

Function overloading in C++ is based on different types, which prevents compile-time integral constants taking part into function overload resolution. There are at least two different mechanisms to perform static dispatching based on integral constants. First is enable-if idiom and the other one is the int-to-type idiom described below.

Solution and Sample Code

edit

A simple template, initially described in Dr. Dobb's Journal by Andrei Alexandrescu provides a solution to this idiom.

template <int I>
struct Int2Type
{
  enum { value = I };
};

The above template creates different types for different integer values used to instantiate the template. For example, Int2Type<5> is a different type from Int2Type<10>. Moreover, the integral parameter is saved in the associated constant value. As each integral constant results in a different type, this simple template can be used for static dispatching based on integral constants as shown below.

Consider an Array class that encapsulates a fixed size array very similar to that of the standard array class from TR1. In fact, our Array class is implemented as a derived class of the standard TR1 array class with the only difference of a sort function.

We intend to dispatch sort function at compile-time based on the size of the array to achieve some performance optimizations. For example, sorting an array of size zero or one should be a no-op. Similarly, arrays smaller than size 50 should be sorted using the insertion-sort algorithm whereas as larger arrays should be sorted using the quick-sort algorithm because insertion-sort algorithm is often more efficient than quick-sort algorithm for small data size. Note that, this selection of the sorting algorithm can be trivially done using run-time if condition. However, int-to-type idiom is used to achieve the same effect at compile-time as shown below.

#include <iostream>
#include <array>

template <int I>
struct Int2Type
{
  enum { value = I };
};

template <class T, unsigned int N>
class Array : public std::array <T, N>
{
   enum AlgoType { NOOP, INSERTION_SORT, QUICK_SORT };
   static const int algo = (N==0) ? NOOP : 
                           (N==1) ? NOOP :
			   (N<50) ? INSERTION_SORT : QUICK_SORT;
   void sort (Int2Type<NOOP>) { std::cout << "NOOP\n"; }
   void sort (Int2Type<INSERTION_SORT>) { std::cout << "INSERTION_SORT\n"; }
   void sort (Int2Type<QUICK_SORT>) { std::cout << "QUICK_SORT\n"; }
 public:
   void sort()
   {
     sort (Int2Type<algo>());
   }
};
int main(void)
{
  Array<int, 1> a;
  a.sort(); // No-op!
  Array<int, 400> b;
  b.sort(); // Quick sort  
}

Few more associated types and constants can be defined with Int2Type template to increase its usability. For example, enumeration value is used to retrieve the integer constant associated with the type. Finally, other typedefs such as, next and previous are used to find other types in order such that Int2Type<7>::next is the same type as Int2Type<9>::previous.

template <int I>
struct Int2Type
{
  enum { value = I };
  typedef int value_type;
  typedef Int2Type<I> type;
  typedef Int2Type<I+1> next;
  typedef Int2Type<I-1> previous;
};

Known Uses

edit
edit

References

edit

[1] Generic<Programming>: Mappings between Types and Values -- Andrei Alexandrescu