boost::bind/function的索引占位符的实现

boost::bind/function的索引占位符的实现

说明:如果代码中的对boost库的使用有误(如大小写不匹配,丢失连字符等),请勿奇怪。本文仅用于讲解,请把它们当成伪码。

所谓索引占位符,就是指在执行bind时,传入的参数不是具体的数值,而是一个能够起到索引作用的对象,该对象能够从传递给(bind函数返回的函数对象的)函数调用的实参中取得对应的数值,如:
int callback(int a, int b){return a + b;}
int result1 = bind(callback, 10, _1)(20);//callback(10, 20)
int result2 = bind(callback, _2, _1)(20, 10);//callback(10, 20)
上面表达式中的_1和_2就是占位符;对于int result2 = bind(callback, _2, _1)(20, 10);中的(20, 10),20在实参中的索引是1,10在实参中的索引是2,也就是说实参索引也就是实参的顺序,只不过该顺序从1开始。_1表示顺序号为1的实参,_2表示顺序号为2的实参。因此,在int result2 = bind(callback, _2, _1)(20, 10);中,_1对应于(20, 10)中的20,_2对应于(20, 10)中的10。
那么,_1、_2具体是如何进行索引的呢?
先看看_1、_2的定义(这些定义乃个人定义,并非来自boost,不过和boost类似):
template<int INDEX> struct TIndex{};
typedef TIndex<1> _1;
typedef TIndex<2> _2;
可以看到,_1、_2是类型;不过要注意,bind(callback, _2, _1)中的bind接受的是_1、_2对象,而不是_1、_2类型,它相当于:
_2 index2;
_1 index1;
bind(index2, index1);
在上一篇中,已经讲过,bind的返回值是一个函数对象。对于int result2 = bind(callback, _2, _1)(20, 10);这一表达式中的bind,其返回值类型类似于:
class TBoundFunctionObject
{
public:
 int operator()(int a, int b)
 {
  int parameter1 = ValueFromIndex(m_index1InParameters, a, b);
  int parameter2 = ValueFromIndex(m_index2InParameters, a, b);
  return m_callback(parameter1, parameter2);
 }
private:
 int (*m_callback)(int a, int b);
 TIndex<2> m_index1InParameters;
 TIndex<1> m_index2InParameters;
};
参考上一篇,将之改为模板:
template<typename TReturn, typename TArg1, typename TArg2, typename TIndex1, typename TIndex2> class TBoundFunctionObject
{
public:
 TReturn operator()(TArg1 a, TArg2 b)
 {
  TArg1 parameter1 = ValueFromIndex(m_index1InParameters, a, b);
  TArg2 parameter2 = ValueFromIndex(m_index2InParameters, a, b);
  return m_callback(parameter1, parameter2);
 }
private:
 TReturn (*m_callback)(TArg1 a, TArg2 b);
 TIndex1 m_index1InParameters;
 TIndex2 m_index2InParameters;
};
不同之处在于,该模板增加了表示索引占位符类型的模板参数TIndex1和TIndex2。其实这么说是不完整的,因为传递给bind的即可以是索引占位符,也可以是实际数值。为此,将上面的模板稍作更改:
template<typename TReturn, typename TArg1, typename TArg2, typename TBind1, typename TBind2> class TBoundFunctionObject
{
public:
 TReturn operator()(TArg1 a, TArg2 b)
 {
  TArg1 parameter1 = ValueFromIndex(m_bind1, a, b);
  TArg2 parameter2 = ValueFromIndex(m_bind2, a, b);
  return m_callback(parameter1, parameter2);
 }
private:
 TReturn (*m_callback)(TArg1 a, TArg2 b);
 TBind1 m_bind1;
 TBind2 m_bind2;
};
其中TBind1既可以是实际数值类型,也可以是_1、_2等。
现在,关键的地方来了,ValueFromIndex如何实现?
可以肯定的是,ValueFromIndex是一个函数模板,其返回值由其第一个参数决定:
 如果是实际数值,则返回实际数值;
 如果是_1,则返回第二个参数a;
 如果是_2,则返回第三个参数b。
其实现应当类似于:
template<typename TArg1, typename TArg2, typename TArg3> TReturn ValueFromIndex(TArg1 a1, TArg2 a2, TArg3 a3)
{
 if(IsParameter(a1)) return a1;
 if(Is_1(a1)) return a2;
 if(Is_2(a1)) return a3;
 throw exception("invalid a1.");
}
问题来了:
1. TReturn该如何定义?
2. 如果TArg2、TArg3和TArg1(在表示实际数值时)不同,上面的实现显然非法,因为一个函数不可能具有多个返回值类型。那么,该如何解决?
两个问题的解决办法当然是有,需要利用模板元编程。
我们知道三目运算符?:,但它无法应用于类型,boost::mpl中有一个if实现了该功能:
boost::mpl::if<bool, Type1, Type2>::type:如果第一个参数为true,则type表示Type1,否则表示Type2
实现原理(利用偏特化):
template<bool T1, typename Type1, typename Type2> struct if
{
 typedef Type1 type;
};
template<typename Type1, typename Type2> struct if<false>
{
 typedef Type2 type;
};
还可以利用偏特化实现switch类型的功能:
switch<int i, char, short, int>::type:如果i为1,则type表示short;如果为2,则type表示int;否则(default),type表示char。
template<int i, typename Type1, typename Type2, typename Type3> struct switch
{
 typedef Type1 type;
};
template<typename Type1, typename Type2, typename Type3> struct switch<1>
{
 typedef Type2 type;
};
template<typename Type1, typename Type2, typename Type3> struct switch<2>
{
 typedef Type3 type;
};
仿照该switch,第一个问题解决了,TReturn应该如下定义:
TSwitch<TBind, TArg1, TArg2>::type
template<typename TBind, typename TArg1, typename TArg2> struct TSwitch
{
 typedef TBind type;
};
template<typename TArg1, typename TArg2> struct TSwitch<_1>
{
 typedef TArg1 type;
};
template<typename TArg1, typename TArg2> struct TSwitch<_2>
{
 typedef TArg2 type;
};
对于第二个问题,方法类似:
template<typename TArg1, typename TArg2, typename TArg3> TSwitch<TArg1, TArg2, TArg3>::type ValueFromIndex(TArg1 a1, TArg2 a2, TArg3 a3)
{
 return a1;
}
template<typename TArg2, typename TArg3> TSwitch<_1, TArg2, TArg3>::type ValueFromIndex(_1 a1, TArg2 a2, TArg3 a3)
{
 return a2;
}
template<typename TArg2, typename TArg3> TSwitch<_2, TArg2, TArg3>::type ValueFromIndex(_2 a1, TArg2 a2, TArg3 a3)
{
 return a3;
}
但是很可惜,模板函数不支持偏特化,所以需要包装一下:
template<typename TArg1, typename TArg2, typename TArg3> struct ValueFromIndex
{
 static TSwitch<TArg1, TArg2, TArg3>::type Value(TArg1 a1, TArg2 a2, TArg3 a3)
 {
  return a1;
 }
};
template<typename TArg2, typename TArg3> struct ValueFromIndex<_1>
{
 static TSwitch<_1, TArg2, TArg3>::type Value(_1 a1, TArg2 a2, TArg3 a3)
 {
  return a2;
 }
};
template<typename TArg2, typename TArg3> struct ValueFromIndex<_2>
{
 static TSwitch<_2, TArg2, TArg3>::type Value(_2 a1, TArg2 a2, TArg3 a3)
 {
  return a3;
 }
};
相应修改TBoundFunctionObject:
template<typename TReturn, typename TArg1, typename TArg2, typename TBind1, typename TBind2> class TBoundFunctionObject
{
public:
 TReturn operator()(TArg1 a, TArg2 b)
 {
  TArg1 parameter1 = ValueFromIndex<TBind1, TArg1, TArg2>::Value(m_bind1, a, b);
  TArg2 parameter2 = ValueFromIndex<TBind2, TArg1, TArg2>::Value(m_bind2, a, b);
  return m_callback(parameter1, parameter2);
 }
private:
 TReturn (*m_callback)(TArg1 a, TArg2 b);
 TBind1 m_bind1;
 TBind2 m_bind2;
};
最后,还有一个问题需要注意,就是TBoundFunctionObject::operator()能够接受的参数数目需要随着传递给bind的实际数值的个数作出变动,如:
int result1 = bind(callback, 10, _1)(20);//TBoundFunctionObject::operator()必须接受1个参数
int result2 = bind(callback, _2, _1)(20, 10);//BoundFunctionObject::operator()必须接受2个参数
int result2 = bind(callback, _2, _2)(20, 10);//BoundFunctionObject::operator()必须接受2个参数(此处不符合boost中的规定,跳过_1直接使用了_2)
一个简单的解决办法是重载operator(),列出所有可能的情况,然后在各种情况中进行相应的判断
template<typename TReturn, typename TArg1, typename TArg2, typename TBind1, typename TBind2> class TBoundFunctionObject
{
public:
 TReturn operator()()//bind(callback, 10, 20)();
 {
  assert(TMaxIndex<TBind1, TBind2>::Value == 0);
  return m_callback(m_bind1, m_bind2);
 }
 template<tpyename TArg> TReturn operator()(TArg a)//bind(callback, 10, _1)(20);bind(callback, _1, 20)(10);bind(callback, _1, _1)(20);
 {
  assert(TMaxIndex<TBind1, TBind2>::Value == 1);
  TArg1 parameter1 = ValueFromIndex2<TBind1, TArg>::Value(m_bind1, a);
  TArg2 parameter2 = ValueFromIndex2<TBind2, TArg>::Value(m_bind2, a);
  return m_callback(parameter1, parameter2);
 }
 TReturn operator()(TArg1 a, TArg2 b)//bind(callback, _2, _1)(20, 10);bind(callback, _2, _2)(20, 10);
 {
  //如果要禁止bind(callback, _2, _2)(20, 10);,可以增加assert;不过该写法太繁琐,较好的方法参考下一篇
  //assert((TMaxIndex<TBind1>::Value == 1 && TMaxIndex<TBind1>::Value == 2) || (TMaxIndex<TBind1>::Value == 2 && TMaxIndex<TBind1>::Value == 1));
  assert(TMaxIndex<TBind1, TBind2>::Value == 2);
  TArg1 parameter1 = ValueFromIndex<TBind1, TArg1, TArg2>::Value(m_bind1, a, b);
  TArg2 parameter2 = ValueFromIndex<TBind2, TArg1, TArg2>::Value(m_bind2, a, b);
  return m_callback(parameter1, parameter2);
 }
};
ValueFromIndex2类似ValueFromIndex,但只接受两个参数:如果第一个参数是_1,返回第二参数;否则返回第一个参数
TMaxIndex<>:用来取得最大的占位符的最大值;如TMaxIndex<_1,_3,_2>::Value为3;TMaxIndex<SomeType>::Value为0
TMaxIndex的实现:
template<bool b, int i1, int i2> struct IfValue
{
 enum enumValue{Value = i1};
};
template<int i1, int i2> struct IfValue<false>
{
 enum enumValue{Value = i2};
};
template<int i1, int i2> struct TMax : public IfValue<(i1 > i2), i1, i2>
{
};

//一个参数版本的TMaxIndex
template<typename TType> struct TMaxIndex1
{
 enum enumValue{Value = 0};
};
template<> struct TMaxIndex1<_1>
{
 enum enumValue{Value = 1};
};
template<> struct TMaxIndex1<_2>
{
 enum enumValue{Value = 2};
};
//两个参数版本的TMaxIndex
template<typename TType1, typename TType2> struct TMaxIndex2
{
 enum enumValue{Value = TMax< TMaxIndex1<TType1>::Value, TMaxIndex1<TType2>::Value >::Value;
};
//三个参数版本的TMaxIndex
template<typename TType1, typename TType2, typename TType3> struct TMaxIndex3
{
 enum enumValue{Value = TMax< TMaxIndex2<TType1, TType2>::Value, TMaxIndex1<TType3>::Value >::Value;
};
//四个参数版本的TMaxIndex
template<typename TType1, typename TType2, typename TType3, typename TType4> struct TMaxIndex4
{
 enum enumValue{Value = TMax< TMaxIndex3<TType1, TType2, TType3>::Value, TMaxIndex1<TType4>::Value >::Value;
};
上面的TBoundFunctionObject还不够好:无法在编译期检测出调用参数数目错误的operator();对此,可以将assert(TMaxIndex<TBind1, TBind2>::Value == 0);更改为BOOST_STATIC_ASSERT(TMaxIndex<TBind1, TBind2>::Value == 0)等。
如果不允许重载operator(),该如何解决?留给大家思考吧。如果没有什么头绪,可以参考下几篇文章。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值