在C++中使用libuv時對回調的處理 (2)


前情簡介

在完成了第一版的《在C++中使用libuv時對回調的處理》之后,在對項目進行開發的時候,還是感覺有一些難受。

因為在實際操作的時候,需要構建一個結構體,並且需要對這個結構體的內存進行管理,非常的麻煩。

在對C++的模板編程進行簡單的學習后,了解到一個比較基本的知識。如果一個值或者類型能在編譯的時候確定,那么它是一定可以作為模板參數的。

反觀我之前為了完成操作構建的結構體,可以很明顯的發現,成員函數指針那一個變量是一直保持不變的,而且可以在編譯的時候確定,所以是有辦法將成員函數指針放入模板里面的。

解決方案

使用回調的函數

typedef struct {
    void *data;
    int s;
} call_back_t;

typedef void (*call_back_func_t)(call_back_t *t, int s, int v);

int call_back_func(call_back_t *t, call_back_func_t func) {
    func(t, t->s, 12);
    return 0;
}

回調函數及其類

class CallBack {
public:
    int a = 0;
    void call_back(call_back_t *t, int s, int v) {
        std::cout << "t->s:" << t->s << std::endl;
        std::cout << "s:" << s << std::endl;
        std::cout << "v:" << v << std::endl;
        std::cout << "a:" << a << std::endl; 
    }
};

解決方案

template<typename T,  T>
struct comm_call_back_s;

template<typename ClassType, typename ...ArgTypes, void (ClassType::*FunType)(call_back_t *t, ArgTypes...) >
struct comm_call_back_s<void (ClassType::*)(call_back_t *t, ArgTypes...), FunType> {
    static void comm_call_back(call_back_t *t, ArgTypes... Value) {
        ClassType *mClass = static_cast<ClassType *>(t->data);
        (mClass->*FunType)(t, std::forward<ArgTypes>(Value)...);
    }
};

#define define_comm_call_back_s(F) (comm_call_back_s<decltype((F)), (F)>::comm_call_back)

以上代碼是根據[1]中大佬代碼修改得來的。首先是第一段

template<typename T,  T>
struct comm_call_back_s;

這一段代碼定義了模板的原型,模板包括兩個參數。一個是類型T,另一個是類型為T的值。

template<typename ClassType, typename ...ArgTypes, void (ClassType::*FunType)(call_back_t *t, ArgTypes...) >
struct comm_call_back_s<void (ClassType::*)(call_back_t *t, ArgTypes...), FunType> {
    static void comm_call_back(call_back_t *t, ArgTypes... Value) {
        ClassType *mClass = static_cast<ClassType *>(t->data);
        (mClass->*FunType)(t, std::forward<ArgTypes>(Value)...);
    }
};

第二段代碼是我們主要使用的偏特化模板。一共定義了三個模板參數,第一個是包含回調函數的類,第二個是回調函數的部分參數,第三個是回調函數。

在具體的特化中,我們將回調函數的類型作為原型里面的類型T,回調函數作為值。

然后,我們定義了一個comm_call_back函數作為我們封裝的回調函數。

#define define_comm_call_back_s(F) (comm_call_back_s<decltype((F)), (F)>::comm_call_back)

最后一段,我們定義了一個宏,方便我們的調用。

使用

int main() {
    CallBack b;
    b.a = 100;
    call_back_t call;
    call.s = 1024;
    call.data = static_cast<void *>(&b);
    call_back_func(&call, define_comm_call_back_s(&CallBack::call_back));
}

完整代碼示范

反思

在寫完這一些代碼后,我思考了幾個問題,並做了一定的解答。

  1. 為什么使用結構體,而不直接使用模板函數。
    因為我們在定義模板原型的時候沒辦法決定函數的參數,所以先使用結構體定義,然后使用偏特化實現具體的函數。

引用

[1] https://stackoverflow.com/questions/9779105/generic-member-function-pointer-as-a-template-parameter


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM