[英]Template programming: specialization and enable_if
I'm working with libffi and I've made a class with a similar template to std::function
(ie class Func<Ret (Args...)> { /* ... */};
. I want to convert the return type ( Ret
) and each argument type ( Args
) to their corresponding libffi type (see this for reference). So far I've come up with this: 我正在使用libffi,并且已经制作了一个具有与
std::function
类似的模板的class Func<Ret (Args...)> { /* ... */};
(即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
// ....
}
Where the GetFFIType function is implemented as the following: 其中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; }
This works, but obviously there is some room for improvements. 这可行,但是显然还有一些改进的空间。 If the type is invalid (ie a class or a structure) it is not identified at compile-time (a runtime-error occurs instead using
assert
). 如果类型无效(即类或结构),则在编译时将不对其进行标识(而是使用
assert
发生运行时错误)。 How would I avoid this, and make this function determine whether a type is valid (a primitive type) or not during compilation? 如何避免这种情况,并使该函数确定类型在编译期间是否有效(原始类型)?
I also dislike the way I am identifying the underlying type in case of enum
s. 我也不喜欢在
enum
的情况下识别基础类型的方式。 I would prefer using std::underlying_type<T>
instead (commented out in the code) but it issues compile-errors if the type is for example a void pointer ( type_traits:1762:38: error: 'void*' is not an enumeration type
) 我宁愿使用
std::underlying_type<T>
代替(在代码中注释掉),但是如果类型是例如void指针( type_traits:1762:38: error: 'void*' is not an enumeration type
)
I tried to achieve this behavior using std::enable_if
but without success... Do tell if I should explain something in case it sounded a bit fuzzy! 我尝试使用
std::enable_if
实现此行为,但没有成功...请告诉我是否应该解释一下,以防听起来有些模糊!
Summary: I want to get the GetFFIType function to determine everything during compilation and the function should only support primitive types (see this for a more extensive reference) 摘要:我想获取GetFFIType函数来确定编译期间的所有内容,并且该函数应仅支持基本类型(请参阅此以获得更广泛的参考)
EDIT: Sorry for the title, nothing better came to mind :( 编辑:对不起,标题,没有更好的主意:(
It's easier and usually better to overload function templates, rather than specialize them. 重载功能模板比专门化它们更容易,通常更好。 I'll add a version of the function with a pointer argument so it can be called without a template parameter list:
我将添加带有指针参数的函数版本,以便可以在没有模板参数列表的情况下调用它:
inline ffi_type * GetFFITypeHelper( void* ) { return &ffi_type_void; }
inline ffi_type * GetFFITypeHelper( byte* ) { return &ffi_type_uchar; }
// ...
Then you can use enable_if
for the more generalized cases you want to cover. 然后,可以将
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) ); }
AFTER all those overloads are declared, the version you wanted is: 在声明所有这些重载之后,所需的版本是:
template<typename T> ffi_type * GetFFIType()
{ return GetFFITypeHelper( static_cast<T*>(nullptr) ); }
Putting the logic inside a class template rather than a function template will allow for partial specializations, which we can also take advantage of for SFINAE tricks: 将逻辑放在类模板而不是函数模板中将允许部分专业化,我们也可以利用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();
}
};
The total specializations are straightforward to write so I won't show them. 总体专业知识很容易编写,因此我将不展示它们。 Although note that you can choose to instead match eg
std::is_integral
and switch on sizeof(T)
if you want, similar to what you did to work around std::underlying_type
. 尽管注意,您可以选择匹配例如
std::is_integral
,然后根据需要打开sizeof(T)
,这与解决std::underlying_type
所做的工作类似。
Finally here are two suggested implementations of the two utilities which are assumed in the above code; 最后,这是上面代码中假定的两个实用程序的两个建议的实现。 obviously you don't need to use them verbatim as long as you write something else along in the same vein.
显然,只要您沿相同的方向写其他内容,就不需要逐字使用它们。
// 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.