2008年12月7日 星期日

C++的列舉型別的type-safe問題 (2)

繼上一篇「C++的列舉型別的type-safe問題」,其實基本上我會找到bitwise這個enum class template是因為我需要enum的bitwise operations,因為C++原本的eums的bitwise operations有一些type-safe上的問題,以及缺少一些bitwsie operations。


enum Style
{
StyleA = 0x01,
StyleB = 0x02,
StyleC = 0x04,
}

enums Area
{
Area1,
Area2,
Area3,
Area4,
}

// pass
Style s1 = StyleA;

// error, can't convert int to enums 'Style'
s1 = StyleA | StyleB;


很神奇的,為什麼StyleA | StyleB的結果無法放在s1裡呢,因為StyleA | StyleB的結果會是一個int而不是enum 'Style'。主要原因應該還是因為C++裡並沒有Enum operator |(Enum s1, Enum s2),而會implicit將enum轉成int,而改呼叫int operator (int s1, int s2),所以回傳值會是個int而不是enums囉。但是這不是我們預期的結果。

下面這樣的強制轉型可以解決上面的問題

// ok
Style s1 = static_cast<Style>(StyleA | Style B);


但是如果你不小心打錯了呢?

// pass, but what is 'StyleA' | 'Area4'??
Style s1 = static_cast<Style>(Style | Area4);

這樣的強制轉型方式缺乏type-safe。

另外如果你需要Enum Enum::operator |=(Enum s2) 這種運算的話,那現有的C++是不支援的喔

// error, no |=, &= operation in enums
s1 |= StyleC;
s1 &= StyleC;


所以很自然的你會改成下面這樣

// error, can't conver int to enums 'Style'
s1 = s1 | StyleC;
s1 = s1 & StyleC;


咦~ 又不對.. XD,因為跟之前的問題一樣,你需要做強制轉型將int轉成enum,所以在做bitwise operations會常常需要強制轉型。


function的參數也是一樣會遇到要強制轉型的問題

void foo(Style style)
{
// ...
}

void main()
{
/ error, can't conver int to enums 'Style'
foo(StyleA | Style B);

// pass, but it's non type-safe
foo(static_cast<Style>(StyleA | StyelB));
}


所以會看到有些lib的function的參數就不使用enum,改成unsigned int這種整數型態。

void foo(unsigned int style)
{
// ...
}

void main()
{
// pass, but it's non type-safe
foo(StyleA | Style B);
}


假設你改用bitwise_enum這個template class的話,除了上一篇講到的功能,當你在bitwise operations時依舊可以做到type-safe。

#include "bitwise_enums.hpp"

void foo(bitwise_enum<Style> style)
{
// ...
}

void main()
{
// pass
bitwise_enum<Style> s1 = StyleA | StyleB;
s1 |= StyleC;
foo(StyleA | StyleB);

// error, your complier will say it can't convert enums 'Area' to 'Style', :)
foo(StyleA | Area1);
}



不過用bitwise_enum有一些缺點,因為bitwise_enum有implict ctor自動將enum轉成bitwise_enum,因此當你要判斷某個bit是否為1時,你不能用簡單的 and 運算,而需要呼叫他的member function。

bitwise_enum<Style> s1 = StyleA | StyleB;
if (s1.has_bits(StyleC)
{
// ...
}


Note:
1. 跟前一篇一樣,如果我們不去擔心type-safe的問題的話,那用強制轉型這樣其實也沒什麼問題的。

Reference:
1. bitwise_enums
2. bitwise_enums ComplierErrors

沒有留言:

LinkWithin

Related Posts with Thumbnails