テンプレートを使用して汎用プログラミングの魔法に参加したので、コンパイル時に適切なサイズの整数型を生成するクロスプラットフォームクラスを実装することにしました。 これに使用する主な手法は、再帰的な型の再定義とテンプレートの部分的な特殊化です。
定義を形式化することから始めます。 標準タイプごとに、適切なオプションの検索アルゴリズムに必要な「次の」タイプを決定する必要があります。 部分的な特殊化を使用して、type_traitsテンプレートを実装します。
template<typename type> struct type_traits; template<> struct type_traits <unsigned char> { typedef unsigned char current_type; typedef unsigned short next_type; }; template<> struct type_traits <unsigned short> { typedef unsigned short current_type; typedef unsigned int next_type; }; template<> struct type_traits <unsigned int> { typedef unsigned int current_type; typedef unsigned long next_type; }; template<> struct type_traits <unsigned long> { typedef unsigned long current_type; typedef unsigned long long next_type; }; template<> struct type_traits <unsigned long long int> { typedef unsigned long long int current_type; }; template<> struct type_traits <signed char> { typedef signed char current_type; typedef short next_type; }; template<> struct type_traits <short> { typedef short current_type; typedef int next_type; }; template<> struct type_traits <int> { typedef int current_type; typedef long next_type; }; template<> struct type_traits <long> { typedef long current_type; typedef long long next_type; }; template<> struct type_traits <long long int> { typedef long long int current_type;};
(符号なし)long long int型の特性は、next_typeが定義されていないことです。これは、それらよりも保証されるものがないからです。
次に、2つのパラメーターを含む選択アルゴリズムのメインテンプレートを決定する必要があります。typeは標準の数値型であり、この型がサイズに適している場合はtrue、そうでない場合はfalseであるbool変数です。 デフォルトの実装では、タイプから「現在のタイプ」-type_traits :: current_typeを取得し、タイプが適合しない場合は、「next」-type_traits :: next_typeを取得します。
// template<typename type, bool> struct type_choice { typedef typename type_traits<type>::current_type std_type; }; template<typename type> struct type_choice<type, false> { typedef typename type_traits<type>::next_type next_type; typedef typename type_choice<next_type, sizeof(next_type) == capacity>::std_type std_type; };
3番目のユーティリティテンプレートは、初期バージョンを選択するように設計されています。charまたはunsigned charのいずれを使用するかによって、2つのバージョンがあります。
// template <bool is_signed> struct base_type_selector { typedef signed char base_type; }; template <> struct base_type_selector<false> { typedef unsigned char base_type; };
最後に、目的のタイプを含むメインクラスを定義する必要があります。 このクラスをfixed_intと呼びます。2つのテンプレートパラメーターがあります。最初のパラメーターはsize_t型であり、バイト単位で必要な容量を示し、2番目のパラメーターはブール型であり、型記号を処理します。 オープンエンティティのクラス自体には、1つの魔法のtypedefのみが含まれます。
typedef typename type_choice< typename base_type_selector<is_signed>::base_type, sizeof(base_type_selector<is_signed>::base_type) == capacity >::std_type type;
メインクラスとサービスクラスは、さまざまな方法で配置できます。 賢明なMVSコンパイラは、ためらうことなくローカルテンプレートクラスをコンパイルします。
template <size_t capacity, bool is_signed> class fixed_int { // template <int x> struct unsupported_capacity { int i[1/(xx)]; }; template <> struct unsupported_capacity<1> {}; template <> struct unsupported_capacity<2> {}; template <> struct unsupported_capacity<4> {}; template <> struct unsupported_capacity<8> {}; // , template<typename type> struct type_traits; template<> struct type_traits <unsigned char> { typedef unsigned char current_type; typedef unsigned short next_type; }; template<> struct type_traits <unsigned short> { typedef unsigned short current_type; typedef unsigned int next_type; }; template<> struct type_traits <unsigned int> { typedef unsigned int current_type; typedef unsigned long next_type; }; template<> struct type_traits <unsigned long> { typedef unsigned long current_type; typedef unsigned long long next_type; }; template<> struct type_traits <unsigned long long int> { typedef unsigned long long int current_type; typedef unsupported_capacity<capacity> next_type; }; template<> struct type_traits <signed char> { typedef signed char current_type; typedef short next_type; }; template<> struct type_traits <short> { typedef short current_type; typedef int next_type; }; template<> struct type_traits <int> { typedef int current_type; typedef long next_type; }; template<> struct type_traits <long> { typedef long current_type; typedef long long next_type; }; template<> struct type_traits <long long int> { typedef long long int current_type; typedef unsupported_capacity<capacity> next_type;}; // template<typename type, bool> struct type_choice { typedef typename type_traits<type>::current_type std_type; }; template<typename type> struct type_choice<type, false> { typedef typename type_traits<type>::next_type next_type; typedef typename type_choice<next_type, sizeof(next_type) == capacity>::std_type std_type; }; // template <bool is_signed> struct base_type_selector { typedef signed char base_type; }; template <> struct base_type_selector<false> { typedef unsigned char base_type; }; public: typedef typename type_choice< typename base_type_selector<is_signed>::base_type, sizeof(base_type_selector<is_signed>::base_type) == capacity >::std_type type; };
たとえば、Qtは、別のテンプレートクラス内のテンプレートクラスの部分的な特殊化について文句を言います。 このような場合、サービス内部テンプレートは、名前空間__privateで個別に取り出して、共通の名前空間を散らかさないようにすることができます。このような場合、このメソッドはLokiライブラリでAlexandrescuを使用します(たとえば、タイプリスト)。
次のように、すべてのタイプに便利な名前を追加します。
typedef fixed_int<1, false>::type uint8; typedef fixed_int<2, false>::type uint16; typedef fixed_int<4, false>::type uint32; typedef fixed_int<8, false>::type uint64; typedef fixed_int<1, true>::type int8; typedef fixed_int<2, true>::type int16; typedef fixed_int<4, true>::type int32; typedef fixed_int<8, true>::type int64;
...そしてそのすべての結果を確認します(MVS2015 / intel 0x86で実行):
... int32 x1; uint64 x2; fixed_int<2, true>::type x3; std::wcout<<typeid(x1).name()<<std::endl; std::wcout<<typeid(x2).name()<<std::endl; std::wcout<<typeid(x3).name()<<std::endl; ...
結果:
int unsigned __int64 short
合計で、サードパーティの情報を使用して決定しないクロスプラットフォームの固定型を取得しました。 ボードとして、追加の計算がコンパイル段階で実行されます。 テンプレートパラメータが正しくないか、プラットフォームでこのディメンションをサポートできない場合、コンパイルエラーが発生しますが、これもプラスになります。
PS:テンプレートのインスタンス化エラーの説明には多少の複雑さがあるため、議論の余地のないトリックを使用しました:部分的な特殊化のみをコンパイルするテンプレートヘルパークラスを定義する:
// template <int x> struct unsupported_capacity { int i[1/(xx)]; }; template <> struct unsupported_capacity<1> {}; template <> struct unsupported_capacity<2> {}; template <> struct unsupported_capacity<4> {}; template <> struct unsupported_capacity<8> {};
主に標準のエラーの記述が定義されていないため議論の余地がなく、そのようなクラスの有用性は保証されていません。 Microsoftコンパイラは、このタイプのfixed_int <3、true> ::タイプをインスタンス化しようとすると、エラーを返します。
exp4.cpp(127): error C2057: expected constant expression exp4.cpp(156) : see reference to class template instantiation 'fixed_int<3,true>::unsupported_capacity<3>' being compiled ...
PS:char型をsigned charに、(unsigned)long longを(unsigned)long long intに置き換えました