I'm working with libffi and I've made a class with a similar template to std::function
(i.e 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:
// 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:
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 (i.e a class or a structure) it is not identified at compile-time (a runtime-error occurs instead using 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. 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
)
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!
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)
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:
Then you can use
enable_if
for the more generalized cases you want to cover.AFTER all those overloads are declared, the version you wanted is:
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:
The total specializations are straightforward to write so I won't show them. Although note that you can choose to instead match e.g.
std::is_integral
and switch onsizeof(T)
if you want, similar to what you did to work aroundstd::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.