前情簡介
在完成了第一版的《在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] https://stackoverflow.com/questions/9779105/generic-member-function-pointer-as-a-template-parameter