C++11のお勉強(パターンマッチを作ってみる)

人並みにC++11が使えるように新機能を試してみました。
今回使った昨日は、λ、戻り値を後ろに置く関数構文、可変長引数テンプレート、コンパイラが生成する関数へのdefault/delete指定、メタプログラミングのための型特性な所です。

んで、出来たコードは以下となります。
まぁ、パターンマッチもどきですヽ(;´Д`)ノ

#include "extended_match.h"←今回書いたコード
struct tree { virtual ~tree(){}};
struct empty : public tree{};
struct leaf : public tree{};
struct node : public tree
{
  node( tree *l , tree *r )
    :l_(l),r_(r)
  {
  }
  tree *l_;
  tree *r_;
};

int depth( const tree & t )
{
  return extended_match::match
    ( t ,
	//empty型にマッチする
      extended_match::match_dynamic< empty , tree >( []( const empty & ref )
						     { 
						       std::cout << "de" << std::endl;
						       return 0;
						     }),
       //leaf型にマッチする
      extended_match::match_dynamic< leaf , tree >( []( const leaf & ref )
						    { 
						      std::cout << "dl" << std::endl;
						      return 1;
						    }),
	//node型にマッチする						    
      extended_match::match_dynamic< node , tree >( []( const node & ref )
						    { 
						      std::cout << "dn" << std::endl;
						      return 1 + std::max( depth(*ref.r_) , depth(*ref.l_) );
						    })
      );
}

int main()
{
  //木の深さを探索する
  node n( new leaf() , new node( new leaf() ,  new node( new leaf() , new leaf() ) ) );
  std::cout << "depth is " << depth( n ) << std::endl;
}

やっている事は、typeidを繋げて、条件判定しているだけなのですが...。
でも、ほぼ標準機能だけでここまで出来るC++って素敵(*´∀`)つ
(標準だけじゃ実現不能でboost::any使ってしまいましたm(__)m)

ちなみに、上記例では、動的な型判定だけを書きましたが、
ソレ以外のマッチも書けます。

#include "extended_match.h"←今回書いたコード
template < typename T >
void match_test( const T & t )
{

  try
    {
      const auto a = extended_match::match
	( t , 
//double型にマッチ
	  extended_match::match_type< const double >( [](const double & ref)
	{ 
	  std::cout << "double:" << ref << std::endl;
	  return "double";
	}),
//string型にマッチ
	  extended_match::match_type< const std::string >( [](const std::string & ref)
	{ 
	  std::cout << "string:" << ref << std::endl;
	  return "string";
	}),
//データにマッチ
	  extended_match::match_data< const std::vector< int > >( {1,2,3} ,
								  []( const  std::vector< int > & ref )
	{ 
	  std::cout << "vector 123" << std::endl;
	  return "vector 123";
	}),
//int型にマッチ
	  extended_match::match_type< const int >( []( const int & ref )
	{ 
	  std::cout << "int:" << ref << std::endl;
	  return "int";
	})	  
	  );

      std::cout << a << std::endl;

    }
  catch( const std::exception & e )
    {
      std::cout << e.what() << std::endl;
    }
}

int amin(){
  match_test( 10 );
  match_test( 10.0 );
  match_test( std::string("hoge") );
  
  {
    const std::vector< int > t = {1,2,3};
    match_test(t);
  }

  {
    const std::vector< int > t = {2,3};
    match_test(t);
  }
}

実現は以下コードでやっています。
(extended_match.hの中身です。)

//
//--[extended_match]--
//
#ifndef _EXTENDED_MATCH_
#define _EXTENDED_MATCH_

#include <typeinfo>
#include <stdexcept>
#include <boost/any.hpp>
#include <type_traits>

namespace extended_match
{
    template < typename CONDITION , typename lamba_type >
    struct match_type_holder
    {
    private:
      lamba_type l_;

    public:  
      typedef CONDITION CONDITION_TYPE;

      match_type_holder( const lamba_type & l )
      :l_(l)
      {}

      auto do_func( const boost::any & r ) -> decltype( l_( boost::any_cast< CONDITION_TYPE >( r ) )  ) 
      {
	return l_( *boost::any_cast< CONDITION_TYPE *>( r ) );
      }

      template < typename JUDGMENT_TYPE >
      bool is_match( boost::any r )
      {
	(void)(r);

	if( typeid( CONDITION_TYPE ) == typeid( JUDGMENT_TYPE ) )
	  {
	    return true;
	  }
	return false;
      }
  
    };

    template < typename CONDITION , typename lamba_type >
    struct match_data_holder
    {
    public:

      typedef CONDITION CONDITION_TYPE;
  
    private:
      CONDITION_TYPE c_;
      lamba_type l_;
  
    public:
      match_data_holder( const CONDITION_TYPE & c , const lamba_type & l )
	: c_(c) , l_(l)
      {}

      auto do_func( const boost::any & r ) -> decltype( l_( boost::any_cast< CONDITION_TYPE >( r ) ) ) 
      {
	return l_( *boost::any_cast< CONDITION_TYPE *>( r ) );
      }

      template < typename JUDGMENT_TYPE >
      bool is_match( boost::any r )
      {
	if( typeid( CONDITION_TYPE ) == typeid( JUDGMENT_TYPE ) )
	  {
	    if( *boost::any_cast< CONDITION_TYPE *>( r ) == c_ )
	      {
		return true;
	      }
	  }
	return false;
      }

    };

    template < typename OBJECT_TYPE , typename CONDITION , typename lamba_type >
    struct match_dynamic_holder
    {

    public:

      typedef CONDITION CONDITION_TYPE;
  
    private:
      lamba_type l_;
  
    public:
      match_dynamic_holder( const lamba_type & l )
      :  l_(l)
      {}

      auto do_func( const boost::any & r ) -> decltype( l_( dynamic_cast< const OBJECT_TYPE & >( *boost::any_cast< const CONDITION_TYPE * >( r ) ) ) ) 
      {
	return l_( dynamic_cast< const OBJECT_TYPE & >( *boost::any_cast< const CONDITION_TYPE * >( r )  ) );
      }


      template < typename JUDGMENT_TYPE >
      bool is_match( boost::any r , typename std::enable_if< std::is_base_of< JUDGMENT_TYPE , OBJECT_TYPE >::value >::type* = 0)
      {
	try
	  {
	    const JUDGMENT_TYPE * t = boost::any_cast< const JUDGMENT_TYPE * >( r );
	    const OBJECT_TYPE & ot = dynamic_cast< const OBJECT_TYPE & >(*t);
	    (void)(ot);
	    return true;
	  }
	catch( const std::exception & e )
	  {
	    std::cout << e.what() << std::endl;
	  }

	return false;
      }


      template < typename JUDGMENT_TYPE >
      bool is_match( boost::any r , typename std::enable_if< !std::is_base_of< JUDGMENT_TYPE , OBJECT_TYPE >::value >::type* = 0)
      {
	return false;
      }

    };

  template < typename CONDITION , typename T >
  match_type_holder< CONDITION , T > match_type( const T & t )
  {
    return std::move( match_type_holder< CONDITION , T >( t ) );
  }

  template < typename CONDITION , typename T >
  match_data_holder< CONDITION , T > match_data( const CONDITION & cond , const T & t )
  {
    return std::move( match_data_holder< CONDITION , T >( cond , t ) );
  }

  template < typename OBJECT_TYPE , typename CONDITION , typename lamba_type >
  match_dynamic_holder< OBJECT_TYPE , CONDITION , lamba_type > match_dynamic( const lamba_type & t )
  {
    return std::move( match_dynamic_holder< OBJECT_TYPE , CONDITION , lamba_type >( t ) );
  }

  template < typename COND , typename LAMBDA_T >
  auto match( const COND & c , LAMBDA_T t ) -> decltype( t.do_func( c ) ) 
  {
    if( true == t.LAMBDA_T::template is_match< COND >( &c ) )
      {
	return t.do_func( &c );
      }

    std::cout << typeid(COND).name() << std::endl;
    throw std::runtime_error("no conditions!!!");
  }

  template < typename COND , typename LAMBDA_T , typename... Args >
  auto match( const COND & c , LAMBDA_T t , Args... args  ) -> decltype( match( c , args... ) ) 
  {

    if( true == t.LAMBDA_T::template is_match< COND >( &c ) )
      {
	return t.do_func( &c );
      }
    else
      {
	return match( c , args... );
      }
  }
}
#endif