TEST

2010年11月16日 星期二

exception使用的時機

一直以來對於 exception 使用的時機不是很清楚,所以從來沒用過,想說反正也有 google約爾等有名大師都說不要用 exception ,所以一直以來都是使用 error code return (或紀錄 error code) 的方式。 但也有人說 exception 不是惡魔,且沒了 exception 就沒有 RAII 這方便的作法。但看了半天 RAII 是跟 exception 綁在一起的嗎?沒有 exception 一樣可以做 RAII ,是沒有 exception 就沒有做 RAII 的理由吧? exception 可以說是一種 goto ,對於程式流程跟可讀性來說,會造成人腦在run程式的複雜度。我也覺得非必要時候不用 exception 。那何時是非必要時?何時又是使用 exception 的時機呢? 這時就必須了解c+++中常用的處理錯誤方法:
  • assert
  • error code
  • exception
在此不闡述,就我自己的理解思考,使用 exception 時機有
  1. 用到 STL 或有拋出 exception 的程式,因你無法保證 100%不會有 exception。
  2. 簡化程式流程的時候,這在預設執行的各 method/function 都是 99.9%正確時可簡化,遇到不能考慮的錯誤時就是那 0.01%。
  3. 子程式不知怎麼處理此錯誤,可能父程式或父父程式以上(最高為使用者)知道如何處理時。
  4. 想回傳error code卻不行時。
避免濫用
  1. 不要在同一 scope throw, catch,這很明顯可以用 if else/switch 來解決,但是很多書上都這樣教。
  2. 父程式可考慮解決的錯誤可以使用 error code。
有時 exception 使用上沒有 error code 來的簡潔,比如說以下這段,你只是要秀錯誤訊息那用 exception 很方便但是要針對錯誤來處理呢?
while (IsSending)
{
 try
    {
        foo->Send(SendData);
        IsSending=false;
        foo->Save(SendData);
    }
    catch(Exception &e)
    {
        ErrorMsg = e.ClassName() + ":" + e.Message;
    }
}
還是使用 error code 的方式吧,我無法想像用exception來處理:
while (IsSending)
{
    ErrorCode error_code;
    error_code=foo->Send(SendData);
    switch(error_code)
    {
    case SEND_OK:
        IsSending=false;
        error_code=foo->Save(SendData);
        switch(error_code)
        {
        case SAVE_OK:
            break;
        case SAVE_NO_FILE:
            //Create a new file
            break;
        case SAVE_BE_LOCK:
            //UnLock or throw if can't be UnLock here
            break;
        }
        break;
    case SEND_NOT_FINISH:
        //Get left SendData  (then Send)
        break;
    case SEND_EMPTY:
        //WaitData or GetData (then Send )
        break;
    case SEND_BE_LOCK:
        //UnLock(then Send) or throw if can't be UnLock here
        break;
    }
}
好吧寫個參考,用exception來處理:
while (IsSending)
{
    try
    {
        foo->Send(SendData);
        IsSending=false;
    }
    catch(Exception &e)
    {
        ErrorCode error_code = e.Code;
        switch(error_code)
        {
        case SEND_NOT_FINISH:
            //Get left SendData  (then Send)
            break;
        case SEND_EMPTY:
            //WaitData or GetData (then Send )
            break;
        case SEND_BE_LOCK:
            //UnLock(then Send) or throw if can't be UnLock here
            break;
        }
    }
    
    try
    {
        foo->Save(SendData);
    }
    catch(Exception &e)
    {
        ErrorCode error_code = e.Code;
        switch(error_code)
        {
        case SAVE_NO_FILE:
            //Create a new file
            break;
        case SAVE_BE_LOCK:
            //UnLock or throw if can't be UnLock here
            break;
        }
    }
}
嗯有比較簡潔嗎? 或許可以改寫成以下方式:
while (IsSending)
{
    try
    {
        foo->Send(SendData);
        IsSending=false;
        foo->Save(SendData);
    }
    catch(Exception &e)
    {
        ErrorCode error_code = e.Code;
        switch(error_code)
        {
        case SEND_NOT_FINISH:
            //Get left SendData  (then Send)
            break;
        case SEND_EMPTY:
            //WaitData or GetData (then Send )
            break;
        case SEND_BE_LOCK:
            //UnLock(then Send) or throw if can't be UnLock here
            break;
            
        case SAVE_NO_FILE:
            IsSending=false;
            //Create a new file
            break;
        case SAVE_BE_LOCK:
            IsSending=false;
            //UnLock or throw if can't be UnLock here
            break;
        }
    }
}
雖然在程式上看來較簡潔,但是對於程式流程卻較複雜,隨著程式碼越多,跳到哪裡都不知道了... 其他不使用ex的原因還有影響效能,團隊編程顧慮等。 但使用error code (status code) 也有以下缺點:
  1. error code是可以被忽略的。
  2. 無法自動向上上層(父父程式)傳播。必須由上層接力在丟一次。
  3. 在程式流程中不能集中處理(某些情況是優點)。
  4. 必須有回傳值或紀錄值。
反過來說你不想要這些缺點也是使用 exception 的時機。

Next: c++ checked exceptions (Exception specifications)?
根據[C++ coding Standards] 書中第 75 項 Avoid exception specifications , 且在 A Pragmatic Look at Exception Specifications 中的建議 "Never write an exception specification." 所以就不研究了,進階可看 Questions About Exception Specifications

[Reference]

2010年6月17日 星期四

ASSERT的使用時機

ASSERT全大寫表示它是全域的。它是一種假設,當假設不成立時,程式就得全部中止。且只作用在Debug時,為的就是讓你在Debug時可以更快地寫程式、找出錯誤。Release時會不會假設就不成立了?有可能,但通常寫程式Debug時就會出現ASSERT出錯的情況,在此時就會快速的發現錯誤,如果Relese版本才出錯,就是考慮的不夠嚴謹,有些ASSERT沒有設定,或是某種情況Relese版才出現。此時更需要Unit Test配合。

寫程式需不需要到處都先假設?

被呼叫端的函式比如fun(obj * o);就必須成立一假設ASSERT(NULL!=o);
為何不return error code或throw exception,要考慮的是上層有沒有檢查就呼叫。通常是會檢查的,就沒有返回錯誤碼或丟例外的必要,但並不能保證。

如果上層要檢查那上上層要不要檢查?在每一層都必須檢查是無謂的。

通常一個軟體設計會分成三個模組,也就是所謂的MVC,是指模組而言。在程式邏輯可分成Controller、UI、Library三層,是有點類似的。Controller呼叫UI和Library,而UI傳送使用者event給Controller。在大型的軟體,可能Controller上還會有一個總體的Controller,Library可能還會呼叫別的Library。
實際的程式可能是C1->C2->L1->L2->L3,UI可能由C1整合(C1<->UI),

設有一程式輸入名子就可以知道工作時數來取得Payment(薪水、股票)
UI(input)<->C1->C2->
fun1(string name)->fun2(int work_time)->fun3(Payment * pay);

fun1就必須有ASSERT(!name.IsEmpty());
fun2就必須有ASSERT(work_time>0);
fun3就必須有ASSERT(NULL!=pay);

在此情況*下L1,L2,L3都必須ASSERT,而C1或C2就必須檢查避免傳遞無效名子。Controller會去使用UI跟Library,被呼叫端UI跟Library就必須要假設是被正常使用。

但Controller不能有ASSERT,因為它不能有任何的假設情況,必須把會發生的都考慮進去,遇到不正常的時候就阻擋。而被呼叫端都必須要確認情況是成立的才能繼續執行。煩惱就交給上層,下層就只要假設就好了。

C++ 編程規範70提到error有三種:

  • Precondition(前條件)
  • Postcondition(後條件)
  • Invariant(不變性)

書中講得很清楚,在此不重述。只有不變性比較抽象,不變性就是滿足一個有效狀態(valid states)物件的條件。比如說一個有帳號、密碼的物件,帳號和密碼必須是成對且正確對應的,其中一個有錯就是error、無效狀態。這很明顯不該使用ASSERT來保證*。

書中說到Postcondition也可用ASSERT來確認:

"假設你呼叫某個API函式,其文件說它總是返回正值,但你懷疑該函式有臭蟲,那麼你可以在呼叫該函式後使用assert驗證其結果。"

這我認為是多餘的,不是很建議。當此API函式出錯有兩種解法:

  • API可以修改:修改API錯誤,但不管Debug還是Release時還是要再確認。
  • API不可修改:此時也不能用ASSERT中止,且Release版本也不希望出現這個錯誤,當然是由上層想辦法檢查繞過來解決。

不管Debug還是Release時,你還是得再一次確認結果是你所預期的,此時可搭配Unit Test來確認。要不然就是上層自行檢查來避開,沒有必要用ASSERT。Postcondition真的有需要ASSERT時,我覺得只有在驗算的時候,比如設計類似Excel的軟體,寫了兩個演算法來計算同樣的數學式,一個是用來驗算,而且只有Debug版本會算兩次比較慢,不相等的話就一定是其中一個有錯。

總結ASSERT的使用時機:

  • Precondition
  • 當你寫的程式是被使用的UI或Library,請多多利用ASSERT來防範吧。

*這只是假命題,fun1也可以接受空名子再處理,fun2可以用unsign int限制,fun3也可以傳reference防null物件,所以實際上用到ASSERT的情況不多。
*不要用ASSERT報告執行期錯誤[C++ 編程規範68]。

2010年5月23日 星期日

安裝設定Wine執行Zoundry Raven

請先參考 [教學] 圖解 wine 入門 來安裝wine、cabextract,不用裝ies4linux,以winetricks裝ie6、msxml3、mfc40、mfc42、wininet。
打開Configure Wine,還需要用到Windows的shlwapi.dll,在libraries頁中新增輸入shlwapi,之後將c:\Windows\system32目錄下的shlwapi.dll複製到~/.wine/drive_c/Windows/system32下,可以覆蓋。

請勿安裝1.1.42版本,在編輯區會沒有滑鼠游標,害我重裝了一整天也沒用......><",後來才知道是bug
後來裝了1.2-rc1版,又遇到一個bug,某些地方字型無法平滑化,目前無解,尚可執行就勉強用了。請看最後有解決了。

之後以下例的方式新增字型:
將/usr/share/fonts/中要新增字型檔案連結到~/.wine/drive_c/windows/Fonts
修改./wine/system.reg 全部替換成自己要的字型。

[Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes]
"Arial CE,238"="PMingLiU"
"Arial CYR,204"="PMingLiU"
"Arial Greek,161"="PMingLiU"
"Arial TUR,162"="PMingLiU"
"Courier New CE,238"="PMingLiU"
"Courier New CYR,204"="PMingLiU"
"Courier New Greek,161"="PMingLiU"
"Courier New TUR,162"="PMingLiU"
.....

修改視窗亂碼:
修改./wine/system.reg

[Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink]
"Lucida Sans Unicode"="DroidSansFallback.ttf,Droid Sans Fallback"
"Microsoft Sans Serif"="DroidSansFallback.ttf,Droid Sans Fallback"
"PMingLiU"="DroidSansFallback.ttf,Droid Sans Fallback"
"SimSun"="DroidSansFallback.ttf,Droid Sans Fallback"
"Tahoma"="DroidSansFallback.ttf,Droid Sans Fallback"

字型平滑化:
修改./wine/user.reg

[Control Panel\\Desktop]
"DragFullWindows"="0"
"FontSmoothing"="2"
"FontSmoothingGamma"=dword:00000578
"FontSmoothingOrientation"=dword:00000001
"FontSmoothingType"=dword:00000002

[Software\\Wine\\X11 Driver]
"ClientSideAntiAliasWithCore"="Y"
"ClientSideAntiAliasWithRender"="Y"
"ClientSideWithCore"="Y"
"ClientSideWithRender"="Y"
"Managed"="Y"

執行畫面:

另外可考慮安裝Wine-doors來使用其他程式。

1.2-rc1版某些地方字型無法平滑化,最後跟據回報bug頁的解法是說不是wine的問題,可能是我裝在9.04上的關係吧。
新增一檔案~/font.conf,輸入以下內容就可以了。

<?xml version="1.0"?><!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<match target="font" >
<edit mode="assign" name="rgba" >
<const>rgb</const>
</edit>
</match>
<match target="font" >
<edit mode="assign" name="hinting" >
<bool>true</bool>
</edit>
</match>
<match target="font" >
<edit mode="assign" name="hintstyle" >
<const>hintmedium</const>
</edit>
</match>
<match target="font" >
<edit mode="assign" name="antialias" >
<bool>true</bool>
</edit>
</match>
</fontconfig>

2010年5月5日 星期三

自訂Beagle搜尋rar、big5編碼txt

Beagle是很好用的桌面搜尋,預設是只搜尋使用者家目錄,且不會找隱藏資料夾裡的檔案。有些文件如.doc裡面的中文是搜尋不到的,英文可以。但.odt、.pdf中英文都可以。對我來說,主要還有以下缺點:
  • 但是如果是rar壓縮檔裡面的資料就找不到。
  • 搜尋中文時,以前是big5編碼的純文字內容就會找不到,我有不少以前的資料是用big5存的阿。
分享一下解決方法:
安裝完Beagle後,到/etc/beagle新增external-filters.xml加入資料如下:
<?xml version="1.0" encoding="utf-8"?>
<external-filters>
<filter>
  <mimetype>application/x-rar</mimetype>
  <extension>.rar</extension>
  <command>rar</command>
  <arguments>lb %s</arguments>
</filter>

<filter>
  <mimetype>text/plain</mimetype>
  <!--mimetype>application/gedit</mimetype-->
  <extension>.txt</extension>
  <command>iconv</command>
  <arguments>-f big5 -t utf-8 %s</arguments>
</filter>
</external-filters>
然後到console

beagle-config --beagled-reload-config

就可以了。
但是之前已經index過得檔案就不會重新index喔,所以當然還是找不到,把舊index刪除 ,重新index就可以了。

如果你不確定增加external-filters有沒有成功,可以試試以下指令來確認:
beagle-info --list-filters
會列出所有支援的filters,在這個例子應該出現;
FilterExternal - Version 10 (/usr/lib/beagle/Filters/Filters.dll)
  - MimeType: application/x-rar
  - Extension: .rar
  - MimeType: text/plain
  - Extension: .txt
要確認是不是指令下錯,可以使用:
beagle-extract-content --show-generated test.rar
beagle-extract-content --show-generated test.txt
來測試你要尋找有包含資料的檔案,如果他的輸出內容有包含你要搜尋的字串就對了。
.doc裡面的中文搜尋不到,目前不急用就先不解了。
參考:
http://www.linux.com/news/software/applications/254610:desktop-search-engines-compared
http://beagle-project.org/ExternalFiltersRepository#Rar_filter
http://osdir.com/ml/dashboard-hackers/2010-03/msg00006.html

2010年2月3日 星期三

在windows單機上使用rsync備份

首先至cwRsync下載安裝檔cwRsync_4.0.3_Installer.zip,只要單機用不需要安裝server版。
安裝完之後,並沒有將rsync所需環境參數設定進系統,而是寫在範例檔C:\Program Files\cwRsync\cwrsync.cmd。把指令寫在範例檔最後,直接執行即可。
rsync -av --delete /cygdrive/c/Temp/test/ /cygdrive/c/Temp/test2/ >>test
rsync -av --delete-after --exclude-from=rsync_exclude.txt /cygdrive/d/ /cygdrive/z/company/ >rsync3.log

rsync_exclude.txt內容範例
#System file
RECYCLER/
System Volume Information/
Thumbs.db

#no use data
not sync/
*.obj
*.pch
*.ncb
*.bsc
*.ilk
*.pdb