2012-07-09 74 views
2

我正在使用libffi和我已經做了一個類似的模板std::function(即class Func<Ret (Args...)> { /* ... */};。我想將返回類型(Ret)和每個參數類型(Args)轉換爲。其相應的libffi類型(見this僅供參考)到目前爲止,我想出了這一點:模板編程:專業化和enable_if

// Member function of 'Func' class 
Prepare(void) 
{ 
// This vector holds all the type structures 
std::vector<ffi_type*> argumentTypes{ GetFFIType<Args>()... }; 
ffi_type * returnType = GetFFIType<Ret>(); 

// Rest of the code below 
// .... 
} 

凡GetFFIType功能是通過以下實現的:

template <typename T> 
ffi_type * GetFFIType(void) 
{ 
    // We will check for any kind of pointer types 
    if(std::is_pointer<T>::value || std::is_array<T>::value || 
     std::is_reference<T>::value || std::is_function<T>::value) 
     return &ffi_type_pointer; 

    if(std::is_enum<T>::value) 
     //return GetFFIType<std::underlying_type<T>::type>(); 
    { 
     // Since the size of the enum may vary, we will identify the size 
     if(sizeof(T) == ffi_type_schar.size) return std::is_unsigned<T>::value ? &ffi_type_uchar : &ffi_type_schar; 
     if(sizeof(T) == ffi_type_sshort.size) return std::is_unsigned<T>::value ? &ffi_type_ushort : &ffi_type_sshort; 
     if(sizeof(T) == ffi_type_sint.size) return std::is_unsigned<T>::value ? &ffi_type_uint : &ffi_type_sint; 
     if(sizeof(T) == ffi_type_slong.size) return std::is_unsigned<T>::value ? &ffi_type_ulong : &ffi_type_slong; 
    } 

    assert(false && "cannot identify type"); 
} 

// These are all of our specializations 
template <> ffi_type * GetFFIType<void>(void)  { return &ffi_type_void; } 
template <> ffi_type * GetFFIType<byte>(void)  { return &ffi_type_uchar; } 
template <> ffi_type * GetFFIType<char>(void)  { return &ffi_type_schar; } 
template <> ffi_type * GetFFIType<ushort>(void)  { return &ffi_type_ushort; } 
template <> ffi_type * GetFFIType<short>(void)  { return &ffi_type_sshort; } 
template <> ffi_type * GetFFIType<uint>(void)  { return &ffi_type_uint; } 
template <> ffi_type * GetFFIType<int>(void)  { return &ffi_type_sint; } 
template <> ffi_type * GetFFIType<ulong>(void)  { return &ffi_type_ulong; } 
template <> ffi_type * GetFFIType<long>(void)  { return &ffi_type_slong; } 
template <> ffi_type * GetFFIType<float>(void)  { return &ffi_type_float; } 
template <> ffi_type * GetFFIType<double>(void)  { return &ffi_type_double; } 
template <> ffi_type * GetFFIType<long double>(void) { return &ffi_type_longdouble; } 

這個工作,但顯然有一些改進的餘地。如果類型無效(即類或結構),則在編譯時不會識別(發生運行時錯誤,而不是使用assert)。我將如何避免這種情況,並使編譯過程中該函數確定類型是否有效(基本類型)?

我也不喜歡我在enum的情況下識別底層類型的方式。我寧願用std::underlying_type<T>代替(代碼中的註釋),但如果類型是例如一個空指針會發出編譯錯誤(type_traits:1762:38: error: ‘void*’ is not an enumeration type

我試圖實現使用std::enable_if但沒有成功這種行爲...操作告訴我是否應該解釋一些事情,以防聽起來有點模糊!

摘要:我想要得到的GetFFIType功能在編譯期間決定一切,功能應該只支持基本類型(見this更爲詳盡的參考)

編輯:很抱歉的標題,沒有什麼更好的來介意:(

回答

2

把邏輯類模板內,而不是一個函數模板將允許部分特例,我們也可以採取對於SFINAE技巧的優勢:

// Second parameter is an implementation detail 
template<typename T, typename Sfinae = std::true_type> 
struct ToFFIType; 

// Front-end 
template<typename T> 
ffi_type* GetFFIType() 
{ return ToFFIType<T>::make(); } 

// Primary template where we end up if we don't know what to do with the type 
template<typename T, typename = std::true_type> 
struct ToFFIType { 
    static_assert(dependent_false_type<T>::value, 
        "Write your clever error message to explain why we ended up here"); 

    static ffi_type* make() = delete; 
}; 

// Trait-like to match what we want with ffi_type_pointer 
template<typename T> 
struct treat_as_pointer: or_< 
    std::is_pointer<T> 
    , std::is_array<T> 
    , std::is_reference<T> 
    , std::is_function<T> 
> {}; 

template<typename T> 
struct ToFFIType<T, typename treat_as_pointer<T>::type> { 
    static ffi_type* make() 
    { return &fii_type_pointer; } 
}; 

// Matches enumeration types 
template<typename T> 
struct ToFFIType<T, typename std::is_enum<T>::type> { 
    static ffi_type* make() 
    { 
     return ToFFIType<typename std::underlying_type<T>::type>::make(); 
    } 
}; 

總專精很直接寫,所以我不會顯示它們。雖然請注意,您可以選擇改爲匹配例如std::is_integral並打開sizeof(T),如果您需要,類似於您在std::underlying_type中所做的工作。

最後,這裏是上述代碼中假設的兩個實用程序的兩個建議實現;顯然你不需要逐字逐句使用它們,只要你以同樣的方式寫下其他東西。

// Same functionality as std::false_type but useful 
// for static_assert in templates 
template<typename Dummy> 
struct dependent_false_type: std::false_type {}; 

// Disjunction of boolean TMP integral constants 
// Take care to inherit from std::true_type/std::false_type so 
// the previous SFINAE trick works 
template<typename... T> 
struct or_: std::false_type {}; 

// There likely are better implementations 
template<typename Head, typename... Tail> 
struct or_<Head, Tail...>: std::conditional< 
    Head::value 
    , std::true_type    // short circuit to desired base 
    , typename or_<Tail...>::type // or inherit from recursive base 
>::type {}; // Note: std::conditional is NOT the base 
4

它更容易,通常更好的重載函數模板,而不是專注他們,我將添加一個版本的指針參數的函數,因此它可以在沒有模板參數列表來調用:。

inline ffi_type * GetFFITypeHelper(void*) { return &ffi_type_void; } 
inline ffi_type * GetFFITypeHelper(byte*) { return &ffi_type_uchar; } 
// ... 

然後,您可以使用enable_if作爲您想要覆蓋的更一般化的案例。

template<typename T> auto GetFFITypeHelper(T*) -> 
    std::enable_if< std::is_function<T>::value, ffi_type* >::type 
{ return &ffi_type_pointer; } 
template<typename T> auto GetFFITypeHelper(T*) -> 
    std::enable_if< std::is_enum<T>::value, ffi_type* >::type 
{ return GetFFITypeHelper(static_cast<std::underlying_type<T>::type*>(nullptr)); } 

畢竟這些重載聲明,你想要的版本是:

template<typename T> ffi_type * GetFFIType() 
{ return GetFFITypeHelper(static_cast<T*>(nullptr)); } 
+0

等等,第一次嘗試並不好。 'declval'會被評估。固定,我想。 – aschepler 2012-07-09 16:37:15