我使用一個API,其宣告是:
some_API.h
typedef void (CALLBACK *EventCallback)()。
class some_API{
public:
//...: public.
void EnableInterrupt(EventCallback func1, EventCallback func2)。
//...
};
而在另一邊,我有一個使用這個API的類:
My_class.h
class My_class
{
some_API API。
void CALLBACK my_func() { cout << "this is my_func!
" }
public:
void using_api().
{
API.EnableInterrupt(my_func, nullptr)。
}
};
主要問題是my_func的型別,錯誤:
錯誤(active) E0167 "void (__stdcall My_class::*)() "型別的引數與 "EventCallback "型別的引數不兼容。
我找到了這個答案,但問題是,這個API是近源的,所以我不能改變void EnableInterrupt(EventCallback, EventCallback)的宣告。
我也不想把My_class::using_API和My_class::API宣告為靜態成員。
我想要類似于這樣的東西:
API。 EnableInterrupt((static_cast<void*>(my_func)), nullptr) 。
//or
API.EnableInterrupt((static_cast<EventCallback>(my_func)), nullptr); // invalid type conversion。
是否有辦法將該成員函式轉換為非成員函式以傳遞給some_API::EnableInterrupt(...)?
uj5u.com熱心網友回復:
你的問題是回呼型別
typedef void (CALLBACK *EventCallback)()。
沒有任何用戶提供的資料指標。這是慣例,正是因為用戶經常需要它。
而且只是為了完整起見,
是否有辦法將該成員函式轉換為非成員函式呢
不,它們從根本上是不同的,因為非靜態成員函式必須用隱式this指標來呼叫,而在普通的函式指標中沒有地方可以存盤它。這正是我們使用用戶提供的指標引數的目的,如果 API 有這樣的引數。
我也不想把My_class::using_API和My_class::API宣告為靜態成員。
如果我們為每個不同的注冊生成另一個static怎么樣?我們可以通過使用lambda型別的唯一性來實作自動化:
template <typename Lambda>
class TrampolineWrapper
{
inline static std::optional<Lambda> dest_; //optional所以我們可以推遲初始化。
public:
TrampolineWrapper(some_API *api, Lambda &&lambda)
{
//將你的_stateful_ functor存盤為一個唯一的靜態。
dest_.emplace( std::move(lambda) ) 。
//注冊一個唯一的_stateless_ lambda(可轉換為自由函式)。
api->EnableInterrupt(
[](){
(*TrampolineWrapper<Lambda>::dest_)()。
},
nullptr)。)
}
};
//方便的型別教育函式。
template <typename Lambda>
TrampolineWrapper<Lambda> wrap(some_API *api, Lambda & &lambda)
{
return {api, std::move(lambda)};
}
并且像這樣使用它
class My_class
{
some_API API。
void my_func() { cout << "this is my_func!
" }
public:
void using_api()
{
auto wrapper = wrap(&API, [this](){ my_func(); }) 。
// stateful lambda ^^^^
//不能直接轉換為自由函式。
}
};
這依賴于lambda型別的唯一性來作業--你使用的每個lambda都會得到一個唯一的相同型別的靜態dest_物件(它可能是有狀態的),以及一個唯一的無狀態轉發lambda(對于這個可怕的API,它可以轉換為一個自由函式)。
注意,只有當你有多個不同的呼叫者注冊了不同的 lambdas 時,這才是唯一的。兩個對My_class::using_api的呼叫仍然會發生沖突(第二個將覆寫第一個的 lambda)。
唯一令人討厭的是,一旦靜態設定完成,我們就會丟棄包裝器......如果能保留它,我們就可以用它來取消注冊回呼或其他東西,那就更棒了。如果你想的話,你可以這樣做,但由于你不能在lambda的范圍之外知道它的型別,它需要做一些動態的事情。
uj5u.com熱心網友回復:
當你有東西將回呼函式與你的類實體聯系起來時,C和C 之間的回呼作業得很好。為了使其發揮作用,你需要從C端傳遞資訊(通常在回呼注冊時提供一個void*)。最簡單的是使用一個靜態方法和this指標:
typedef void (*EventCallback)(void*)();
void EnableInterrupt(EventCallback func1, void *userdata);
static void CallbackWrapper(void* data) {
static_cast<My_class*>(data)->my_func()。
}
EnableInterrupt(CallbackWrapper, this)
現在,當你的回呼是這樣的:
typedef void (CALLBACK *EventCallback)()。
這可能是由于糟糕的設計,也可能是API設計者不希望你注冊一個以上的回呼處理程式。這可能是一個重要的問題,考慮到該函式將擾亂中斷,如果他們不希望你注冊多個處理程式,我將理解。
因此,您只需要創建一個類的實體,所以單子模式可能是一個不錯的選擇。為了實作這一目標,你必須這樣做:
void CALLBACK global_callback_handler(){
My_class::getInstance()-> my_func()
}
void using_api()
{
API.EnableInterrupt(global_callback_handler)。
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/315460.html
標籤:
上一篇:是否可以將幾個不同的函式宣告指向一個單一的函式定義?
下一篇:將指標設定為NULL可修復專案
