Giter Site home page Giter Site logo

wstd-bind's People

Contributors

humorly avatar

Watchers

 avatar  avatar

wstd-bind's Issues

剖析std::bind并实作一个

剖析std::bind源码,根据其中的设计**,实作相对简洁易懂的wstd::bind,源码实现如下:

一:构建binder对象
1.构建std::binder,用以存储函数指针与相关绑定参数/占位符;
2.提供call接口,用以function执行函数调用时回调该接口;
二:构建函数对象
1.构建std::function,重载调用操作符,用以实现函数调用;
2.编写std::function以binder对象的构造函数,用以在function函数调用时转入binder之中的调用过程,并将function中传入的参数一并传入其中;
三:构建bind接口
1.提供用以创建binder对象的bind接口,用以绑定成员/非成员函数;

实现过程
1.binder

	// 构建非成员函数的binder模板
	template <typename Fx, typename ... args>
	class binder
	{
	public:
        // f-> 绑定的函数指针, params...->参数列表
        // tuple_ -> 用以接收bind构造过程的参数
		binder(Fx f, args ... params) : tuple_(params ...)
		{
			function_ = f;
		}
		virtual ~binder() {}

		// 提供给function对象调用的回调
        // function调用时将传入附带参数列表
		template <typename ... args>
		auto call(args ... params)
		{
			return func(args_seq_, params ...);
		};

        // call的实际处理过程,该过程实际将原先bind绑定的参数包展开
        // 判定bind时有占位符时,将替换以function调用call时的传入参数
		template <std::size_t ... I, typename ... args>
		auto func(std::index_sequence<I ...>, args ... params)
		{
			return function_(params_wrapper< decltype(std::get<I>(tuple_)) >::template get_value(std::get<I>(tuple_), params ...) ...);
		}
	protected:
        // 用以存储函数指针
		Fx function_;
        // 获取绑定的参数列表
		std::index_sequence_for<args...> args_seq_{};
        // 存储参数列表的元组
		std::tuple<args...> tuple_;
	};

2.funciton

①.funciton
    // funciton在构建之时本需要对参数模板Fx进行拆分
    // 用以剥离返回类型ret_type与参数列表args ...
    // 此处通过继承func_impl以实现剥离参数
    // 继承于此,因此也将相关接口封装至基类中
	template <typename Fx>
	class function : public func_impl<Fx>
	{
	public:
        // 上述创建的binder对象将会被传入至此
		template<typename __binder_type>
		function(__binder_type t)
		{
            // 调用基类初始函数
            // 将binder实质传入其中
			this->reset(t);
		}
		virtual ~function() {}

        // 重载调用操作符,经过function调用过程传入的参数
        // 将会经过此处投递至binder对象的call中
		template<typename ... args>
		auto operator ()(args ... params)
		{
			this->call(params ...);
		}
	};


②.func_impl 
    // func_impl常规版本
	template <typename ret, typename ... args>
	class func_impl {};

    // 对函数类型ret (args ...)偏特化以萃取返回值ret与参数列表args ...
	template <typename ret, typename ... args>
	class func_impl<ret (args ...)>
	{
	public:
		typedef typename ret ret_type;

		func_impl() {}
		virtual ~func_impl() 
		{
			if (nullptr != impl_)
				delete impl_;
		}

	protected:
		// function调用此处时将binder对象传入
		// binder对象将会被用以构造binder_impl对象
		// 利用此方法即可在function构造中存储binder类型
		template <typename binder>
		void reset(binder call_impl)
		{
                        // 存储binder_impl类型
			impl_ = new binder_impl<binder, ret, args...>(call_impl);
		}

		// function对象调用此处以跳转到binder_impl_warpper中的virtual call
		// 即会跳转至binder_impl中的call
		ret call(args ... params)
		{
			return impl_->call(params ...);
		}

		// 存储binder_impl对象指针,其中存储了binder对象
		// 在binder对象构造之前并不知道binder
		// 会指向何种类型的函数指针
		// 因此只能在构造时确定该binder_impl指针
		binder_impl_wrapper<ret, args ...> * impl_ = nullptr;
	};


④.binder_impl_wrapper
	// 用来进行跳转的基类
	template <typename ret, typename ... args>
	class binder_impl_wrapper
	{
	public:
		binder_impl_wrapper() {}
		virtual ~binder_impl_wrapper() {}

		// 用以跳转的接口
		virtual ret call(args ... parmas) { return ret(); };
	};


⑤.binder_impl
	// call_type即为binder类型,ret -> 返回值类型
	// args ... 参数包类型
	template <typename call_type, typename ret, typename ... args>
	class binder_impl : public binder_impl_wrapper<ret, args ...>
	{
	public:
		// 构造过程存储实际的binder类型
		binder_impl(call_type cal) : caller_(cal)
		{
		}
		virtual ~binder_impl() {}

		// 经过func_impl调用而跳转至此
		virtual ret call(args ... parmas) 
		{
			// 转到真实的binder中的call接口
			// 至此从function函数调用跳转到binder中的call
			return caller_.call(parmas ...);
		};

	protected:
		// 实际的binder对象存储
		call_type caller_;
	};

⑥.核心调用过程 -> binder.call()
		template <typename ... args>
		auto call(args ... params)
		{
			// args_seq_ -> 传递bind参数序列,用以展开binder的参数
			// params ... -> function传递的参数,用以替换binder对应占位符参数
			return func(args_seq_, params ...);
		};

		template <std::size_t ... I, typename ... args>
		auto func(std::index_sequence<I ...>, args ... params)
		{
			// tuple_中存储的是bind时的实际参数内容
			// 需要一个模板对参数(std::get<I>(tuple_)参数类型进行特化,
			// #1.如果是占位符则获取占位符标记N ,比如std::placeholder::_1则捕获1,
			// 而后此处用args ...中的第1个参数替换
	
			// #2.如果非占位符则直接获取(std::get<I>(tuple_)的值以传入函数
			// 指针function_中作为参数
	
			// 这里使用模板params_wrapper实现
			return function_(params_wrapper< decltype(std::get<I>(tuple_)) >::template get_value(std::get<I>(tuple_), params ...) ...);
		}



⑦.params_wrapper
        // 常规版本 -> 非占位符
	template <typename __place_type>
	struct params_wrapper
	{
		// 如果调用到此处直接返回参数val,即上述中的std::get<I>(tuple_)
		// 否则调用占位符特化版本中的参数包中的对应参数
		template <typename ... args>
		static auto get_value(__place_type val, args ... params)
		{
			return val;
		}
	};

        // 占位符类型的偏特化
	// placeholders special
#define place_holder_wrapper(__index) 		template <>																	\
											struct params_wrapper<std::_Ph<__index> &>										\
											{																			\
												template <typename ... args>											\
												static auto get_value(std::_Ph<__index> &, args ... params)				\
												{																		\
													// 构造参数列表
													static std::tuple<args...> tu_(params ...);							\
													// 返回占位符std::_Ph<__index>对应的function参数包中的参数
													return std::get<__index - 1>(tu_);									\
												}																		\
											};

	// 使用上述定义偏特化宏偏特化占位符 std::_Ph<1> ~ std::_Ph<20>
	// placeholder 1 ~ 20
	place_holder_wrapper(1);
	place_holder_wrapper(2);
	place_holder_wrapper(3);
	place_holder_wrapper(4);
	place_holder_wrapper(5);
	place_holder_wrapper(6);
	place_holder_wrapper(7);
	place_holder_wrapper(8);
	place_holder_wrapper(9);
	place_holder_wrapper(10);
	place_holder_wrapper(11);
	place_holder_wrapper(12);
	place_holder_wrapper(13);
	place_holder_wrapper(14);
	place_holder_wrapper(15);
	place_holder_wrapper(16);
	place_holder_wrapper(17);
	place_holder_wrapper(18);
	place_holder_wrapper(19);
	place_holder_wrapper(20);

3.wstd::bind接口提供

问题:wstd::bind需要解决一个问题,区分成员函数/非成员函数,对于非成员函数提供的binder模板是binder,对于成员提供一个新模板binder_t,实现如下:
①.binder_t
	// function wrapper with member funciton
	template <typename Fx, typename T, typename ... args>
	class binder_t
	{
	public:
		binder_t(Fx f, T ob, args ... params) : tuple_(params ...)
		{
			function_ = f;
			ob_ = ob;
		}
		virtual ~binder_t() {}

		// call
		template <typename ... args>
		auto call(args ... params)
		{
			return func(args_seq_, params ...);
		};

		template <std::size_t... I, typename ... args>
		auto func(std::index_sequence<I...>, args ... params)
		{
			// 调用时以成员指针形式调用
			return (ob_->*function_)(params_wrapper< decltype(std::get<I>(tuple_)) >::template get_value(std::get<I>(tuple_), params ...)...);
		}
	protected:
		// 存储成员函指针
		Fx function_;
		// 存储class类型
		T ob_;
		std::index_sequence_for<args ...> args_seq_{};
		std::tuple<args ...> tuple_;
	};

基于上述,wstd::bind接口需要能判定是否成员/非成员函数,以返回不同的类型,因此需要在构建一个区分模板make_binder
②.make_binder
        // 常规版本
	// binder selector
	template <bool __type>
	struct make_binder
	{
		// 非成员函数返回binder类型
		template<typename Fx, typename ... args>
		static binder<Fx, args...> bind(Fx f, args... a)
		{
			binder<Fx, args...> binder_(f, a...);
			return binder_;
		}
	};

	// 偏特化成员函数
	template <>
	struct make_binder<true>
	{
		// 成员函数返回binder_t
		template<typename Fx, typename T, typename ... args>
		static binder_t<Fx, T, args...> bind(Fx f, T ob, args... a)
		{
			binder_t<Fx, T, args...> binder_(f, ob, a...);
			return binder_;
		}
	};

因此,完整的wstd::bind接口如下:
	template<typename Fx, typename T, typename ... args>
	auto bind(Fx f, T ob, args ... params)
	{
		// 利用std::is_member_function_pointer<decltype(f)即可判定函数指针f是否成员函数
		// value = true即为成员函数,反之即为非成员
		return make_binder<std::is_member_function_pointer<decltype(f)>::value>::template bind(f, ob, params ...);
	}

#测试代码

#include "bind.h"

#include <iostream>
#include <functional>

class print
{
public:
	print() {}
	virtual ~print() {}

	int draw(int a, int b, int c)
	{
		std::cout << "a = " << a << "," << "b = " << b << "," << "c = " << c << "\n";
		return 0;
	}
};

int test(int a, int b, int c)
{
	std::cout << "a = " << a << "," << "b = " << b << "," << "c = " << c << "\n";
	return 0;
}

int main()
{
	print pt_;

	std::function<int(int)> invoke_ = std::bind(test, 0, 0, std::placeholders::_1);
	invoke_(12345);
	std::function<int(int)> mem_invoke_ = std::bind(&print::draw, &pt_, 0, 0, std::placeholders::_1);
	mem_invoke_(12345);

	wstd::function<int (int)> inv_ = wstd::bind(test, 0, 0, std::placeholders::_1);
	inv_(12345);

	wstd::function<int (int)> mem_inv_ = wstd::bind(&print::draw, &pt_, 0, 0, std::placeholders::_1);
	mem_inv_(123);

	return 0;
}


Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.