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