C++函数委托
环境:
win7_x64旗舰版、VS2015企业版
场景:
C++标准库提供std::function类来将一个对象的调用操作封装在一个对象内部,然后可以委托调用,但是有一些弊端,例如下面的需求:
我们需要将调用操作封装存储到一个map中,来实现观察者模式或信号槽,由于std::function是在编译期确定类型,导致你无法将不同类型的std::function(例如std::function<void()>和std::function<void(int)>)放入同一个map中。
function_delegate里就是为了解决上面的问题而写的。
实现代码:
function_delegate.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * @file function_delegate.h * @brief 函数委托 * @author DC * @date 2019-04-16 * @note * @example * class my_class * { * public: * void test(int a, double b) * { * } * }; * * my_class my; * function_delegate<> delegate(&my, &my_class::test); * delegate(1, 2.0); * */ #ifndef FUNCTION_DELEGATE_H_ #define FUNCTION_DELEGATE_H_ namespace function_delegate_pri { ///< 对象成员函数指针 class member_ptr { public: ///< 哈希函数 typedef std::_Bitwise_hash<member_ptr> hash; ///< 构造函数 template<class T, class Func> member_ptr(T* obj, Func func) { obj_ = obj; *(Func*)&func_ = func; } ///< 小于函数 bool operator <(const member_ptr& ptr) const { if (func_[0] != ptr.func_[0]) { return func_[0] < ptr.func_[0]; } if (func_[1] != ptr.func_[1]) { return func_[1] < ptr.func_[1]; } return obj_ < ptr.obj_; } ///< 相等函数 bool operator ==(const member_ptr& ptr) const { if (func_[0] != ptr.func_[0]) { return false; } if (func_[1] != ptr.func_[1]) { return false; } return obj_ == ptr.obj_; } ///< 调用函数 template < typename T, typename U, typename Result, typename ... Args > Result invoke(Args... args) { typedef Result(U::*Call)(Args...); Call call = *(Call*)&func_; return (((T*)obj_)->*call)(args...); } void* obj_{nullptr}; ///< 对象指针 ///< 对象成员函数需要使用三个指针,C++多重虚拟继承导致 void* func_[3]{nullptr}; ///< 类成员函数指针 }; ///< 函数委托 template<class Strategy = void> class delegate_impl { public: ///< 设置对象和成员函数 template < typename T, typename U, typename Result, typename ... Args > delegate_impl(T* ptr, Result(U::*fn)(Args...)) : refcnt_(ptr), ptr_(ptr, fn) { typedef Result(*Invoke)(member_ptr*, Args...); Invoke call = &delegate_impl::invoke<T, U, Result, Args...>; invoke_ = call; } bool operator <(const delegate_impl& func) const { if (invoke_ != func.invoke_) { return invoke_ < func.invoke_; } return ptr_ < func.ptr_; } ///< 调用成员函数 template < typename Result = void, typename ... Args > Result operator()(Args... args) const { typedef Result(*Invoke)(member_ptr*, Args...); Invoke call = (Invoke)invoke_; return call((member_ptr*)&ptr_, args...); } Strategy* object() const { return refcnt_; } private: template < typename T, typename U, typename Result, typename ... Args > static Result invoke(member_ptr* ptr, Args... args) { return ptr->invoke<T, U, Result>(args...); } Strategy* refcnt_{ nullptr }; ///< member_ptr ptr_; ///< 成员函数指针 void* invoke_{ nullptr }; ///< invoke函数地址 }; template<> class delegate_impl<void> { public: ///< 设置对象和成员函数 template < typename T, typename U, typename Result, typename ... Args > delegate_impl(T* ptr, Result(U::*fn)(Args...)) : ptr_(ptr, fn) { typedef Result(*Invoke)(member_ptr*, Args...); Invoke call = &delegate_impl::invoke<T, U, Result, Args...>; invoke_ = call; } bool operator <(const delegate_impl& func) const { if (invoke_ != func.invoke_) { return invoke_ < func.invoke_; } return ptr_ < func.ptr_; } ///< 调用成员函数 template < typename Result = void, typename ... Args > Result operator()(Args... args) const { typedef Result(*Invoke)(member_ptr*, Args...); Invoke call = (Invoke)invoke_; return call((member_ptr*)&ptr_, args...); } private: template < typename T, typename U, typename Result, typename ... Args > static Result invoke(member_ptr* ptr, Args... args) { return ptr->invoke<T, U, Result>(args...); } member_ptr ptr_; ///< 成员函数指针 void* invoke_{ nullptr }; ///< invoke函数地址 }; } using function_ptr = function_delegate_pri::member_ptr; template<class Strategy = void> using function_delegate = function_delegate_pri::delegate_impl<Strategy>; #endif ///< !FUNCTION_DELEGATE_H_
测试代码:
function_delegate_test.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <vector> #include <functional> #include "time_stamp.h" #include "function_delegate.h" namespace function_delegate_unit_test { class testa { public: virtual int testa1(int n) { std::cout << "testa::testa1\tparam:" << n << ", data:"; for (int v : avec_) { std::cout << v << ","; } std::cout << std::endl; return n; } int testa2(int n) { std::cout << "testa::testa2\tparam:" << n << ", data:"; for (int v : avec_) { std::cout << v << ","; } std::cout << std::endl; return n; } protected: std::vector<int> avec_{ 1, 2, 3 }; }; class testb { public: virtual int testb1(int n) { std::cout << "testb::testb1\tparam:" << n << ", data:"; for (int v : bvec_) { std::cout << v << ","; } std::cout << std::endl; return n; } int testb2(int n) { std::cout << "testb::testb2\tparam:" << n << ", data:"; for (int v : bvec_) { std::cout << v << ","; } std::cout << std::endl; return n; } protected: std::vector<int> bvec_{ 4, 5, 6 }; }; class testc : public testa, public testb { public: int testa1(int n) override { std::cout << "testc::testa1\tparam:" << n << ", data:"; for (int v : bvec_) { std::cout << v << ","; } std::cout << std::endl; return testa::testa1(n); } int testb1(int n) override { std::cout << "testc::testb1\tparam:" << n << ", data:"; for (int v : bvec_) { std::cout << v << ","; } std::cout << std::endl; return testb::testb1(n); } int test(int n) { std::cout << "testc::testb1\tparam:" << n << ", data:"; for (int v : avec_) { std::cout << v << ","; } for (int v : bvec_) { std::cout << v << ","; } std::cout << std::endl; return n; } }; class testd { public: virtual int test(int n) { std::cout << "testd::test\tparam:" << n << ", data:"; for (int v : dvec_) { std::cout << v << ","; } std::cout << std::endl; return n; } int testd1(int n) { std::cout << "testd::testd1\tparam:" << n << ", data:"; for (int v : dvec_) { std::cout << v << ","; } std::cout << std::endl; return n; } protected: std::vector<int> dvec_{ 1, 2, 3 }; }; class teste { public: virtual int test(int n) { std::cout << "teste::test\tparam:" << n << ", data:"; for (int v : evec_) { std::cout << v << ","; } std::cout << std::endl; return n; } int teste1(int n) { std::cout << "teste::teste1\tparam:" << n << ", data:"; for (int v : evec_) { std::cout << v << ","; } std::cout << std::endl; return n; } protected: std::vector<int> evec_{ 1, 2, 3 }; }; class testf : public virtual testd, public virtual teste { public: virtual int test(int n) override { std::cout << "teste::test\tparam:" << n << ", data:"; for (int v : dvec_) { std::cout << v << ","; } for (int v : evec_) { std::cout << v << ","; } std::cout << std::endl; testd::test(n); teste::test(n); return n; } int testf1(int n) { std::cout << "testf::testf1\tparam:" << n << ", data:"; for (int v : dvec_) { std::cout << v << ","; } for (int v : evec_) { std::cout << v << ","; } std::cout << std::endl; return n; } }; class testg { public: int test(int n) { std::cout << "testg::test\tparam:" << n << ", data:"; for (int v : gvec_) { std::cout << v << ","; } std::cout << std::endl; return n; } protected: std::vector<int> gvec_{ 1, 2, 3 }; }; ///< 单元测试 void test() { std::cout << "function_delegate_unit_test start" << std::endl; ///< TEST testa testa a; { function_delegate<> delegate(&a, &testa::testa1); delegate(1000); } { function_delegate<> delegate(&a, &testa::testa2); delegate(1000); } ///< TEST testb testb b; { function_delegate<> delegate(&b, &testb::testb1); delegate(1000); } { function_delegate<> delegate(&b, &testb::testb2); delegate(1000); } ///< TEST testc testc c; { function_delegate<> delegate(&c, &testa::testa1); delegate(1000); } { function_delegate<> delegate(&c, &testa::testa2); delegate(1000); } { function_delegate<> delegate(&c, &testb::testb1); delegate(1000); } { function_delegate<> delegate(&c, &testb::testb2); delegate(1000); } { function_delegate<> delegate(&c, &testc::testa1); delegate(1000); } { function_delegate<> delegate(&c, &testc::testa2); delegate(1000); } { function_delegate<> delegate(&c, &testc::testb1); delegate(1000); } { function_delegate<> delegate(&c, &testc::testb2); delegate(1000); } { function_delegate<> delegate(&c, &testc::test); delegate(1000); } ///< TEST testd testd d; { function_delegate<> delegate(&d, &testd::testd1); delegate(1000); } { function_delegate<> delegate(&d, &testd::test); delegate(1000); } ///< TEST teste teste e; { function_delegate<> delegate(&e, &teste::teste1); delegate(1000); } { function_delegate<> delegate(&e, &teste::test); delegate(1000); } ///< TEST testf testf f; { function_delegate<> delegate(&f, &testd::testd1); delegate(1000); } { function_delegate<> delegate(&f, &testd::test); delegate(1000); } { function_delegate<> delegate(&f, &teste::teste1); delegate(1000); } { function_delegate<> delegate(&f, &teste::test); delegate(1000); } { function_delegate<> delegate(&f, &testf::testd1); delegate(1000); } { function_delegate<> delegate(&f, &testf::teste1); delegate(1000); } { function_delegate<> delegate(&f, &testf::test); delegate(1000); } ///< TEST testg testg g; { function_delegate<> delegate(&g, &testg::test); delegate(1000); } std::cout << "function_delegate_unit_test end" << std::endl; } } namespace function_delegate_efficiency_test { class testa { public: int test(int n) { return n + 1; } }; class testb { public: virtual int test(int n) { return n + 1; } }; class testc : public testb { public: int test(int n) override { return n + 2; } }; ///< 性能测试 void test(int count) { std::cout << "function_delegate_efficiency_test start" << std::endl; testa a; { time_stamp ts; for (int i = 0; i < count; ++i) { a.test(i); } double tm = ts.milliseconds(); std::cout << "count:" << count << "\tc++ object call:\t\t" << tm << "(ms)" << std::endl; } { time_stamp ts; std::function<int(int)> func = std::bind(&testa::test, &a, std::placeholders::_1); for (int i = 0; i < count; ++i) { func(i); } double tm = ts.milliseconds(); std::cout << "count:" << count << "\tstd::function call:\t\t" << tm << "(ms)" << std::endl; } { time_stamp ts; function_delegate<> delegate(&a, &testa::test); for (int i = 0; i < count; ++i) { delegate(i); } double tm = ts.milliseconds(); std::cout << "count:" << count << "\tdelegate call:\t\t\t" << tm << "(ms)" << std::endl; } testc c; testb* pb = &c; { time_stamp ts; for (int i = 0; i < count; ++i) { pb->test(i); } double tm = ts.milliseconds(); std::cout << "count:" << count << "\tc++ pointer virtual call:\t" << tm << "(ms)" << std::endl; } { time_stamp ts; std::function<int(int)> func = std::bind(&testb::test, pb, std::placeholders::_1); for (int i = 0; i < count; ++i) { func(i); } double tm = ts.milliseconds(); std::cout << "count:" << count << "\tstd::function virtual call:\t" << tm << "(ms)" << std::endl; } { time_stamp ts; function_delegate<> delegate(pb, &testb::test); for (int i = 0; i < count; ++i) { delegate(i); } double tm = ts.milliseconds(); std::cout << "count:" << count << "\tdelegate virtual call:\t\t" << tm << "(ms)" << std::endl; } std::cout << "function_delegate_efficiency_test end" << std::endl; } }
1)其中time_stamp.h包含一个计时类time_stamp的实现,这里没贴代码,可以自己实现。
2)function_delegate类使用成员data_[0]、data_[1]和data_[2]存储成员函数指针是为了处理被委托的类存在多重继承的情况(用于动态调整this指针,编译器自动调整),如果只使用一个void*存储会发生崩溃。
注意:这里有个奇怪的现象,对于使用虚拟继承类成员函数,在我的台式机上测试只需要使用data_[0]和data_[1]即可,而在我的笔记本上测试需要使用data_[0]、data_[1]和data_[2] ,所以我统一使用data_[3]存储。
扩展:
1)function_delegate类可以用于实现观察者模式和信号槽等,后面会有单独的文章说明。
2)关于C++ 成员函数指针使用两个void*存储的原因:https://www.oschina.net/translate/wide-pointers?cmp