2012-07-09 75 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