随笔 - 31  文章 - 128  trackbacks - 0
<2006年7月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿(5)

随笔分类(38)

随笔档案(31)

收藏夹(4)

College

High School

最新随笔

搜索

  •  

积分与排名

  • 积分 - 54314
  • 排名 - 408

最新评论

  • 1. re: [yc]详解link
  • 面试的时候面试官就问过我什么是编译和链接,我说编译就是把代码文件生成目标文件,链接就是把目标文件生成可执行文件,他说不对,又问我什么是动态链接,还问我预编译都做什么处理。。。都在这里找到了答案!!!!
  • --王至乾
  • 2. re: [yc]详解link
  • @刘伟
    我是说博主,不是叫你啊
  • --溪流
  • 3. re: [yc]详解link
  • 谁是石老师,我不是哈@溪流
  • --刘伟
  • 4. re: [yc]详解link
  • 石老师?我是溪流~
  • --溪流
  • 5. re: [yc]详解link
  • 期待楼主下文啊,多谢楼主了
  • --刘伟

阅读排行榜

评论排行榜

发信人: shifan (家没有豚豚 T.T), 板面: C++
标  题: 如何实现Lambda[第二部分]
发信站: 飘渺水云间 (Thu Jun  8 23:30:20 2006), 转信


章节:
八:第一部分的小结
九:简化,如何减少Lambda代码的冗余和依赖性
十:bind的实现
十一:实现phoenix


八.    中期总结
目前的结果是这样的,为了支持一个操作符,我们需要作如下几件事:
    1。 实现一个functor,该functor的operator()要能执行该操作符的语义
    2。 在该functor中实现result_1至result_n,其中n是支持参数的最大值。
    3。 在picker中实现一个操作符重载,返回该functor

 


九. 简化
很明显,要支持一个操作符所要做的工作太多了,而且在每个functor中申明result_1至
result_n,可见如果n发生变化,维护的开销极大。
我们现在需要找到一个自动生成这种functor的方法。
首先,我们注意到result_x的形式很统一。对于各种操作符,其返回值无非下列几种:
    1.  返回值。如果本身为引用,就去掉引用。
            +-*/&|^等
    2.  返回引用。
            =,各种复合赋值等
    3.  返回固定类型。
            各种逻辑/比较操作符(返回bool)
    4.  原样返回。
            operator,
    5.  返回解引用的类型。
            operator*(单目)
    6.  返回地址。
            operator&(单目)
    7.  下表访问返回类型。
            operator[]
    8.  如果左操作数是一个stream,返回引用,否则返回值
            operator<<和operator>>

OK,这样我们将返回值类型总结为以上8种,就可以将各种result_x从functor中剥离出来了

例如针对第一条,我们实现一个policy类:
    

template < typename Left >
    
struct  value_return
    
{
        template
< typename T >
        
struct  result_1
        
{
            typedef typename const_value
< typename Left::template
result_1
< T > ::result_type > ::value_type result_type;
        }
;

        template
< typename T1, typename T2 >
        
struct  result_2
        
{
            typedef typename const_value
< typename Left::template result_2 < T1,
T2
> ::result_type > ::value_type result_type;
        }
;
    }
;

其中const_value是一个将一个类型转为其非引用形式的trait

下面我们来剥离functor中的operator()
首先operator里面的代码全是下面的形式:
   

   return l(t) op r(t)
    
return l(t1, t2) op r(t1, t2)
    
return op l(t)
    
return op l(t1, t2)
    
return l(t) op
    
return l(t1, t2) op
    
return l(t)[r(t)]
    
return l(t1, t2)[r(t1, t2)]


很自然的,我们会想到用函数替代这种操作符行为以获得更加一致的形式:
    单目:  return f(l(t), r(t));
        return f(l(t1, t2), r(t1, t2));
    双目:  return f(l(t));
        return f(l(t1, t2));
下面就是f的实现,以operator/为例

     struct  meta_divide
    
{
        template 
< typename T1, typename T2 >
        
static  ret execute( const  T1 &  t1,  const  T2 &  t2)
        
{
            
return  t1  /  t2;
        }

    }
;


这个工作可以让宏来做:

     #define  DECLARE_META_BIN_FUNC(op, desc, ret) struct meta_##desc{\
        template 
< typename T1, typename T2 >  \
        
static  ret execute( const  T1 &  t1,  const  T2 &  t2)  { return  ((T1 & )t1) op
((T2
& )t2);}
 };


以后可以直接用
    DECLARE_META_BIN_FUNC(/, divide, T1)
来申明meta_divide。同样还可以申明宏DECLARE_META_UNY_PRE_FUNC和
DECLARE_META_UNY_POST_FUNC来产生单目前缀和后缀操作符的函数
(ps.我本坚持该lambda实现不使用宏的,但是在这种小剂量的又很一致的代码面前,使用
宏实在是很诱人。。。)


下面就是要把operator()和result_x拼凑起来,形成一个我们要的functor,下面是一个单目
的functor的实现体

    template < typename Left, typename Right, typename Rettype, typename FuncType >
    
class  unary_op :  public  Rettype
    
{
            Left l;
    
public :
            unary_op(
const  Left &  l) : l(l) {}

        template
< typename T >
            typename Rettype::template result_1
< T > ::result_type  operator ()( const
T
&  t)  const
            
{
                
return  FuncType::execute(l(t));
            }


            template
< typename T1, typename T2 >
            typename Rettype::template result_2
< T1, T2 > ::result_type
operator ()( const  T1 &  t1,  const  T2 &  t2)  const
            
{
                
return  FuncType::execute(l(t1, t2));
            }

    }
;

同样还可以申明一个binary_op

    template < typename Left, typename Right, typename Rettype, typename FuncType >
    
class  binary_op :  public  Rettype
    
{
            Left l;
        Right r;
    
public :
            binary_op(
const  Left &  l, const  Right &  r) : l(l), r(r) {}

        template
< typename T >
            typename Rettype::template result_1
< T > ::result_type  operator ()( const
T
&  t)  const
            
{
                
return  FuncType::execute(l(t), r(t));
            }


            template
< typename T1, typename T2 >
            typename Rettype::template result_2
< T1, T2 > ::result_type
operator ()( const  T1 &  t1,  const  T2 &  t2)  const
            
{
                
return  FuncType::execute(l(t1, t2), r(t1, t2));
            }

    }
;

很完美不是么,unary_op/binary_op继承了Rettype, 也就拥有了该类所定一个全部
result_x, 同时使用FuncType来执行运算符操作,很漂亮
比如要支持操作符operator+,则需要写一行
    DECLARE_META_BIN_FUNC(+, add, T1)
那么binary_op<Left, Right, value_return, meta_add>就自然是operator+(双目)的
functor,不需要自己手动实现。
停!不要陶醉在这美妙的幻觉中!
如果把这段代码拿到VC7或VC8下编译,你会得到很有趣的结果。。。
好了,这不是我们的错,但是确实我们应该解决它。
这实际上是vc的bug,解决方法是不要去使用typename Rettype::template result_2<T1,
T2>::result_type这样的形式。(感谢vbvan)
下面是修改过的unary_op

    template < typename Left, typename OpClass, typename RetType >
    
class  unary_op
    
{
        Left l;

    
public :

        unary_op(
const  Left &  l) : l(l) {}

        template
< typename T >
        
struct  result_1
        
{
            typedef typename RetType::template result_1
< T > ::result_type
result_type;
        }
;

        template
< typename T1, typename T2 >
        
struct  result_2
        
{
            typedef typename RetType::template result_2
< T1, T2 > ::result_type
result_type;
        }
;

        template
< typename T1, typename T2 >
        typename result_2
< T1, T2 > ::result_type  operator ()( const  T1 &  t1,  const
T2
&  t2)  const
        
{
            
return  OpClass::execute(lt(t1, t2));
        }


        template
< typename T >
        typename result_1
< T > ::result_type  operator ()( const  T &  t)  const
        
{
            
return  OpClass::execute(lt(t));
        }


    }
;

该方法避免直接使用RetType的result_x,而自己申明一个对应的result_x做一次中转,虽
然其实毫无意义,却恰好避开了vc的bug
好啦,现在才真正完美了。
现在在picker里面就可以这么添加了:

    template < typename Right >
    picker
< binary_op < Action, typename picker_maker < Right > ::result_type,
ref_return
< Action > , meta_add_assign >   >   operator += ( const  Right &  rt)  const
    
{
        
return  binary_op < Action, typename picker_maker < Right > ::result_type,
ref_return
< Action > , meta_add_assign > ( * this , rt);
    }

有点长不是么?不过实际代码量减少了很多,而且此后如果支持的参数上限发生变化,我们
就只需要修改binary_op和unary_op就行了。

 


十.    bind
既然都做到这份上了,我们顺便把bind也做了吧,其实事情已经变得很简单了。
先来分析一下一段例子

int  foo( int  x,  int  y) { return  x  -  y;}
bind(foo, _1, constant(
2 )( 1 )   // return -1
bind(foo, _2, _1)( 3 6 )   // return foo(6, 3) == 3

 

可见bind是一系列重载函数,返回某种functor,该functor的执行就是执行传进bind的函数
指针并正确的确定参数。
我们来写个简单的。
首先要知道一个函数的返回类型,我们使用一个trait来实现:
对于函数对象类的版本:

    template < typename Func >
    
struct  functor_trait
    
{
        typedef typename Func::result_type result_type;
    }
;


对于无参数函数的版本:

    template < typename Ret >
    
struct  functor_trait < Ret ( * )() >
    
{
        typedef Ret result_type;
    }
;


对于单参数函数的版本:

    template < typename Ret, typename V1 >
    
struct  functor_trait < Ret ( * )(V1) >
    
{
        typedef Ret result_type;
    }
;


对于双参数函数的版本:

    template < typename Ret, typename V1, typename V2 >
    
struct  functor_trait < Ret ( * )(V1, V2) >
    
{
        typedef Ret result_type;
    }
;


等等。。。
然后我们就可以仿照value_return写一个policy

    template < typename Func >
    
struct  func_return
    
{
        template
< typename T >
        
struct  result_1
        
{
            typedef typename functor_trait
< Func > ::result_type result_type;
        }
;

        template
< typename T1, typename T2 >
        
struct  result_2
        
{
            typedef typename functor_trait
< Func > ::result_type result_type;
        }
;
    }
;

最后一个单参数binder就很容易写出来了

    template < typename Func, typename aPicker >
    
class  binder_1
    
{
        Func fn;
        aPicker pk;
    
public :

        template
< typename T >
        
struct  result_1
        
{
            typedef typename func_return
< Func > ::template
result_1
< T > ::result_type result_type;
        }
;

        template
< typename T1, typename T2 >
        
struct  result_2
        
{
            typedef typename func_return
< Func > ::template result_2 < T1,
T2
> ::result_type result_type;
        }
;

        binder_1(Func fn, 
const  aPicker &  pk) : fn(fn), pk(pk) {}

        template
< typename T >
        typename result_1
< T > ::result_type  operator ()( const  T &  t)  const
        
{
            
return  fn(pk(t));
        }

        template
< typename T1, typename T2 >
        typename result_2
< T1, T2 > ::result_type  operator ()( const  T1 &  t1,  const
T2
&  t2)  const
        
{
            
return  fn(pk(t1, t2));
        }

    }
;

一目了然不是么?
最后实现bind

    template < typename Func, typename aPicker >
    picker
< binder_1 < Func, aPicker >   >  bind( const  Func fn,  const  aPicker &  pk)
    
{
        
return  binder_1 < Func, aPicker > (fn, pk);
    }


2个以上参数的bind可以同理实现。
另外还可以照样实现一系列binder来绑定类成员函数/变量,手法雷同,就不详细介绍了。

 

 

 


十一.    phoenix
Boost.phoenix可能知道的人不多,让我们来看一段代码吧:

    for_each(v.begin(), v.end(),
    (
        do_
        [
            cout 
<<  _1  <<   " "
        ]
        .while_(
-- _1),
        cout 
<<  var( " \n " )
        )
    );


是不是华丽的让人撞墙?其实这个比想象的好实现的多。还是照惯例分析一下吧:
首先do_很明显是个对象,该对象重载了operator[],接受一个functor作为参数,并返回另
一个对象,该对象有一个成员函数while_,同样接受一个functor作为参数,并返回一个

functor, 最后2个functor用operator, 生成一个新的functor
operator,的实现这里略过了,请参照前面的描述。
那么我们就照着这个思路来实现吧:

    template < typename Cond, typename Actor >
    
class  do_while
    
{
        Cond cd;
        Actor act;
    
public :
        template
< typename T >
        
struct  result_1
        
{
            typedef 
int  result_type;
        }
;

        do_while(
const  Cond &  cd,  const  Actor &  act) : cd(cd), act(act) {}

        template
< typename T >
        typename result_1
< T > ::result_type  operator ()( const  T &  t)  const
        
{
            
do
            
{
                act(t);
            }

            
while  (cd(t));
            
return   0 ;
        }

    }
;

这就是最终的functor,我略去了result_2和2个参数的operator().
代码很清晰,但是还是让我来解释一下为什么要用int作为返回类型。
其实对于do-while语义,返回类型是无意义的,然而将其定义为void会影响在某些情况下
return的简洁性,因为return一个void是不合法的。
因此我们将其定为int,并返回0,这样减少了其它地方编码的复杂度。
下面就是产生这个functor的类:

    template < typename Actor >
    
class  do_while_actor
    
{
        Actor act;
    
public :
        do_while_actor(
const  Actor &  act) : act(act) {}

        template
< typename Cond >
        picker
< do_while < Cond, Actor >   >  while_( const  Cond &  cd)  const ;
    }
;

简单吧,注意到这个while_函数,它自动的生成了一个do_while对象。
最后,是那个do_

     class  do_while_invoker
    
{
    
public :
        template
< typename Actor >
        do_while_actor
< Actor >   operator [](Actor act)  const
        
{
            
return  do_while_actor < Actor > (act);
        }

    }
do_;


好啦,现在明白do_[xxx].while_(xxx)是怎么工作的吧?
同样的,我们还可以做if_, while_, for_, switch_等。
最后来说说怎么处理break和continue
显然break的语义超出了我们的能力范围,然而却是有一个东西很适合模拟其行为,那就是
异常。
具体实现手法这里就不罗嗦了。
--
You well 撒法!You well all 撒法!

※ 来源:·飘渺水云间 freecity.cn·[FROM: shifan]                                                                       

posted on 2006-07-15 15:32 shifan3 阅读(934) 评论(0)  编辑 收藏 引用 所属分类: templateBoostC++

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理