顯示具有 Programing 標籤的文章。 顯示所有文章
顯示具有 Programing 標籤的文章。 顯示所有文章

2010年9月8日 星期三

Callback Function

什麼是Callback function?  
如果在程式中使用某個function, 我們可稱作”call” function,這種行為是主動的,  callback function是被動的, 也就是當特定事件發生時(例如觸控發生, 或某手勢被偵測),callback function才會被呼叫.

CallbackC/C++實現的方式, 就是利用function pointer(函式指標), 例如A程式想要B程式發生某些事件時, 能通知A程式. 利用Callback機制的話, A程式必須傳一個function pointer給B程式,讓B程式可以使用這個function pointer來通知A程式.


function pointer(函式指標)宣告的方式如下:
返回類型 (* 函式指標名稱) (參數列表);

example:
int (* GetPointCallback)(int x, int y);

Callback function的使用案例
例如: 我們在設計一個觸控繪圖的應用程式時, 通常不會讓程式不斷地向系統要觸控點的資料, 而是希望當系統偵測到觸控發生時, 才會丟回觸控點的資料, 而這個丟回觸控點的行為, 就可以設計成callback function. 其示意圖如下所示:

[圖 1] Touch Paint Callback function

Callback function 範例程式
TouchAPI.h
#ifdef  TOUCHAPI_EXPORTS
 #define TOUCH_API __declspec(dllexport)
#else
 #define TOUCH_API __declspec(dllimport)
#endif

// 定義觸控點的相關資訊
struct TouchPoint
{
 int X; 
 int Y; 
};

// 定義callback function的prototype
// CB_TOUCH_FUNC是一個pointer,指向具有一個TouchPoint類型,且回傳void型的函数
// 前面有個typedef,所以現在CB_TOUCH_FUNC是這種類型的别名
typedef void (_stdcall *CB_TOUCH_FUNC) (TouchPoint);

// 定義了一個RegisterCBTouchFunc的函式,可傳入一個具有CB_TOUCH_FUNC型別的callback function
TOUCH_API void RegisterCBTouchFunc(CB_TOUCH_FUNC callback);

TouchAPI.cpp
#include "stdafx.h"
#include "TouchAPI.h"

void DetectTouchFromSystem(CB_TOUCH_FUNC callback)
{
    // 當系統取得touch資料時,
    // 就會呼叫一次pCB_TOUCH_FUNC參數所指到function一次
    TouchPoint tp;
    tp.X = 5;
    tp.Y = 10;
    callback(tp);
}

void RegisterCallbackTouchFunc(CB_TOUCH_FUNC callback)
{
 DetectTouchFromSystem(callback);
}
TouchPaint.cpp
#include "stdafx.h"
#include "conio.h"
#include "TouchAPI.h"
 
// 定義了一個名為GetTouch的函式, 也就是callback函式
// 它的prototype必須與TouchAPI的CB_TOUCH_FUNC宣告一致
void _stdcall GetTouch(TouchPoint point)
{
 printf("x=%d, y=%d", point.X, point.Y);
}
 
int main(void)
{
    //註冊一個callback function
    RegisterCBTouchFunc(GetTouch);
    getch();
    return 0 ;
};

參考資料:
何謂callback function
Callback (computer science)

2009年7月10日 星期五

程式語言的演變

這個世界已經變成瞬息萬變的時代, 尤其在軟體世界,
要學習的東西真的很多. 當你開始學習某種語言, 幾年後已經熟練它,可以出師時,
偏偏它已經開始沒落,快被另一種語言取代. 這時你又必須再學習另一套語言.

還記得剛開始寫程式時,是從最基本的C開始學起, 後來物件導向(OOP)的觀念開始流行時,
就要學習C++和OOP, 為了要優化OOP和結構化程式碼, 你也得必須了解Design Pattern(軟體設計模式)和UML(統一塑模語言),
前幾年微軟開始推展.NET Framework後, 還得學習CLR(共通語言執行平台)和C#

而在GUI(Graphical User Interface,圖形使用者介面)上的改變,
從早期Windows API(
視窗作業系統應用程式介面),
MFC(Microsoft Foundation Classes;以C++類的形式封裝了Windows的API),

再來就是.NET的Windows Form,
近年來還出現XAML的WPF(Windows Presentation Foundation;.NET 3.0內含的一項機制,內建的支援技術整合2D,3D繪圖, 影片, 語音, 數位文件等)

而這些還只是開發Windows application時要學習的東西,
只能說現在的程式設計人員要學的東西真的很多,
當然; 我覺得基本功是最重要的(也就是基本的C語言+物件導向),

只要馬步紮穩, 要學習任何東西應該都很快.

2009年5月14日 星期四

Debug 和 Release的不同

有時候我們執行程式時會遇到Debug版可以跑,但是Release版本反而不能跑了,為什麼呢?
首先, 先了解DebugRelease的主要差異:

Debug版:包含Microsoft格式的除錯資訊,沒有進行最佳化,檔案體積較大
Release版:不包含除錯資訊,有針對執行速度最佳化,檔案體積較小

事實上Debug Release 並沒有本質的界限,他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動.

Debug各參數的定義:
Release各參數的定義:


那些情況下Release版的程式會出錯?
  • Runtime Library差異(MDd和MD)
  Runtime Library 包含了除錯用的訊息,並採用了一些保護機制以幫助發現錯誤,所以性能上較不如Release的版本。編譯器提供的 Runtime Library通常很穩定不會造成 Release 版本錯誤;倒是由於 Debug Runtime Library 加強了對錯誤的檢測,如Heap的記憶體分配,有時會出現Debug 有錯但 Release 正常的現象。這裡要注意的是,如果 Debug 有錯,即使 Release 正常,程式保證是有 Bug 的,只不過可能是 Release 版的某次執行沒有表現出來而已。

  • 最佳化的影響(O1和O2)
  在函式的呼叫過程中,所有呼叫的訊息(回傳位址、參數)以及自動變數都是放在堆疊中的。如果函式的宣告跟實作不同(參數、回傳值、呼叫方式),就會產生錯誤,但 Debug 方式下,堆疊的存取透過 EBP 暫存器儲存的位址來實現,如果沒有發生陣列越界之類的錯誤(或是越界「不多」),函式通常能正常執行;Release 方式下,最佳化會省略 EBP 堆疊基本位址的指標,這樣通過一個全域指標存取堆疊,可能就會造成回傳位址錯誤導致整個程式崩潰。

  當您建置專案進行偵錯時,您是使用偵錯記憶體配置器 (Debug Memory Allocator)。這表示所有的記憶體配置周圍都放置了保護位元組。這些保護位元組會偵測記憶體覆寫。由於Release的組建與Debug版的堆積配置不同,在Debug中的記憶體覆寫可能不會造成任何問題,但在Release的組建中卻可能產生災難性的影響。
  • 變數的初始化(GZ的選項)
  Deug通常會將記憶體和變數做初始化,包括用 0xCC 初始化所有自動變數,0xCD(Cleared Data)初始化Heap中分配的記憶體(即動態分配的記憶體,例如 new ),0xDD ( Dead Data ) 填充已被釋放的Heap記憶體(例如delete),0xFD(deFencde Data)初始化受保護的記憶體(debug 版在動態分配記憶體的前後加入保護記憶體以防止越界存取).

  Release 版中未初始化的變量是隨機的(沒有初始化),這有可能使指針指向一個有效地址而掩蓋了非法訪問.

  • DEBUG 與 NDEBUG
  當定義了 _DEBUG 時,ASSERT() 函式會被編譯,而 NDEBUG 時不被編譯。此外,TRACE() 巨集的編譯也受 _DEBUG 控制有些人會自行定義#ifdef _DEBUG 之類的條件編譯,必須注意。


解決Release 版出錯的問題
  • 逐項測試Release的編譯選項
  前面已經提過,DebugRelease只是一組編譯選項的差別,實際上並沒有什麼定義能區分二者。我們可以修改Release版的編譯選項來縮小錯誤範圍。如上所述,可以把Release 的選項逐個改為與之相對的Debug選項,如/MD改為/MDd、/O1改為/Od,或運行時間最佳化改為程序大小最佳化。 注意,一次只改一個選項,看改哪個選項時錯誤消失,再對應該選項相關的錯誤,針對性地查找。
  • 對變數作初始化
  • 檢查ASSERT(),TRANCE()和#ifdef _DEBUG 之類的條件編譯的使用
  • 注意VC的警告訊息
  在 Debug 版中使用 /W4 警告級別,這樣可以從編譯器獲得最大限度的錯誤訊息,比如 if( i =0 )就會引起 /W4 警告。不要忽略這些警告,通常這是你程式中的 Bug 引起的。但有時 /W4 會帶來很多冗長的訊息。我們可以用:

   #progma warning(disable: 4702) //禁止
   //…
   #progma warning(default: 4702)
   //重新允許來暫時禁止某個警告,或使用

   #progma warning(push, 3) //設置警告級別為 /W3
   //…
   #progma warning(pop) //重設為 /W4
   //暫時改變警告級別,有時你可以只在認為可疑的那一部分程式碼使用
  • Dll的使用
  將其他不同版本的Dll混合造成不一致的現象稱為“動態連結庫的地獄(Dll Hell)”
  http://www.codeproject.com/dll/The_DLL_Hell.asp
  http://www.developerfusion.co.uk/show/1719/9/

  如果你的程式使用DLL時請注意,不能將debugrelease版的DLL混合在一起使用。
  • 利用Debug工具
   DebugView : 可用來捕捉OutputDebugString(),可以在VC的環境外執行程式查看除錯訊息.
   Gimpel Lint :靜態代碼檢查工具,不過要花$.
   SmartChecker: Rum-Time錯誤檢查工具
   BoundsChecker: Rum-Time錯誤檢查工具