#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/type_traits/is_same.hpp>
#include <iostream>

using namespace std;

namespace mpl = boost::mpl;

//using namespace mpl::placeholders;

template<bool condition, class Then, class Else>
struct IF
{
  typedef Then RET;
};

template<class Then, class Else>
struct IF<false, Then, Else>
{
  typedef Else RET;
};

/////////// SWITCH

const int DEFAULT = ~(~0u >> 1);

const int nilValue = DEFAULT+1;

struct NilCase
{
};

template<int tag_, class Type_, class Next_ = NilCase>
struct CASE
{
  enum { tag = tag_ };
  typedef Type_ Type;
  typedef Next_ Next;
};

template<int tag, class Case>
class SWITCH
{
  typedef typename Case::Next NextCase;
  
  enum
    {
      caseTag = Case::tag,
      found = (caseTag == tag || caseTag ==DEFAULT)
    };

public:

  typedef typename IF<found,
		      typename Case::Type,
		      typename SWITCH<tag, NextCase>::RET>::RET RET;
  
};

template<int tag>
class SWITCH<tag, NilCase>
{
public:
  typedef NilCase RET;
};

////////// END SWITCH


enum tagit
  {
    one,
    two,
    three
  };

struct First
{
  static void func()
  {
    cout << "First" << endl;
  }
};



struct Second
{
  static void func()
  {
    cout << "Second" << endl;
  }
};


struct Third
{
  static void func()
  {
    cout << "Third" << endl;
  }
};


struct Default
{
  static void func()
  {
    cout << "Third" << endl;
  }
};


typedef mpl::map<
                 mpl::pair<mpl::integral_c<tagit, one>, First>,
                 mpl::pair<mpl::integral_c<tagit, two>, Second>,
                 mpl::pair<mpl::integral_c<tagit, three>, Third>
                > funcchooser;

template<tagit ti>
void funcCaller()
{
  mpl::eval_if<
               boost::is_same<mpl::integral_c<tagit, ti>, mpl::integral_c<tagit, one> >, 
               mpl::identity<First>, 
               mpl::eval_if<
                            boost::is_same<mpl::integral_c<tagit, ti>, mpl::integral_c<tagit, two> >, 
                            mpl::identity<Second>, 
                            mpl::eval_if<
                                        boost::is_same<mpl::integral_c<tagit, ti>, mpl::integral_c<tagit, three> >,
                                        mpl::identity<Third>, 
                                        mpl::identity<Default>
                                        >
                           >
              >::type::func();

  SWITCH<ti, CASE<one, First, CASE<two, Second, CASE<three, Third> > > >::RET::func();

  mpl::at<funcchooser, mpl::integral_c<tagit, ti> >::type::func();
  
}
int main()
{
  funcCaller<one>();
  funcCaller<two>();
  funcCaller<three>();
  
  return 0;
}

