【问题标题】:Template programming: specialization and enable_if模板编程:专业化和 enable_if
【发布时间】:2012-07-09 16:07:23
【问题描述】:

我正在使用 libffi,并且我使用与 std::function 类似的模板创建了一个类(即class Func<Ret (Args...)> { /* ... */};。我想转换返回类型(Ret)和每个参数类型(Args ) 到它们对应的 libffi 类型(参见this 以供参考)。到目前为止,我已经想出了这个:

// 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 会发生运行时错误)。我该如何避免这种情况,并让这个函数在编译期间确定一个类型是否有效(原始类型)?

我也不喜欢在enums 的情况下识别基础类型的方式。我更喜欢使用 std::underlying_type&lt;T&gt; 代替(在代码中注释掉),但如果类型是 void 指针 (type_traits:1762:38: error: ‘void*’ is not an enumeration type),则会出现编译错误

我尝试使用 std::enable_if 实现此行为,但没有成功...请告诉我是否应该解释一些事情,以防听起来有点模糊!

总结:我想获取 GetFFIType 函数来确定编译期间的所有内容,并且该函数应该只支持原始类型(请参阅this 以获得更广泛的参考)

编辑:对不起标题,没有更好的想法:(

【问题讨论】:

    标签: c++ gcc c++11 libffi


    【解决方案1】:

    重载函数模板比​​专门化它们更容易而且通常更好。我将添加一个带有指针参数的函数版本,这样就可以在没有模板参数列表的情况下调用它:

    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) ); }
    

    【讨论】:

    • 等等,第一次尝试不好。 declval 将被评估。我想是固定的。
    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 2015-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-18
      • 2020-12-27
      相关资源
      最近更新 更多