C++函數委託

環境:

  win7_x64旗艦版、VS2015企業版ios

場景:

  C++標準庫提供std::function類來將一個對象的調用操做封裝在一個對象內部,而後能夠委託調用,可是有一些弊端,例以下面的需求:c++

    咱們須要將調用操做封裝存儲到一個map中,來實現觀察者模式或信號槽,因爲std::function是在編譯期肯定類型,致使你沒法將不一樣類型的std::function(例如std::function<void()>和std::function<void(int)>)放入同一個map中。ide

  function_delegate裏就是爲了解決上面的問題而寫的。函數

實現代碼:

 function_delegate.h性能

/**
* @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_
View Code

測試代碼:

function_delegate_test.h單元測試

#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;
    }
}
View Code

  1)其中time_stamp.h包含一個計時類time_stamp的實現,這裏沒貼代碼,能夠本身實現。測試

  2)function_delegate類使用成員data_[0]、data_[1]和data_[2]存儲成員函數指針是爲了處理被委託的類存在多重繼承的狀況(用於動態調整this指針,編譯器自動調整),若是隻使用一個void*存儲會發生崩潰。this

  注意:這裏有個奇怪的現象,對於使用虛擬繼承類成員函數,在個人臺式機上測試只須要使用data_[0]和data_[1]便可,而在個人筆記本上測試須要使用data_[0]、data_[1]和data_[2] ,因此我統一使用data_[3]存儲。spa

擴展:

  1)function_delegate類能夠用於實現觀察者模式和信號槽等,後面會有單獨的文章說明。.net

  2)關於C++ 成員函數指針使用兩個void*存儲的緣由:https://www.oschina.net/translate/wide-pointers?cmp

相關文章
相關標籤/搜索