An easier way to select types

The Boost Template Meta-Programming Library is a very powerful library but there are some things that become necessary that can make things really tough to read. One example is having to nest if ‘blocks’ instead of having an ‘else if’. Considder

template < typename T >
struct match_type
  : mpl::if_
    <
      std::is_same<T, mpl::true>
    , double
    , if_
      <
        std::is_same<T, int>
      , char*
      , long
      >::type
    >
{};

Imagine that this went on for 5 checks or more. It becomes quite ungainly.

With this specific example we could use an mpl::map and things would be much easier. What if we where doing something else though? What if we really did need to run some metafunction on T in order to decide?

So I decided to create something a bit simpler to work with, a sort of “select/case” mechanism though at this time it doesn’t exactly match the same semantics as your standard switch statement. At any rate, here it is:


#include <boost/mpl/identity.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/mpl/placeholders.hpp>
#include <type_traits>

using namespace boost;
using namespace boost::mpl::placeholders;

template < typename Test, typename Result >
struct case_ : Result 
{
};
template < typename Result >
struct default_ : Result {};

struct null_ {};

template < typename Switch
         , typename C1 = null_
         , typename C2 = null_
         , typename C3 = null_
         , typename C4 = null_ >
struct select_ {};

template < typename Switch, typename C1Test, typename C1Result, typename C2, typename C3, typename C4 >
struct select_<Switch, case_<C1Test, C1Result>, C2, C3, C4>
  : mpl::if_
    <
      typename C1Test::template apply<Switch>::type
    , C1Result
    , select_<Switch, C2, C3, C4>
    >::type
{
};

template < typename Switch, typename C1Result, typename C2, typename C3, typename C4 >
struct select_<Switch, default_<C1Result>, typename C2, typename C3, typename C4 >
  : C1Result
{};

// test code...

#include <iostream>
#include <typeinfo>


template < typename Var >
struct test_select
  : select_
    < Var
    , case_
      <
        mpl::lambda< std::is_same<_, mpl::true_> >::type
      , mpl::identity<double> 
      >
    , case_
      <
        mpl::lambda< std::is_same<_, int> >::type
      , mpl::identity<char*>
      >
    , default_< mpl::identity<long> >
    >
{};


// select as metafunction class...

template < typename C1 = null_
         , typename C2 = null_
         , typename C3 = null_
         , typename C4 = null_ >
struct select
{
  template < typename Switch >
  struct apply : select_<Switch, C1,C2,C3,C4> {};
};

// test select as metafunction class.
typedef select
    < case_
      <
        mpl::lambda< std::is_same<_, mpl::true_> >::type
      , mpl::identity<double> 
      >
    , case_
      <
        mpl::lambda< std::is_same<double, int> >::type
      , mpl::identity<char*>
      >
    , default_< mpl::identity<long> >
    > my_select;

// visually verify answer
int main()
{
  std::cout << typeid( test_select<double>::type ).name() << std::endl;
  std::cout << typeid( my_select::apply<int>::type ).name() << std::endl;
}

This could be easily adjusted to run the second parameter to ‘case_’ as a metafunction instead of simply grabbing a type.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: