模板编程:专业化和enable_if
Template programming: specialization and enable_if
我正在使用libffi,我已经用类似于std::function
(即class Func<Ret (Args...)> { /* ... */};
)的模板创建了一个类。我想将返回类型(Ret
)和每个参数类型(Args
)转换为相应的libffi类型(请参阅此参考)。到目前为止,我已经想出了这个:
// 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
s的情况下识别底层类型的方式。我更喜欢使用std::underlying_type<T>
(在代码中注释掉),但如果类型是例如空指针(type_traits:1762:38: error: ‘void*’ is not an enumeration type
),它会发出编译错误
我试图用std::enable_if
实现这种行为,但没有成功。。。请告诉我是否应该解释一下,以防听起来有点模糊!
摘要:我想让GetFFIType函数来确定编译期间的所有内容,并且该函数应该只支持基元类型(请参阅此以获得更广泛的参考)
编辑:对不起标题,没有什么比这更好的了:(
重载函数模板比专门化它们更容易,通常也更好。我将添加一个带有指针参数的函数版本,这样就可以在没有模板参数列表的情况下调用它:
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) ); }
将逻辑放入类模板而不是函数模板将允许部分专业化,我们也可以利用这一点进行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
- 我的简单if-else语句是如何无法访问的代码
- 如何将enable-if与模板参数和参数包一起使用
- 无论条件是否为true,if总是在c++中执行
- Arduino:for/while/if在void setup()或void loop()之前?——错误:之前需要不合格
- Insert函数不适用于2 if语句C++
- If语句未被求值C++
- C++嵌套if语句,基本货币交换
- 多个If语句与使用逻辑运算符计算条件的单个语句的比较
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 如何删除peer if else分支中的冗长句子
- 我似乎对if/else的基本语句有问题:/
- if数组上的随机数
- 将按位if条件转换为普通if条件
- If语句在c++中被忽略
- 比较if语句中的数组值和int值
- 使用if-else将数字转换为单词
- 为什么简单的算术减法在"if"条件下不起作用?
- 检查一个类型是否直接派生自"enable if"上下文中的另一个类型(是其子类型)
- SFINAE with boost enable if