2016-09-18 51 views
5

我試圖寫在c庫接口,使用這個熟悉的模式:如何將std :: function對象傳遞給函數指針的函數?

void some_c_handler(void(*func)(void*), void* data); 

現在,我想寫一個C++包裝這個功能看起來像這樣:

void my_new_cpp_handler(std::function<void()>&& func) 
{ 
    void (*p)() = foo(func); 
    void* data = bar(func); 
    some_c_handler(p, data); 
} 

some_c_handlermy_new_cpp_handler都解決了同樣的問題;他們正在接受某種功能以及某種狀態。但後者是首選,因爲它從用戶中抽象出大部分實現細節,並允許簡單地傳入一個lambda對象。

因此my_new_cpp_handler應該參照func參數,將其轉換爲函數指針並將其狀態轉換爲data

我對標準知之甚少,或者執行std::function知道這是否是一個合理的要求。 foobar是否存在?

換句話說,我想要的是能夠將有狀態函數傳遞給c回調處理程序,而無需手動實例化我自己的struct類型以傳遞它。顯然std::function已經爲我們做了這個,所以我很想能夠以某種方式將函數指針從狀態中分離出來,並將它傳遞到c樣式處理程序。這可能嗎?

回答

7

這可能嗎?

號你可以用C風格的回調到std::function<>...,編譯器會發出代碼提取datahandler並調用它。但是,確切的代碼將取決於編譯器,即ABI和正在使用的標準C++庫。你只能在std::function包裝下才能神奇地重建那些代碼。此外,任意std::function...(或lambda)可以將代碼其他包裝成比對C風格處理程序的調用。很明顯,應用「神奇重構」代碼從這樣一個std::function...實例中提取C風格的處理程序是不可能成功的。

P.S.

換句話說,我想要的是能夠將有狀態函數傳遞給c回調處理程序,而無需手動實例化自己的結構類型以傳遞它。顯然std::function已經做到了這一點對我們來說

那麼,爲什麼你剛纔用什麼std::function已經爲你做:

void my_new_cpp_handler(std::function<void()>&& func) 
{ 
    func(); // calls some_c_handler(p, data) IFF that is what "func" encodes. 
} 
-2

根據this鏈接,std::function對象沒有可訪問的成員可以提供對指針的原始訪問。您應該定義一個struct,它包含一個指向函數指針和對象的指針,以及一個構造函數包裝器,它在構造std::struct之前將指針的地址存儲到結構中,以便分配存儲在指向它的指針中的地址到你的C處理程序的參數。

4

你可以做一個包裝函數,其目的是簡單地執行std::function回調。

void some_c_handler(void(*)(void*), void*) {} 

void std_function_caller(void* fn) { 
    (*static_cast<std::function<void()>*>(fn))(); 
}; 

auto make_std_function_caller(std::function<void()>& fn) { 
    return std::make_pair(std_function_caller, static_cast<void*>(&fn)); 
} 

void my_new_cpp_handler(std::function<void()>&& func) { 
    const auto p = make_std_function_caller(func); 
    some_c_handler(p.first, p.second); 
} 
4

如果你仔細查看這個庫中的文檔,你會發現,

void some_c_handler(void(*func)(void*), void* data); 

調用func,其傳遞data說法。

對於採用回調函數的C庫,這是一種非常常見的設計模式。除了回調函數之外,它們還帶有一個額外的不透明指針,該指針不是由庫解釋的,而是被盲目地轉發到func。換句話說,C庫調用

func(data); 

您可以在C++代碼中使用它來將普通指針傳遞給任何類。

這也包括std::function

訣竅是,在大多數情況下,有必要使用new

auto *pointer=new std::function<function_type>... 

最終的結果是,可以傳遞到C庫的指針,連同一個指向「蹦牀功能「:

some_c_handler(&cpp_trampoline, reinterpret_cast<void *>(pointer)); 

而且蹦牀重鑄不透明指針:

void cpp_trampoline(void *pointer) 
{ 
    auto real_pointer=reinterpret_cast<std::function<...>*>(pointer); 

    // At this point, you have a pointer to the std::function here. 
    // Do with it as you wish. 

您需要在這裏排除的唯一細節是找出動態分配的函數指針的正確範圍,以避免內存泄漏。