首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模板编程:专门化和enable_if

模板编程:专门化和enable_if
EN

Stack Overflow用户
提问于 2012-07-10 00:07:24
回答 2查看 941关注 0票数 2

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

代码语言:javascript
复制
// 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函数的实现方式如下:

代码语言:javascript
复制
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<T> (在代码中注释掉),但是如果类型是空指针(type_traits:1762:38: error: ‘void*’ is not an enumeration type),它就会发出编译错误。

我试图用std::enable_if来实现这个行为,但是没有成功...一定要告诉我是否应该解释一些东西,以防它听起来有点模糊!

摘要:我想让GetFFIType函数在编译期间确定所有内容,并且该函数应该只支持基本类型(有关更广泛的参考,请参阅this )。

编辑:很抱歉这个标题,没有比这更好的了:(

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-07-10 01:25:43

将逻辑放在类模板中而不是函数模板中将允许部分特殊化,我们也可以在SFINAE技巧中利用这一点:

代码语言:javascript
复制
// 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所做的操作类似。

最后,这里是上面代码中假设的这两个实用程序的两个建议实现;显然,您不需要逐字使用它们,只要您以相同的方式编写其他东西。

代码语言:javascript
复制
// 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
票数 2
EN

Stack Overflow用户

发布于 2012-07-10 00:28:28

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

代码语言:javascript
复制
inline ffi_type * GetFFITypeHelper( void* ) { return &ffi_type_void; }
inline ffi_type * GetFFITypeHelper( byte* ) { return &ffi_type_uchar; }
// ...

然后,您可以将enable_if用于您想要涵盖的更一般的情况。

代码语言:javascript
复制
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) ); }

在声明了所有这些重载之后,您想要的版本是:

代码语言:javascript
复制
template<typename T> ffi_type * GetFFIType()
{ return GetFFITypeHelper( static_cast<T*>(nullptr) ); }
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11398966

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档