2

私はlibffiを使用していて、std::function(つまり、リターンタイプ( )と各引数タイプ()を対応するlibffiタイプclass Func<Ret (Args...)> { /* ... */};に変換したい)と同様のテンプレートを使用してクラスを作成しました(これまでのところ、これを参照してください)。私はこれを思いついた:RetArgs

// 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)。これを回避し、コンパイル中に型が有効(プリミティブ型)であるかどうかをこの関数に判断させるにはどうすればよいですか?

また、sの場合に基になるタイプを識別する方法も嫌いですenumstd::underlying_type<T>代わりに(コードでコメントアウトされている)使用したいのですが、タイプがたとえばvoidポインター( type_traits:1762:38: error: ‘void*’ is not an enumeration type)の場合、コンパイルエラーが発生します。

私はこの動作を使用して達成しようとしましstd::enable_ifたが、成功しませんでした...少しあいまいに聞こえた場合に備えて、何かを説明する必要があるかどうか教えてください!

概要:GetFFIType関数を取得して、コンパイル中にすべてを判別したいのですが、関数はプリミティブ型のみをサポートする必要があります(より詳細なリファレンスについては、これを参照してください)。

編集:タイトルについて申し訳ありませんが、これ以上のことは思い浮かびませんでした:(

4

2 に答える 2

4

関数テンプレートを特殊化するよりも、オーバーロードする方が簡単で、通常は優れています。テンプレートパラメータリストなしで呼び出すことができるように、ポインタ引数を持つ関数のバージョンを追加します。

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) ); }
于 2012-07-09T16:28:27.880 に答える
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

最後に、上記のコードで想定されている2つのユーティリティの2つの推奨実装を示します。明らかに、同じ流れで何か他のものを書く限り、それらを逐語的に使用する必要はありません。

// 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
于 2012-07-09T17:25:43.497 に答える