分享
 
 
 

boost.bind 如何 bind continue.1

王朝other·作者佚名  2006-05-27
窄屏简体版  字體: |||超大  

我们先定义目标:

1. simple_bind 提供与 bind 类似的界面,可以只考虑通过对象引用(或者值)调用成员函数的情况,而不考虑 free function 或者通过指针调用等等。具体地说,就是允许

person.SetName("Ralph") --> simple_bind(&Person::SetName, person, _1)(string("Ralph"))

simple_bind(&Person::SetName, _1, string(“Ralph"))(person)

simple_bind(&Person::SetName, _1, _2)(person, string("Ralph"))

simple_bind(&Person::SetName, _2, _1)(string("Ralph"), person)

2. 一个 simple_bind 表达式会 evaluate 成为一个 simple_binder object ,这是一个 functor ,如:

simple_bind(&Person::SetName, person, _1)(string("Ralph")) --> simple_binder_obj(string("Ralph"))

simple_bind(&Person::SetName, _1, _2)(person, string("Ralph")) --> simple_binder_obj(person, string(“Ralph”))

在这一头一尾,我们其实没多少选择余地,一方面我们需要提供的用户界面已经定了,另一方面需要提供的函数调用界面也基本上固定了,要玩的 magic 也就是发生在中间。

3. 对于 placeholder ,前面已经说过,就用简单的解决方案:

template <int I>

class placeholder{};

placeholder<1> _1;

placeholder<2> _2;

...

有了这3个出发点,现在从 simple_bind 出发,首先看看 simple_bind 应该有的形式。从前面的实验中可以看出

template <typename R, typename T, typename Arg>

simple_binder<R, T, Arg>

simple_bind( R (T::*pfn)(Arg), const Arg& arg, placeholder&)

{

return simple_binder<R, T, Arg>(pfn, arg);

}

这样的形式虽然方便,但却不具有 scalability 。因为R (T::*pfn)(Arg) 的限制太大了,首先,它只针对接受一个参数的成员函数,如果要支持多个参数,你要对每个参数数量(它有一个术语叫 arity)写一个重载,而这些不同 arity 还要跟 simple_bind 本身的 arity 做组合,结果是爆炸性的 simple_bind 数量。其次,参数 arg 和 placeholder 也太不灵活,前面已经看到,仅仅在两个参数的情况下,就有4种组合:

simple_bind(&Person::SetName, _1, _2)(person, "Joy");

simple_bind(&Person::SetName, _2, _1)("Joy", person);

simple_bind(&Person::SetName, _1, "Joy")(person);

simple_bind(&Person::SetName, person, _1)("Joy");

可想而知参数更多的时候的恐怖情景。于是我们得出结论:

由于前端的调用组合太复杂,simple_bind 的界面必须以最 generic 的方式给出,由此带来的复杂性应该留待 simple_bind 内部处理。

这是经过了实验的教训得出的结论。

何谓“最 generic”?没有比这更加 generic 的了:

template <typename F, typename A1>

simple_binder<unspecified>

simple_bind(F f, A1 a1)

{

//...

}

即便如此,由于 C++ 至今还没有可变数量的模板参数,我们还是不能回避对每一个 arity 都有所定义,但是这已经把复杂度控制在 O(arity) 了:

template <typename F, typename A1, typename A2>

simple_binder<unspecified>

simple_bind(F f, A1 a1, A2 a2)

{

//...

}

......

但是随之而来的就是问题:我现在把第一个参数(函数指针)类型笼统成了一个 F ,我如何知道它的返回类型、参数类型和对象类型?好在这个问题可解,其技法在 boost 里面还很常见,那就是 traits :

template <typename T>

struct result_of

{};

template <typename R, typename T>

struct result_of<R (T::*)()>

{

typedef R type;

};

template <typename R, typename T, typename A1>

struct result_of<R (T::*)(A1)>

{

typedef R type;

};

template <typename R, typename T, typename A1, typename A2>

struct result_of<R (T::*)(A1, A2)>

{

typedef R type;

};

......

它无非利用 C++ 模板元编程最常用的偏特化来“抽取”函数指针的类型内容,我们甚至无需自己写,boost 已经提供好了相应的工具,但是上面的这个 result_of 也还算清晰好用,在这个例子里面,我们就用它。

在这个方向上,思路已经比较开阔了,现在从另一个方向:simple_binder 入手来想一想。simple_binder 应该长成什么样子呢?由于 function_traits 可以帮我们得到函数返回类型,我们可以肯定 simple_binder 在构造的时候已经知道返回类型了,那么它应该是这样吗?

template <typename R/*返回值的类型*/, typename F, typename A1>

class simple_binder

template <typename R, typename F, typename A1, typename A2>

class simple_binder

如果你的想法是这样,恭喜你,一出门你就撞了南墙:class 不是 function ,除非你偏特化,它是不允许有多个模板参数“重载”的!但是很显然,这些 simple_binder 之间,谁也不是谁的偏特化。那么是不是可以利用类似 TypeList 的技术,用默认模板参数提供变相的可变参数数量模板:

template <typename R, typename F, typename A1, typename A2 = placeholder<0>, typename A3 = placeholder<0> /*...*/ >

class simple_binder

......

好,这下子 simple_bind 的定义算是完整了:

template <typename F, typename A1>

simple_binder<typename result_of<F>::type, F, A1>

simple_bind(F f, const A1& arg1)

{

//...

}

template <typename F, typename A1, typename A2>

simple_binder<typename result_of<F>::type, F, A1, A2>

simple_bind(F f, const A1& arg1, const A2& arg2)

{

//...

}

在这个基础上,提供 simple_binder 的构造函数也是举手之劳,

explicit simple_binder(F f, const A1& a1)

: f_(f)

, a1_(a1)

{}

explicit simple_binder(F f, const A1& a1, const A2& a2)

: f_(f)

, a1_(a1)

, a2_(a2)

{}

其中的 f_, a1_, a2_ 都是 simple_binder 的成员。simple_bind 仍然只是简单的构造一个 simple_binder :

template <typename F, typename A1>

simple_binder<typename result_of<F>::type, F, A1>

simple_bind(F f, const A1& arg1)

{

return simple_binder

<

typename result_of<F>::type,

F,

A1

>

(f, arg1);

}

template <typename F, typename A1, typename A2>

simple_binder<typename result_of<F>::type, F, A1, A2>

simple_bind(F f, const A1& arg1, const A2& arg2)

{

return simple_binder

<

typename result_of<F>::type,

F,

A1,

A2

>

(f, arg1, arg2);

}

至于真正的运算,就留给 simple_binder 和它的助手们去做。我们仍然从最简单的情况开始,研究一下调用时没有参数是什么样,例如这样的调用:

simple_bind(&Person::Name, person)()

这个调用的参数在 simple_bind 的时候已经确定,而在 simple_binder 那里只是简单调用一个0参数的 operator(),也就是:

R operator()()

{

//...

}

即便是这个东西,它代表的意义也不单纯,实际上,在有两个 placeholder 的场合,它代表了两种情况:

1. 类似 simple_bind(&Person::Name, person)(),函数实际上没有参数

2. 类似 simple_bind(&Person::SetName, person, “Joy”)(), 函数其实有一个参数

我们有没有足够的信息来确定是那种情况呢?有,答案就在 simple_binder 的模板参数 A1, A2 中。如果 A2 是 placeholder<0> ,那么就是第一种情况,否则就是第二种情况。为了能在这两种情况之间做出决策,必须加上一个助手,我们称之为 eval :

template <typename R, typename F, typename A1, typename A2>

R eval(F f, A1 a1, A2 a2)

{

return (a1.*f)(a2);

}

template <typename R, typename F, typename A1>

R eval(F f, A1 a1, placeholder<0>)

{

return (a1.*f)();

}

于是乎0参数的 operator() 就成了

R operator()()

{

return eval<R>(f_, a1_, a2_);

}

参数 a2_ 会帮助我们把调用分派到两个 eval 中的一个,从而完成调用决策。

现在来考虑 1 个参数的 operator() ,它就复杂一些了:

1. 类似 bind(&Person::Name, _1)(person) ,函数其实没有参数

2. 类似 bind(&Person::SetName, _1, "Joy")(person),函数其实有1个参数,_1 代表第一个

3. 类似 bind(&Person::SetName, person, _1)("Joy"),函数其实有1个参数,_1 代表第二个

停!!!看来在这个方向上鸣金收兵的时候到了。回顾一下这个设计:

通过提供 generic 的 simple_bind 界面以及 result_of ,我们简化了 simple_bind ;又通过 eval ,我们让 simple_binder 变得单纯起来,然而现在 eval 的复杂性开始膨胀,大有脱离我们控制的趋势。原因在哪里?

参数的复杂性。

作为一个系统,我们的这个东西其实接受两组参数,一组是由 simple_bind 接受的,混杂了 placeholder 和实际参数;另一组是由 simple_binder 的 operator() 接受的,全部都是实际参数,但是其应用的顺序需要根据 placeholder 的类型来决定。了解了这个复杂性的根源,我们的任务再次变得清晰起来:提供一个方法,以统一的方式来处理参数,降低复杂性。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有