一步步实现C++11中的std::function和std::bind(下)

一、本文目的

        前面两篇分别讲了如何封装自己的function和bind,保存了函数和参数包。还有最后一点与C++11提供的明显差异是没有提供部分参数的保存功能。本文将在前篇基础上介绍如何实现部分参数的保存功能。

二、tuple展开分析

        从上一篇我们知道,参数包是可以通过tuple进行保存,在回调时通过对tuple进行展开调用。std::bind可以只绑定部分参数,其他参数可以通过占位符代替,在最后回调的时候传入,替换占位符传给函数进行调用。

        显然在bind绑定时,即保存了固定参数,也保存了占位符。如:f = bind(&print,std::placeholders::_2,1,std::placeholders::_1),那么tuple中保存元素就分别为:std::placeholders::_2、1、std::placeholders::_1。在最后调用时f(2,3);那么相当于调用了print(3,1,2)。调用时先发现std::placeholders::_2是占位符,从f(Args...)参数包第二个参数中取值也就是3,即print(3,...);然后是1为固定参数,即print(3,1,..);最后是std::placeholders::_1,取参数包中第1个值,即print(3,1,2),完成调用。也就是说在展开时,需要根据tuple中元素数据类型取值,如果不是占位符类型取tuple元素值,否则根据占位符编号从f(Args...)参数包中取值。

        在这里定义模板类,模板类型参数为元素类型,包括两类:占位符类、非占位符类型。如下:

// 一般类型
template<typename placeType>
struct param_wrapper
{
	template<typename... Args>
	static auto get_value(placeType val, Args... args)
	{
		return val;   // 直接返回元素值
	}
};

// 占位符类型
#define place_param_wrapper(index)		template<>									 \
										struct param_wrapper<_placeholders_<index> &>  \
										{											 \
											template<typename... Args>               \
											static auto get_value(_placeholders_<index> val, Args... args)  \
											{									 	\
												tuple<Args...> tu(args...);			\
												return std::get<index - 1>(tu);			\
											}										\
										}; 

// 生成1~5占位符类型参数包类
place_param_wrapper(1);
place_param_wrapper(2);
place_param_wrapper(3);
place_param_wrapper(4);
place_param_wrapper(5);

        上面一个是非占位符参数包类模板,get_value返回值是直接从tuple元素中取得的值。下面是占位符参数包类模板,是上面模板类的特例化,通过宏定义定义了1~5中情况,这都是在编译阶段产生的,其get_value是根据占位符编号从参数包arg...中获取相应值。调用时可以通过param_wrapper的模板参数类型判断是哪一种模板类,然后调用其相应的get_value。使用方式如下:

template<size_t... I>
struct _index_seq_ {};

template<size_t N, size_t... I>
class _make_index_seq_ :public _make_index_seq_<N - 1, N - 1, I...> {};

template<size_t... I>
class _make_index_seq_<0, I...> :public _index_seq_<I...> {};

//
template<size_t N>
struct _placeholders_ {};
#define _placeholder_(i) _placeholders_<i>()

//
// 一般类型
template<typename placeType>
struct param_wrapper
{
	template<typename... Args>
	static auto get_value(placeType val, Args... args)
	{
		return val;   // 直接返回元素值
	}
};

// 占位符类型
#define place_param_wrapper(index)		template<>									 \
										struct param_wrapper<_placeholders_<index> &>  \
										{											 \
											template<typename... Args>               \
											static auto get_value(_placeholders_<index> val, Args... args)  \
											{									 	\
												tuple<Args...> tu(args...);			\
												return std::get<index - 1>(tu);			\
											}										\
										}; 

// 生成1~5占位符类型参数包类
place_param_wrapper(1);
place_param_wrapper(2);
place_param_wrapper(3);
place_param_wrapper(4);
place_param_wrapper(5);

template<typename... Args>
class Print
{
public:
	Print(Args... args) :m_tu(args...) {}
	template<typename... Args>
	void operator()(Args... args)
	{
		print(m_seq, args...);
	}
	template<size_t... I, typename... Args>
	void print(_index_seq_<I...>, Args... args)
	{
		int arry[] = { (cout << param_wrapper<decltype(std::get<I>(m_tu))>::get_value(std::get<I>(m_tu),args...) ,0)... };
	}

private:
	_make_index_seq_<sizeof...(Args)> m_seq;
	tuple<Args...> m_tu;
};

template<typename... Args>
Print<Args...> make_print(Args... args)
{
	return Print<Args...>(args...);
}

int main()
{
	auto _print = make_print(_placeholder_(2), "cjf", _placeholder_(1));
	_print(1, 'c');

	system("pause");
	return 0;
}

三、完善自定义binder

        根据上面的分析,我们这里可以给出自定义binder最终实现,代码如下:

template<size_t... I>
struct _index_seq_ {};

template<size_t N, size_t... I>
class _make_index_seq_: public _make_index_seq_<N-1, N-1, I...> {};

template<size_t... I>
class _make_index_seq_<0, I...> : public _index_seq_<I...> {};
//
template<size_t Index>
struct _placeholder{};
#define _placeholder_(i) _placeholder<i>()

template<typename placeType>
struct param_wrapper
{
	template<typename... Args>
	static auto get_value(placeType val, Args... args)
	{
		return val;
	}
};

#define place_param_wrapper(index)		template<> \
										struct param_wrapper<_placeholder<index> &> \
										{ \
											template<typename... Args> \
											static auto get_value(_placeholder<index> val, Args... args) \
											{ \
												tuple<Args...> tu(args...); \
												return std::get<index-1>(tu); \
											} \
										};

place_param_wrapper(1)
place_param_wrapper(2)
place_param_wrapper(3)
place_param_wrapper(4)
place_param_wrapper(5)
place_param_wrapper(6)
place_param_wrapper(7)
place_param_wrapper(8)
place_param_wrapper(9)
place_param_wrapper(10)
place_param_wrapper(11)
place_param_wrapper(12)
place_param_wrapper(13)
place_param_wrapper(14)
place_param_wrapper(15)
place_param_wrapper(16)
place_param_wrapper(17)
place_param_wrapper(18)
place_param_wrapper(19)
place_param_wrapper(20)


//
template<typename Fx, typename... Args>
class _binder_
{
public:
	_binder_(Fx f, Args... args) :m_f(f),m_tu(args...) {}
	template<typename... params>
	auto operator()(params... args)
	{
		return call(m_seq, args...);
	}
	template<size_t... I, typename... params>
	auto call(_index_seq_<I...>, params... args)
	{
		return (*m_f)(param_wrapper<decltype(std::get<I>(m_tu))>::get_value(std::get<I>(m_tu), args...)...);
	}

private:
	std::tuple<Args...> m_tu;
	_make_index_seq_<sizeof...(Args)> m_seq;
	Fx m_f;
};

template<typename Fx, typename T, typename... Args>
class _mbinder_
{
public:
	_mbinder_(Fx f, T *t, Args... args) :m_f(f),m_t(t),m_tu(args...) {}
	template<typename... params>
	auto operator()(params... args)
	{
		return call(m_seq, args...);
	}
	template<size_t... I, typename... params>
	auto call(_index_seq_<I...>, params... args)
	{
		return (m_t->*m_f)(param_wrapper<decltype(std::get<I>(m_tu))>::get_value(std::get<I>(m_tu), args...)...);
	}

private:
	std::tuple<Args...> m_tu;
	_make_index_seq_<sizeof...(Args)> m_seq;
	Fx m_f;
	T *m_t;
};

//
// 一般函数bind
template<typename Fx, typename... Args>
auto _bind(Fx f, Args... args)
{
	return _binder_<Fx, Args...>(f, args...);
}

// 成员函数bind
template<typename Fx, typename T, typename... Args>
auto _bind(Fx f, T *t, Args... args)
{
	return _mbinder_<Fx, T, Args...>(f, t, args...);
}

//
template<typename R, typename... Args>
class binder_wrapper_impl
{
public:
	virtual ~binder_wrapper_impl() {}
	virtual R call(Args... args) { return R(); }
};

template<typename _binderType, typename R, typename... Args>
class binder_wrapper:public binder_wrapper_impl<R, Args...>
{
public:
	binder_wrapper(_binderType binder) :m_binder(binder) {}
	virtual R call(Args... args)
	{
		return m_binder(args...);
	}

private:
	_binderType m_binder;
};

// 泛化版本
template<typename R, typename... Args>
class _function;

// 偏特例化
template<typename R, typename... Args>
class _function<R(Args...)>
{
public:
	template<typename _binderType>
	_function(_binderType binder)
	{
		m_binder = new binder_wrapper<_binderType, R, Args...>(binder);
	}
	virtual ~_function() {
		delete m_binder;
	}
	template<typename... params>
	auto operator()(params... args)
	{
		return m_binder->call(args...);
	}

private:
	binder_wrapper_impl<R, Args...> *m_binder = nullptr;
};

调用代码如下:

class Print
{
public:
	int draw(int a, int b, int c)
	{
		cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
		return 0;
	}
};

int print(int a, int b, int c)
{
	cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
	return 0;
}

int main()
{
	_function<int(int,int)> f = _bind(&print, _placeholder_(2), 2, _placeholder_(1));
	f(1,2);

	Print pt;
	_function<int(int, int)> f2 = _bind(&Print::draw, &pt, _placeholder_(2), 2, _placeholder_(1));
	f2(1, 2);

	system("pause");
	return 0;
}

四、总结

        这三篇文章对std::function和std::bind实现作了大致介绍,可以了解其实现原理。从实现过程中可以发现使用了大量的模板技巧,如果能掌握其中使用方式,也可以增加自身的C++技术。现对使用过的难点和技术做总结:

  1. 使用binder类保存函数和参数包;
  2. 使用function通用一般函数binder和类函数binder的形式,在function构造函数中将binder无差别保存起来;
  3. 使用类模板继承方式,在function中添加对binder调用接口类,在其实现类中保存binder,从而避免function依赖binder。在实现类模板中使用泛型binderType保存_binder和_mbinder;
  4. 使用tuple保存参数包;
  5. 拆包时使用索引序列_index_seq_进行展开;
  6. 需要掌握生成_index_seq_方式,使用递归继承方式实现;
  7. 部分参数实现使用了param_wrapper一般化和特例化将占位符元素和非占位符元素区分开来,分别实现其对应的get_value;
  8. 上面对function和bind实现不能完全代替标准std::function、std::bind,三篇文章目的是掌握其底层实现原理,了解模板使用技巧,还有很多细节并未考虑全;

参考

https://en.cppreference.com/w/cpp/language/parameter_pack

https://blog.csdn.net/pkxpp/article/details/112406177

https://en.cppreference.com/w/cpp/utility/integer_sequence

https://blog.csdn.net/dboyguan/article/details/51706357

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值