2009-06-15 5 views
8

さまざまな整数型、異なる幅、可能な符号付き/符号なしの不一致間のキャストで整数オーバーフローに関するランタイム例外をスローするC++テンプレート関数を記述しようとしています。これらの目的のために、私は浮動小数点型から整数型への変換、その他のオブジェクト間変換を考慮しません。私は特別なケースコードをたくさん書かなくてもこれをしたいと思います。これは私が現在持っているものです:安全な整数型キャスト用のC++テンプレート

template< typename T, typename R > void safe_cast(const T& source, R& result) 
{ 
    // get the maximum safe value of type R 
    R rMax = (R) ~0; 
    if (rMax < 0) // R is a signed type 
    { 
     // assume that we're on an 8-bit twos-compliment machine 
     rMax = ~(0x80 << ((sizeof(R) - 1) * 8)); 
    } 

    if ((source & rMax ) != source) 
    { 
     throw new IntegerOverflowException(source); 
    } 

    result = static_cast<R>(source); 
} 

これは、効率的ですか?

編集:さまざまな理由でstlが利用できないので、std :: numeric_limitsを使用することはできません.Boostのものはすぐに使用できます。

+0

しかし、必要なコードをnumeric_limitsから自分のテンプレートヘルパーにコピーすることができます。すべてをuint64(または最大許容サイズが何であれ)に割り当て、そのタイプ内の比較を行います。 –

+1

これはうまくいくかもしれませんが、実際にはこのようなコードをコピーする際にライセンス条項を認識する必要があります。潜在的に違反することに加えて、GPLの場合のように、誤ってコードに「感染する」可能性があります。この種の作業を行う前に、両方のライセンスが互換性があることを確認してください。通常の "私は弁護士ではありません"という免責事項が適用されます。 – Void

+0

STLを使用できないさまざまな理由は何ですか? – GManNickG

答えて

5

SafeIntを試しましたか?さまざまな整数型の整数オーバーフローチェックを行うクロスプラットフォームテンプレートです。それは私がRが署名された場合には、あなたが最後のビットを除いて、すべて1とRMAXを埋めるためにしようとしていると仮定して修正アム

12

std::numeric_limitsテンプレートを使用すると、より基本的なタイプの最小値と最大値(さらにはその他の情報)をもっとエレガントに得ることができます。 std::numeric_limits<T>::max()<limits>を含める必要があります。

参考:http://www.cplusplus.com/reference/std/limits/numeric_limits/

+0

この編集は7年後に私のアップヴォートをダウンボートに変更しました。提示された例は@ jpo38で追加されているため動作しません。ペアの例:From = int、To = unsigned。ソース== - 1。 –

+0

実際、私はそれを見て、私はこの記事を編集して以来、私のコードで修正しました。しかし、編集を更新するのを忘れました...申し訳ありません。私は今、 'static_cast (static_cast (source))!= source'の場合にスローします(基本的に、キャストによっていくつかの情報が失われた場合...これは素晴らしいことです)。 – jpo38

11

オプションを高めますか?その場合は、boost::numeric_cast<>を試してください。あなたが探している特性を提供するようです。

1

をCodePlexの上で利用できるの?その場合は、0x10(0001 0000)の代わりに0x80(1000 0000)を指定する必要があります。

あなたの関数がソースに対して負の数をサポートしているようにも見えません。

編集:

template< typename T, typename R > 
void safe_cast(const T& source, R& result) 
{ 
    // get the maximum safe value of type R 
    R rMax = (R) ~0; 
    if (rMax < 0) // R is a signed type 
    { 
     // assume that we're on an 8-bit twos-compliment machine 
    rMax = (0x80 << ((sizeof(R) - 1) * 8)); 
    if(source >= 0) 
     rMax = ~rMax; 
    } 

    if ((source >= 0 && (source & rMax ) != source) || (source < 0 && (source & rMax) != rMax)) 
    { 
     throw new IntegerOverflowException(source); 
    } 

    result = static_cast<R>(source); 
} 

編集:固定エラーここ

は、私が文字にint型から変換するためにテストした、わずかに編集されたバージョンです。

7

2の補数を使用するかどうかにかかわらず、これらの作業は今や考えられます。あなたがそれを使用する前に広範囲にテストしてください。彼らは次の結果を出します。各行は、1つのアサーションの失敗を与える

/* unsigned -> signed, overflow */ 
safe_cast<short>(UINT_MAX); 

/* unsigned -> unsigned, overflow */ 
safe_cast<unsigned char>(ULONG_MAX); 

/* signed -> unsigned, overflow */ 
safe_cast<unsigned long>(-1); 

/* signed -> signed, overflow */ 
safe_cast<signed char>(INT_MAX); 

/* always works (no check done) */ 
safe_cast<long>(INT_MAX); 

// giving these assertion failures results 
(type)f <= (type)is_signed<To>::v_max 
f <= (To)-1 
f >= 0 
f >= is_signed<To>::v_min && f <= is_signed<To>::v_max 

実装(ちょうどあなたしてくださいとして例外にそれらを変更します)。最初に、整数ランクをチェックするいくつかのユーティリティ(ランクの高い型は、同じ符号を与えられた、より低いランクの型の値を含むことができます)、いくつかのプロモーションツールは、共通の安全な型を見つけることができます符号なしの型が関与している場合はsigned型は、符号なしの1のすべての値を格納することができません場合。

/* ranks */ 
template<typename> struct int_rank; 
#define RANK(T, I) template<> struct int_rank<T> \ 
    { static int const value = I; } 

RANK(char, 1); RANK(unsigned char, 1); RANK(signed char, 1); 
RANK(short, 2); RANK(unsigned short, 2); 
RANK(int, 3); RANK(unsigned int, 3); 
RANK(long, 4); RANK(unsigned long, 4); 
#undef RANK 

/* usual arith. conversions for ints (pre-condition: A, B differ) */ 
template<int> struct uac_at; 
template<> struct uac_at<1> { typedef int type; }; 
template<> struct uac_at<2> { typedef unsigned int type; }; 
template<> struct uac_at<3> { typedef long type; }; 
template<> struct uac_at<4> { typedef unsigned long type; }; 

template<typename A, typename B> 
struct uac_type { 
    static char (&f(int))[1]; 
    static char (&f(unsigned int))[2]; 
    static char (&f(long))[3]; 
    static char (&f(unsigned long))[4]; 
    typedef typename uac_at<sizeof f(0 ? A() : B())>::type type; 
}; 

/* signed games */ 
template<typename> struct is_signed { static bool const value = false; }; 
#define SG(X, TT) template<> struct is_signed<X> { \ 
    static bool const value = true;    \ 
    static X const v_min = TT##_MIN;    \ 
    static X const v_max = TT##_MAX;    \ 
} 

SG(signed char, SCHAR); 
SG(short, SHRT); 
SG(int, INT); 
SG(long, LONG); 
#undef SG 

template<> struct is_signed<char> { 
    static bool const value = (CHAR_MIN < 0); 
    static char const v_min = CHAR_MIN; // just in case it's signed... 
    static char const v_max = CHAR_MAX; 
}; 

変換テンプレートは、それぞれに把握するために、それらを利用して)、符号付きの型をもたらします何が行われる必要があるか、行われない場合があります。

template<typename To, typename From, 
     bool to_signed = is_signed<To>::value, 
     bool from_signed = is_signed<From>::value, 
     bool rank_fine = (int_rank<To>::value >= int_rank<From>::value)> 
struct do_conv; 

/* these conversions never overflow, like int -> int, 
* or int -> long. */ 
template<typename To, typename From, bool Sign> 
struct do_conv<To, From, Sign, Sign, true> { 
    static To call(From f) { 
     return (To)f; 
    } 
}; 

template<typename To, typename From> 
struct do_conv<To, From, false, false, false> { 
    static To call(From f) { 
     assert(f <= (To)-1); 
     return (To)f; 
    } 
}; 

template<typename To, typename From> 
struct do_conv<To, From, false, true, true> { 
    typedef typename uac_type<To, From>::type type; 
    static To call(From f) { 
     /* no need to check whether To's positive range will 
     * store From's positive range: Because the rank is 
     * fine, and To is unsigned. 
     * Fixes GCC warning "comparison is always true" */ 
     assert(f >= 0); 
     return (To)f; 
    } 
}; 

template<typename To, typename From> 
struct do_conv<To, From, false, true, false> { 
    typedef typename uac_type<To, From>::type type; 
    static To call(From f) { 
     assert(f >= 0 && (type)f <= (type)(To)-1); 
     return (To)f; 
    } 
}; 

template<typename To, typename From, bool Rank> 
struct do_conv<To, From, true, false, Rank> { 
    typedef typename uac_type<To, From>::type type; 
    static To call(From f) { 
     assert((type)f <= (type)is_signed<To>::v_max); 
     return (To)f; 
    } 
}; 

template<typename To, typename From> 
struct do_conv<To, From, true, true, false> { 
    static To call(From f) { 
     assert(f >= is_signed<To>::v_min && f <= is_signed<To>::v_max); 
     return (To)f; 
    } 
}; 

template<typename To, typename From> 
To safe_cast(From f) { return do_conv<To, From>::call(f); } 
3

について方法:キャストが働いていた場合次に

template< typename T, typename R > void safe_cast(const T& source, R& result) 
{ 
    R temp = static_cast<R>(source); 
    if (static_cast<T> (temp) != source 
     || (temp < 0 && source > 0) 
     || (temp > 0 && source < 0)) 
    { 
     throw IntegerOverflowException(source); 
    } 
    result = temp; 
} 

あなただけチェックしています。あなたが始まったことを元に戻してください、そしてその兆候が反転していないことを確認してください。

EDIT: 以下のコメントがめちゃくちゃしまったので、ここではそれがフォーマットされ、次のとおりです。

int myint (-1); 
safe_cast(myint, mychar); 
safe_cast(mychar, myuchar); // Exception is thrown here 
safe_cast(myuchar, myint); 

のchar型に正常に動作しますint型からキャスト。 charからunsigned charへのキャストは例外をスローします。私はここに問題がないと思う。

+0

o、コンパイラは、2つの型のうちの1つが符号なしであり、もう一方がそうでないときに警告を鳴らします(g ++のデータ型の範囲が限られているため、比較は常にfalseです)。 0と比較してもスローされず、データは失われます: -1(int)から-1(char)から0xFF(unsigned char)までのキャストをintに戻すと、-1は得られません。 –

+0

ああ、私は別のコンパイラでテストしました 警告は、tempがunsignedのときは "temp <0"を参照しています。これは正常です。 この時点では投げられず、データは失われません。 あなたの提案をテストしました。つまり、 int myint(-1); safe_cast(myint、mychar); safe_cast(mychar、myuchar); //ここで例外がスローされます safe_cast(myuchar、myint); intからcharへのキャストはうまく動作します。 charからunsigned charへのキャストは例外をスローします。 ここに問題はありません。 – Tim

0

私は何かが欠けする必要がありますが、これはあなたが私がsweet.hppの単一のヘッダがconv.hppと呼ばれてい?:

// using a more cast-like prototype, if I may: 
template<class to, class from> inline 
to safe_cast(from f) 
{ 
    to t = static_cast<to>(f); 
    if (t != f) throw whatever; // no new! 
    return t; 
} 
+0

いいえ、-1(int)から-1(char)〜0xFF(unsigned char)までのキャストをintに戻しても、-1は得られません。キャストは値が途中で変更されると「安全」ではありません。 –

+0

Hi Dribeas、 申し訳ありませんが、私はあなたが何を言っているのか分かりません。 。 safe_cast (int(-1))はオーバーフローせず、細かい を返します。 safe_cast (char(-1))は符号(および値)を変更してスローします。 したがって、正しい動作。または、何を言っているのですか? – sly

+0

charが署名されていると仮定すると、safe_cast (char(-1))はtをUCHAR_MAX(おそらく255)に設定します。 (t!= f)がcharをintに昇格し、-1に符号なしcharをintに昇格し255を生成すると、それらは等しくない。 safe_cast (-1)を実行すると、tはUINT_MAXに設定され、次にifは何も宣言せず、intをunsigned int(UAC)に変換し、UINT_MAXを再度生成し、キャストが成功したと誤って考える。 –

0

を望むものではありません。すべての整数型の境界をテストし、整数型の文字列キャストを行うこともできます。

short a = to<short>(1337); 
std::string b = to<std::string>(a); 
long c = to<long>(b); 
0

http://rrsd.com/blincubator.com/bi_library/safe-numerics

で安全ニューメリックスを考慮し、このライブラリは、すべてのCのプリミティブ整数型のためのドロップイン代替品を提供します。キャスティングを含む誤った結果をもたらすC操作は、検出されるとトラップされます。

関連する問題