/*
 * John Martin: john.p.martin@eudoramail.com
 *
 * This file demostrates the typeselect utility template.  This file utilizes
 * code derived from Andrei Alexandrescu's book: Modern C++ Design, Design Patterns
 * and Generic Programming Applied published by Addison Wesley.  His code is free
 * to use and so is mine.  Enjoy.
 *
 */

#include <iostream>
using namespace std;

// used to terminate typelists

class null_type {};

// used to recursively create lists of types

template <class U, class V>
struct typelist
{
  typedef U head_type;
  typedef V tail_type;
};

// helper macros for managing typelists

#define TL_1(T1) typelist<T1, null_type>
#define TL_2(T1,T2) typelist<T1, TL_1(T2) >
#define TL_3(T1,T2,T3) typelist<T1, TL_2(T2,T3) >
#define TL_4(T1,T2,T3,T4) typelist<T1, TL_3(T2,T3,T4) >

// used to choose between two types at compile time

template <bool Expr, class U, class V>
struct select_type
{
  typedef U result_type;
};

// specialization for false conditions

template <class U, class V>
struct select_type<false, U, V>
{
  typedef V result_type;
};

// choose a type based on a specific size

template <unsigned int Size, class TList>
struct select_type_size;

template <unsigned int Size, class U, class V>
struct select_type_size<Size, typelist<U, V> >
{
  typedef typename select_type
                   <
                   Size == sizeof(U),
                   U,
                   typename select_type_size<Size, V>::result_type
                   >::result_type result_type;
};

// specialization if a type is not found use null_type to generate a compile time error

template <unsigned int Size>
struct select_type_size<Size, null_type>
{
  typedef null_type result_type;
};

// define a list of types

typedef TL_4(signed char, short int, int, long int) signed_ints;

typedef TL_4(unsigned char, unsigned short, unsigned int, unsigned long)
  unsigned_ints;

// now when we port to different platforms we have guaranteed fixed size types
// or the compiler generates an error if we use an invalid type.

typedef select_type_size<1, signed_ints>::result_type int8_t;
typedef select_type_size<2, signed_ints>::result_type int16_t;
typedef select_type_size<4, signed_ints>::result_type int32_t;

typedef select_type_size<1, unsigned_ints>::result_type uint8_t;
typedef select_type_size<2, unsigned_ints>::result_type uint16_t;
typedef select_type_size<4, unsigned_ints>::result_type uint32_t;

// what select_type_size does is iterate through a list of types at compile
// time looking for a type of a specific size, when its found it sets 

int main(int argc, char** argv)
{

  cout << "sizeof(int8_t) == " << sizeof(int8_t) << endl;
  cout << "sizeof(int16_t) == " << sizeof(int16_t) << endl;
  cout << "sizeof(int32_t) == " << sizeof(int32_t) << endl;

  cout << "sizeof(uint8_t) == " << sizeof(uint8_t) << endl;
  cout << "sizeof(uint16_t) == " << sizeof(uint16_t) << endl;
  cout << "sizeof(uint32_t) == " << sizeof(uint32_t) << endl;

  return 0;
}