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

2024-04-08 10:08

本文主要是介绍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(),该如何解决?留给大家思考吧。如果没有什么头绪,可以参考下几篇文章。

 

这篇关于boost::bind/function的索引占位符的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/885281

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

mysql索引四(组合索引)

单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引;组合索引,即一个索引包含多个列。 因为有事,下面内容全部转自:https://www.cnblogs.com/farmer-cabbage/p/5793589.html 为了形象地对比单列索引和组合索引,为表添加多个字段:    CREATE TABLE mytable( ID INT NOT NULL, use

mysql索引三(全文索引)

前面分别介绍了mysql索引一(普通索引)、mysql索引二(唯一索引)。 本文学习mysql全文索引。 全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它能够利用【分词技术】等多种算法智能分析出文本文字中关键词的频率和重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果。 在MySql中,创建全文索引相对比较简单。例如:我们有一个文章表(article),其中有主键ID(

mysql索引二(唯一索引)

前文中介绍了MySQL中普通索引用法,和没有索引的区别。mysql索引一(普通索引) 下面学习一下唯一索引。 创建唯一索引的目的不是为了提高访问速度,而只是为了避免数据出现重复。唯一索引可以有多个但索引列的值必须唯一,索引列的值允许有空值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该使用关键字UNIQUE,把它定义为一个唯一索引。 添加数据库唯一索引的几种

mysql索引一(普通索引)

mysql的索引分为两大类,聚簇索引、非聚簇索引。聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引则不同。聚簇索引能够提高多行检索的速度、非聚簇索引则对单行检索的速度很快。         在这两大类的索引类型下,还可以降索引分为4个小类型:         1,普通索引:最基本的索引,没有任何限制,是我们经常使用到的索引。         2,唯一索引:与普通索引

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

Python 字符串占位

在Python中,可以使用字符串的格式化方法来实现字符串的占位。常见的方法有百分号操作符 % 以及 str.format() 方法 百分号操作符 % name = "张三"age = 20message = "我叫%s,今年%d岁。" % (name, age)print(message) # 我叫张三,今年20岁。 str.format() 方法 name = "张三"age

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现