2013-05-10 87 views
0

我想實現一些協議作爲類模板,其中raw_write和raw_read函數作爲模板參數。這兩種原料的功能已經嚴格定義的接口:如何將lambda,函數對象或std :: function作爲模板參數傳遞並控制其參數

int raw_write(uint8_t *src, size_t len); 
int raw_read(uint8_t *dst, size_t maxlen); 

這是任何方式,當有人試圖 通過例如控制與編譯錯誤,這個接口:

int raw_write2(uint16_t *src, size_t len); 

我應該通過模板參數作爲對象指定的類型或作爲模板實現內部實例化的類型名稱?

+2

如果你只接受一個參數類型,它有什麼用途嗎? – 2013-05-10 14:41:01

+2

@SanderDeDycker我自己有點困惑,但我*想*他想要一個類模板,它將保存一些可用指定簽名調用的對象。但它可能是一個函數指針,函子,lambda,只要它有正確的簽名。所以['std :: function'](http://en.cppreference.com/w/cpp/utility/functional/function)可能是某種形式的答案。 – BoBTFish 2013-05-10 14:43:22

+0

@BoBTFish:看起來你是對的 - 也閱讀標題,而不僅僅是身體幫助:-) – 2013-05-10 14:48:44

回答

1

你可以用SFINAE和一些特徵類來做到這一點。

我懷疑做到這一點的最好方法是期待一個兼容通話的仿函數 - 這也會更容易。

#include <utility> 
#include <type_traits> 
template<typename T, bool=true> 
struct raw_write_compatible: std::false_type {}; 
template<typename T, bool=true> 
struct raw_read_compatible: std::false_type {}; 
template<typename T> 
struct raw_write_compatible< 
    T, 
    std::is_convertable< 
    decltype(
     std::declval<T&>()(
     std::declval<uint8_t *>(), 
     std::declval<size_t>() 
    ) 
    ), 
    int 
    >::value 
>: std::true_type {}; 
template<typename T> 
struct raw_read_compatible< 
    T, 
    std::is_convertable< 
    decltype(
     std::declval<T&>()(
     std::declval<uint8_t *>(), 
     std::declval<size_t>() 
    ) 
    ), 
    int 
    >::value 
>: std::true_type {}; 

的這些要點是raw_read_compatible<T>::valuetrue IFF的T一個實例可以通過與簽名(uint8_t*, size_t)進行評估,並返回類型可以轉換爲一個int

(順便說一句,你的「寫」函數簽名或許應該採取指針const uint8_t,因爲它不修改參數。)

你會使用這樣的:

template<typename Reader, typename Writer> 
typename std::enable_if< 
    raw_read_compatible<Reader>::value && raw_write_compatible<Writer>::value, 
    bool // return value of do_some_io_stuff 
>::type do_some_io_stuff(Reader const& reader, Writer const& writer) { 
    return true; 
} 

do_some_io_stuff將匹配iff讀寫器可以以您想要的方式調用。

這樣做的好處,當你試圖在不兼容的拉姆達或函數指針或仿函數來傳遞do_some_io_stuff未能比賽,而不是匹配,然後未能編譯。從理論上講,這可以讓你覆蓋事物。

上述解決方案需要一個具有良好C++ 11支持的編譯器:例如,MSVC2012不支持上述操作(它缺少「表達式SFINAE」)。

一個簡單的解決辦法是隻取std::function< int(uint8_t*, size_t) >,但有兩項費用:第一,它在每次調用運行時間成本(大致相當於一個virtual方法調用 - 相比IO時使不高) - 通過函數調用邊界阻止優化的實際成本。其次,你可以得到一些編譯失敗,而不是失敗,以符合我的經驗中的簽名錯誤(我不確定C++ 11標準是否指定std::function的「lambda」構造函數只有在傳遞兼容類型時才匹配,但我認爲我已經看到那些測試失敗的實現)。

std::function解決方案的優點是它更簡單,它允許將實現放在單獨的文件中,並且意圖更易於理解。

+0

我對這個解決方案印象非常深刻! – ardabro 2013-05-10 15:12:28

+0

@ardabro謝謝。如果我寫或找到了「符號兼容」特徵類,比如'is_signature_compatible :: value',會更好。 – Yakk 2013-05-10 17:46:16

7

認爲這是所期望的(或至少一個)使用std::function溶液來存儲可調用(一個用於不要求定義template類):

#include <iostream> 
#include <functional> 

struct protocol_callbacks 
{ 
    using func_t = std::function<int(uint8_t*, size_t)>; 

    protocol_callbacks(func_t a_reader, func_t a_writer) : 
     reader(a_reader), 
     writer(a_writer) {} 
    func_t reader; 
    func_t writer; 
}; 

int writer(uint8_t*, size_t) { return 0; } 
int reader(uint8_t*, size_t) { return 0; } 

int bad_writer(uint16_t*, size_t) { return 0; } 

int main() 
{ 
    protocol_callbacks pc1(reader, writer); 
    protocol_callbacks pc2([](uint8_t*, size_t) { return 0; }, 
          [](uint8_t*, size_t) { return 0; }); 
    //protocol_callbacks pc3(bad_writer, reader); 
} 

bad_writer使用造成的彙編失敗(沒有bad_writerhttp://ideone.com/hG7tqcbad_writerhttp://ideone.com/roMJgM)。

+0

好,簡單。沒有必要的模板。謝謝。 – ardabro 2013-05-10 15:13:39

相關問題