顯示具有 程式設計 標籤的文章。 顯示所有文章
顯示具有 程式設計 標籤的文章。 顯示所有文章

2012年1月17日 星期二

calling matlab from c

* matlab array is column-major. C array and openCV mat are row-major.

* mxArray *engGetVariable(Engine *ep, const char *name);

Returns

Pointer to a newly allocated mxArray structure, or NULL if the attempt fails.
engGetVariable reads the named mxArray from the MATLAB engine session associated with ep. engGetVariable allocates memory for mxArray. Use mxDestroyArray to destroy the mxArray created by this routine when you are finished with it.


* Avoid "server busy" message box, add three lines to InitInstance as shown below.


    // Initialize OLE libraries
    if (!AfxOleInit())
    {
        AfxMessageBox(IDP_OLE_INIT_FAILED);
        return FALSE;
    }

    // for Matlab
    // The following three lines eliminate the Server busy message
    COleMessageFilter* pFilter = AfxOleGetMessageFilter();
    ASSERT_VALID(pFilter);
    pFilter->EnableNotRespondingDialog(FALSE);

2011年12月19日 星期一

calling GNUPLOT from a c++ program

calling GNUPLOT from a c++ program
  • 底下透過popen() 進行,但在windows系統下,僅console application能夠用,欲在windows application 下執行,不能用popen() ,詳細請看最後面的Note說明
  • 欲在windows application 下執行,最簡單的方法還是將gnuplot 指令輸出成一個檔案,再用system () 呼叫



class GNUplot {
public:
GNUplot() throw(string);
~GNUplot();
void operator ()(const string& command);
// send any command to gnuplot
protected:
FILE *gnuplotpipe;
};

GNUplot::GNUplot() throw(string) {
gnuplotpipe=popen("gnuplot","w");
if (!gnuplotpipe) {
throw("Gnuplot not found !");
}
}

GNUplot::~GNUplot() {
fprintf(gnuplotpipe,"exit\n");
pclose(gnuplotpipe);
}

void GNUplot:operator() (const string& command) {
fprintf(gnuplotpipe,"%s\n",command.c_str());
fflush(gnuplotpipe);
// flush is necessary, nothing gets plotted else
};


You simply construct one object and invoke it with operator () like

GNUplot plotter;
plotter("plot sin(x)");

Note that GNUplot will be killed as soon as your program terminates. So
you need to wait for keystroke or similar, otherwise you will only see
short flashing of the graph. If you need that the graph window stays on
screen after your pragram fnished, then instead of "gnuplot" in the
constructor invoke "gnuplot -persist".


Ref.
http://www.velocityreviews.com/forums/t278698-calling-gnuplot-from-a-c-program.html
http://nxforce.blogspot.com/2008/07/cc-calling-gnuplot-in-c.html


Note Note
If used in a Windows program, the _popen function returns an invalid file pointer that causes the program to stop responding indefinitely. _popen works properly in a console application. To create a Windows application that redirects input and output, see Creating a Child Process with Redirected Input and Output in the Windows SDK.

  • _popen()
http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k%28POPEN%29;k%28DevLang-%22C%2B%2B%22%29;k%28TargetOS-WINDOWS%29&rd=true

  • C++ interface to Gnuplot via POSIX pipes
http://code.google.com/p/gnuplot-cpp/
此方法還是一樣用_popen(), windows program 可能不適用

  • 欲在 windows application中呼叫gnuplot 請參考底下這篇
http://code10111.blogspot.com/2011/06/call-gnuplot-from-c-program-in-windows.html
他的做法跟  Creating a Child Process with Redirected Input and Output 有些雷同




2011年6月9日 星期四

Copy dialog in Visual Studio 2010 (C++)

If you want to copy a dialog from one project to another. You cannot just copy/paste in resource view of VS. The following steps will teach you how to copy dialog from project P1 to another project P2 by manual.

1. Use a text editor (e.g., Ultra Editor) to open P1.rc and P2.rc.

2. Find and copy the dialog code that you want to copy in P1.rc.

3. Paste the code to P2.rc.

4. Copy the dialog class files (e.g., xxx.h and xxx.cpp) from P1 to P2.

5. Modify the dialog class files (xxx.cpp, xxx.h), especially #include directive (P1 ->P2)

6. Open resource.h of P1 and resource.h of P2.

7. Copy the corresponding ID from the resource.h of P1 to resource.h of P2. Remember to modify the ID number using the sequence number of P2. In this step, you can try to build the P2 in VS. VS will output error messages to indicate which IDs are undeclared. Also remember to modify the next default values (_APS_NEXT_XXX_VALUE).

8. In step 7, if the ID symbol is duplicate, you should modify the ID symbol in the resource.h and rc file.

2011年6月8日 星期三

GSL problem: Run-Time Check Failure #0

If you call some gsl functions, the app pops up an error message.
"Run-Time Check Failure #0 - The value of ESP was not properly saved across a 
function call.  This is usually a result of calling a function declared with 
one calling convention with a function pointer declared with a different calling 
convention. "
 
There are some ways to solve the problem.   
  • You can set the [C/C++->Code Generation->Basic Runtime Checks] to default 
    which ensure the error about ESP won't happen. 
  •  Another method: ??

 
Test code:
#include <gsl/gsl_vector.h>
#include <stdio.h>

int main( void)
{
 double array[3] = { 1, 2, 3};
 gsl_vector_view view = gsl_vector_view_array( array, 3);
 return 0;
} 

2011年6月7日 星期二

win7 32bits 與 64bits 使用差異

  • 網路銀行ActiveX 或是flash 無法安裝在 IE 64bits 版本, 需要自行執行 C:\Program Files (x86) 裡面的IE, 才能安裝ActiveX
  • VC 用win32 編譯,matlab 也要安裝 32 bits版本, 不然vc project 無法link matlab 64 bits 的lib
  • 很多scanner 的驅動程式只支援到 win7 32-bit, 沒有64bit

Build GSL for Windows

1. Install MinGW
    Download mingw-get-inst-20110530.exe (574.8 KB) 
    Please check the following link to download the new one.
    http://sourceforge.net/projects/mingw/files/http://sourceforge.net/projects/mingw/files/
2. Download and unzip GSL
3. Run  MinGW shell, and change directory to the GSL directory
4. In GSL directory, run the following instructions.
    configure
    make
    make install
5. After "make install", the GSL binary files and header files are in MSYS local (C:\MinGW\msys\1.0\local\). You can copy the MSYS local directory to other directory and rename it.

2011年6月6日 星期一

手動編譯GSL for MSVC

手動編譯GSL for MSVC
(http://www.cnblogs.com/bobyguo/articles/1343620.html)

這篇文章會教你如何編出for MSVC的gsl library。
如果你想學習如何用Visual C++ link MinGW產生的dll檔先別急著左轉,
下面有網址會教你。

最近要用到一些線性代數的計算。
因為考慮到C/C++的效能較MATLAB好(老闆的經驗),
所以決定使用C/C++的linear algebra libraries。

最有名的linear algebra library莫過於LAPACK,
但是它是用Fortran寫的,我想用C/C++ XD
雖然有人port LAPACK到C++,但很久都沒有更新了。

後來我想到GSL,就去GSL官網找,發現沒有binaries for Windows。
雖然非官網有人有編好的gsl for Windows,但已兩年沒有更新。
我仍然想試試自己編譯gsl-1.11看看。
我就下載了gsl-1.11的source,看INSTALL,gsl可以在cygwin下編譯、
也可以在MSYS下編譯。由於自己體驗過cygwin的效能很...,
所以決定用MSYS & MinGW編。
我的MinGW是安裝Qt Open Source 4.4.0時一起裝的,
所以我還少MSYS,所以我又去MinGW的官網下載了MSYS 1.0.10。

首先編譯gsl。
打開MSYS:
tar zxvf gsl-1.11.tar.gz
cd gsl-1.11
./configure
make
make install

這樣就編完了。
但是去/usr/local/lib卻只發現libgsl.a、libgsl.dll.a、...這幾個檔案,
沒有發現libgsl.lib。
但在/usr/local/bin發現了libgsl-0.dll、libgslcblas-0.dll這兩個檔案。
但我目標其實要能在Visual C++下使用gsl。
因為我已經編好Qt for Visual C++ 2008,我想用Qt開發我的界面,
gsl為使用的linear algebra library。
所以我試著在Visual C++ 2008下找了個gsl的範例程式,
然後link /usr/local/lib的檔案libgsl.a、libgsl.dll.a,
但都失敗,不是無法link,就是執行時期掛掉。
後來我不死心,試著去google搜如何用MinGW產生給VC++用的.lib檔。
果然給我搜到了,就在MinGW的Wiki找到了。


原來MinGW在編譯libraries時候可以給參數產生一個叫def的檔案。
有了它,然後用Visual Studio的lib指令就可以產生lib檔。

但問題是我之前編譯完gsl並沒有發現仼何def檔。
難道要我修改gsl的Makefile加上產生def的參數,重編一次嗎?
太困難了,況且我也不太會改。

雖然我對def、lib、exp這些檔案的關係不是非常的了解,
但我作了一個大膽的猜想,
理論上dll檔裡面的資訊應該是可以用某個程式匯出成def。
果然在上面那個網址就有提到mingw-utils package的pexports可以辦得到。
我就又去官網下載了mingw-utils裝。

然後試著產生libgsl-0.dll的lib檔:
cd /usr/local/bin
pexports libgsl-0.dll >libgsl-0.def
lib /machine:i386 /def:libgsl-0.def

結果真的產生了libgsl-0.lib。

然後用Visual C++ 2008 Express重編我的gsl範例,就成功執行了。
有圖有真相:


那個範例程式是從GSL網站找的:


我用Visual C++ 2008編,輸出結果是:
-1.775967713143382600e-01
我用MinGW編,輸出結果是:
-1.775967713143382900e-01
GSL網站輸出結果是:
-1.775967713143382920e-01

2010年12月28日 星期二

Cout redirection

Redirecting Standard Output (Cout) to a Text File

A couple of times I've needed/wanted to output cout operations to a text file. A solution is:
 
#include <iostream>
#include <fstream>
using namespace std;

int main ()
{
 cout << "This is sent to prompt" << endl;
 ofstream fileOutputStream;
 fileOutputStream.open ("test.txt");
 streambuf* sbuf = cout.rdbuf();
 cout.rdbuf(fileOutputStream.rdbuf());
 cout << "This is sent to file" << endl;
 cout.rdbuf(sbuf);
 cout << "This is also sent to prompt" << endl; 
 return 0;
}
 
However, note that if you call another function from main then fileOutputStream will go out of scope, so if you want to call other functions the file must be made global. i.e.:


#include <iostream>
#include <fstream>
#include <string>
using namespace std; 

ofstream fileOutputStream;  // this must be kept somewhere global

void redirectStandardOutputToFile ( string filePath, bool toPromptAlso )
{
 streambuf* sbuf = cout.rdbuf();
 fileOutputStream.open( filePath.c_str() );
 cout.rdbuf(fileOutputStream.rdbuf());
 if ( toPromptAlso )
  cout.rdbuf(sbuf);
}

void anotherFunction() {
 cout << "The contents of this function is also sent to file";
}

int main ()
{
 redirectStandardOutputToFile("test.txt", false );
 
 cout << "This is sent to file" << endl;
 anotherFunction();
 cout << flush;  // is a good policy to flush or else program might close before text is output.

 return 0;
}



origin from http://www.andrewnoske.com/wiki/index.php?title=Cout_redirection

2010年11月11日 星期四

typedef 知多少?

《教學》typedef 知多少?
很多寫 C/C++ 的人都把 typedef 當成#define 來使用。
誠然,像這樣的定義
typedef unsigned short WORD;
就相當於
#define WORD unsigned short
但就本義來說,#define 是字串的取代,例如
#define WORD Hello!
編譯器不會有任何的抗議就通過的,在程式任何使用到 WORD 的地方
編譯器會忠實的用 Hello! 取代 WORD

由於取代的字串可不限於一行且可代入參數,所以#define 可用於 Macro 的定義如
#define max(a,b) (((a) > (b)) ? (a) : (b))

回過頭來說typedef主題,從本義來說typedef是 C/C++ 型態的別名
所以若你想把
#define WORD Hello!
套在 typedef 的身上:
typedef Hello! WORD;
那麼編譯器會給你一個難堪錯誤訊息!因為 Hello! 不是一個合法的
型態,你不能為它取型態別名。

typedef看來不怎麼有用是吧!?非也,由於所謂 C/C++ 的型態是很廣義的,使用typedef
可以使程式看起來簡潔易懂且不容易出錯。下面是幾個typedef的應用例子:

簡單型態的別名
typedef unsigned char BYTE;//定義無號單字節的型態
typedef unsigned short WORD;//定義無號雙字節的型態
typedef unsigned long DWORD;//定義無號四字節的型態
//嗯!這三行沒什麼大不了,用#define也可以做

結構型態的別名
typedef struct StructTag{
int mA;
int mB;
}STRUCTTAG, *PSTRUCTTAG;
當要建立這個結構的物件時,就可以用別名 STRUCTTAG 和 PSTRUCTTAG,例如
STRUCTTAG StructObj;
PSTRUCTTAG pStructObj;
就相當於
struct StructTag StructObj;
struct StructTag *pStructObj;

函式型態的別名
如果你有一個 library 提供了字串轉整數的函式
int HexToInt(char *str);//十六進位字串轉整數
int DecToInt(char *str);//十進位字串轉整數
當你的程式要使用這些函式時,就必需include這些函式的定義,但用typedef會
更簡潔:
typedef int ToInt(char *str);
//上面這行定義了一個需傳入字串(char *)且返回整數(int)的函式型態別名
//叫 ToInt
ToInt HexToInt,DecToInt;//宣告HexToInt,DecToInt這兩個函式
現在你的程式可以呼叫HexToInt(...),DecToInt(...)了。

在Windows,通常是應用程式呼叫API來工作的,但由於某種需求,Windows API
會反過來呼叫應用程式的函式,這種函式稱之為 callback function
callback function 對API來說只有該傳入那些參數和返回值型態,沒有函式名稱而用函式
指標來呼叫(事實上Windows也不可能用程式的函式名稱來呼叫)。這時使用函式型態的別名
來宣告函式指標就特別好用了。
舉個例子吧!建立一個新的行程(Thread)的函式:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT
nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

其中AFX_THREADPROC就是一個函式型態的別名
它的定義是
typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID);
//AFX_THREADPROC 是一個傳入一個指標參數返回UINT型態值的函式
//指標的名稱(AFX_CDEL是標明函式呼叫為 __cdecl(C style calling convention))

用這個宣告,不論API或應用程式本身都很方便,如應用程式可以這樣來
開啟行程:
CWinThread* pMyThread=AfxBeginThread(
(AFX_THREADPROC)MyThreadFunc,............
是不是很簡潔明瞭?嗯...MyThreadFunc算不算 callback function 有點疑義,但做為 typedef 的使用例子應沒問題的








 


原文引用:http://ehome.hifly.to/showthread.php?postid=332

OnTimer是否沒有返回而重複執行

看到一篇blog,http://blog.pfan.cn/yuqiexing/40732.html
/////////////////
CxxxDlg::OnTimer(UINT nIDEvent)
{
  static int i = 0,j;
  j = i++;
  if (i==2) KillTimer(nIDEvent);
  MessageBox("!");
  i++;
  CString str;
  str.Format("%d,%d ",j,i);
  ::OutputDebugString(str);
}
以上代碼在執行的時候,會彈出2個messagebox。很多人都有1個疑問,那就是當第一個消息框彈出的時候,應該相當於是模態對話框,怎麼還會第二次進入OnTimer,並且再次彈出messagebox?
他的解釋是:
原來,當你在主消息循環的消息處理函數中彈出一個模態對話框,他阻塞了主消息循環,理論上,這時可以把主窗口關閉了!但是因為這個彈出的模態對話框還在, 它阻塞了winmain這個主消息循環,實際上你是關不掉的。注意,阻塞的實際意思是說:消息被模態對話框處理了,而沒有傳到後面的父窗口。而每次彈出對 話框,都是由最上層的那個消息框掌握著消息循環,其他的消息循環被阻塞了。

      所以,上面代碼運行的情況是:

(1)彈出第一個messagebox,然後後續代碼暫停執行。

(2)第二次進入OnTimer

(3)彈出第二個messagebox接替消息循環,第一個messagebox禁用。後續代碼暫停執行。

(4)用戶點擊第二個messagebox。此時顯示結果,i = 3,j=1;再次,點擊第一個messagebox,此時顯示結果,i=4 ; j=0
(測試顯示應該是: i =4, j=1);


我的疑問,當彈出第一個messagebox後,主消息循環被阻塞,OnTimer中後續代碼暫停執行,也就是說OnTimer沒有返回,是這樣嗎?那麼 怎麼能夠第二次進入OnTimer呢?這樣的話一個函數還沒有返回又第二次進入執行,有可能造成衝突啊,事實上的確是第二次進入OnTimer了。

以上說明:OnTimer沒有返回時,間隔時間到,可以重複進入該函數。又做了個測試
double fun(double i)//執行時間較長,大於100ms
{
double t = 1;
for(;i <100000000;i++)
{
t = t*i/(i+1);
}
return t;
}

void CMainFrame::OnTimer(UINT nIDEvent)
{
static double i=0,j=0;
fun(i);
j++;
if(j == 0)
{
MessageBox("jjjjjjjjjjjjjjjjjjjjjjjjjj");
}
CFrameWnd::OnTimer(nIDEvent);
}
在主窗口初始化時定義定時器

SetTimer(1,100,NULL);

結果沒有任何messagebox輸出;

也就是說在fun()正在執行,沒有返回時,間隔時間到,並沒有再次進入OnTimer(),而是一直等到fun()返回[b]。[/b]
這個和前一個測試有什麼區別?為什麼能導致不同的結果呢?

---------------------------------------------------------------------
樓主首先要明白,第二次進入OnTimer不需要在第一次退出OnTimer後才能發生,函數重入是非常常見的事情,尤其是有模態對話框的情況,wltg解釋的很好,希望你能懂
引用 6 樓 wltg2001 的回覆:
也就是說OnTimer沒有返回,是這樣嗎?那麼怎麼能夠第二次進入OnTimer呢?
======================
最主要的原因是MessageBox是模態對話框,而模態對話框內部是有消息循環的,當第一個MessageBox彈出時,由於模態對話框內部有消息循環,所以定時器消息還是可以被處理.
你的第一個例子,消息處理程序是阻塞在MessageBox上的,因為MessageBox內部有消息循環,所以消息還是可以被處理的.第二個例子卻是阻塞在fun()上,在這個函數內部卻沒有消息循環,當然要等它做完能繼續下去了.

C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較

http://www.cppblog.com/lingjingqiu/archive/2009/01/06/71363.html
最近用了一段時間的wxWidgets。
基本上主流的界面庫也就算用的差不多了。
這裡拿一個表格對這些界面庫做一個比較直觀的比較。

界面庫名稱 接口設計 界面編輯器 高級佈局功能 平面繪製 平台兼容性 語言支持 IDE兼容性 視圖-模型分離機制 運行時 其它
Windows Forms 接口優秀。C++下使用CLI擴展,其它語言為原生支持。 界面編輯器完整,包括佈局、屬性、消息關聯的完整設置。不可預覽。 Table Layout,Splitter Layout,Flow Layout等,Anchor和Dock機制。多分辨率界面下表現良好。 GDI+,面向對象的2D繪製接口,使用簡便。 需要.Net平台支持。WIndows或Linux(Mono,非官方支持),支持Windows CE C++/CLI, 支持.net的語言。 僅VS。 佈局和視圖方案建立在代碼中。部分組件支持Model-View架構。 需要部署對應的.net 商業協議
MFC 基於宏和虛函數,使用特殊格式註釋,使用自定義的RTTI系統。類接口設計優良。通過回調函數和虛繼承重載調用客戶代碼。 基於資源編輯器,僅能對空間基本佈局和少量屬性進行調整。不可預覽。 缺乏高級佈局功能,多分辨率需要是手工或程序中調整。 GDI及GDI封裝,可選GDI+ Windows,Windows CE C++ Only,
支持COM時可以實現Binary級別復用。
僅Visual Studio 使用資源保存控件的基本控件佈局,提供Doc-View機制和控件數據交換支持視圖分離。 需要部署MFC運行時庫。 商業協議
WTL 基於模板和虛函數。類接口類似於MFC。需要使用多重繼承。通過模板特化和回調函數與客戶代碼交互。 同MFC 同MFC 同MFC 同MFC 同MFC,對COM的支持比MFC完善很多。 Visual Studio,Windows下支持標準的C++編譯器。 使用資源文件保存空間佈局。 自由協議
wxWidget 宏,自定義RTTI。使用回調函數與用戶代碼交互。 無官方界面編輯器。可使用第三方界面編輯器。部分編輯器具有完整的所見即所得功能,且具有預覽能力。 使用Sizer實現多分辨率的佈局。功能偏弱。 wxDC等。 Windows,Linux,Unix,MacOS等 C++, .NET, Python,
Lua,
Ruby等
良好的編譯器兼容性,缺乏IDE繼承。 可以將界面屬性生成到代碼中,也可以使用XML格式保存。 wx的動態鏈接庫或靜態鏈接。 自由協議
Qt 使用宏和自定義的RTTI。使用Singal-Slot機制實現用戶代碼交互。可通過繼承實現擴展。 Qt Designer,具備完整的所見即所得編輯功能。可預覽界面。 具備完整的佈局功能。多分辨率/多平台下表現良好。 QCanvas等。 Windows,Linux,Unix,MacOS等。 C++,Python等 可集成到Eclipse和VS 使用資源文件保存界面信息。部分組件具備Model-View-Delegate架構 qt的動態鏈接庫。 開源協議+商業協議
GTK+ 使用signal-slot機制完成用戶代碼交互。 GLADE,具備所見即所得的界面編輯功能 Layout Containers,具備較完整的佈局能力。 GTK Graphics Context Windows,Linux,Unix,MacOS等。 C,C++,Python,.NET等。 (暫時未知) 使用代碼完成界面設置。部分組件具備Model-View架構。 GTK Runtime 開源協議

此外,除了WTL外,其餘各界面庫均有完整及時的文檔和手冊,因此比較項中不再指明。
回帖中有人指出國際化問題的比較,事實上MS的產品和開源對於國際化的解決方法是不同的。但是都可以比較方便的解決國際化問題。
posted on 2009-01-06 18:09 空明流轉 閱讀(3617) 評論(10)  編輯 收藏 引用



評論



# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-01-06 19:05 qtopia
Nokia 最近推了 Qt Creator,是較好的IDE環境  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-01-06 19:05 t
博主還可以增加一欄「國際化支持」  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較[未登錄] 2009-01-08 12:48 kenlistian
我覺得wxwidget是不錯的選擇。  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-01-11 20:25 www
wxwidget做簡單的還可以, 做複雜的界面,嘿嘿,那就麻煩了. win下還是MFC最爽.  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-01-19 00:49 ArenAK
有沒有哪一個是作者比較喜歡用的呢,及其原因?  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較[未登錄] 2009-01-19 16:35 六水
寫的好~  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-02-05 11:08 空明流轉
@六水
我就知道你是友情贊助的。。。  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較[未登錄] 2009-02-11 22:35 六水
這你也知道?  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-02-25 10:19 aladdina
wxWidgets的代碼質量不是很高,有一些比較初級的bug。我用過一段時間的wxWidgets,自己測試沒問題,但是用戶總會發回一些crash report。

另外,wxWidgets和Qt除了對於一些常見應用,比如窗口、DC的封裝之外,還提供了很多系統接口的封裝,比如clipboard, thread, socket等。

Qt有Qt Creator作為IDE,工程項目文件可以跨平台,另外,工程文件也可以用qmake轉成平台無關的makefile。準備最近有空試試Qt,個人覺得商業代碼質量還是要稍微好一些。

  回覆  更多評論

# re: C++下Windows Forms + MFC + WTL + wxWidgets + Qt + GTK+ 非官方綜合比較 2009-09-18 12:09 idol
gtk 哪有 signal-slot機制,用的明明是 callbacks

WTL---WxWidget---MFC 何去何從

WTL---WxWidget---MFC 何去何從
C++程序員打交道最多的就是MFC了,這個我不想多說,說來都是淚(C#年年更新,C++十年才出了一個featurePack還不是很好用)

現在另外兩支隊伍越來越龐大(所謂窮則思變,呵呵),一是WTL,關於它的種種傳說可以參考WTL中文教程,裡面講的很詳細,這裡我想說的是為什麼使用WTL:
傳 說WTL是微軟內部所使用的界面框架程序,原因是高效,簡潔,實際也是如此,WTL的程序內的內存佔用比MFC小的多,而且WTL內建了對於可縮放對話框 的支持(支持控件停靠哦),對於多文檔以及視類的支持更是MFC望塵莫及的,但是WTL也有其自身的缺點,比如開發文檔少,尤其是中文的,嚮導不夠聰明 (誰用誰知道,呵呵),換句話說,用WTL就意味著多敲一些代碼,不過這個可以有一些第三方工具緩解(還是那句話,窮則思變),如WTL helper,還有國產的WTL helper,還有國產的VFC,兩者都是開源軟件,放心使用,建議有意開發WTL的程序員先打好基本功,因為這個基於ATL的這個鐵娘子不是誰都可以駕馭的哦,如果對程序大小要求不是太苛刻的話,還是拿MFC將就吧

其二就是我最近很感興趣的WxWidget, 這款框架最大的優勢就是真正意義上的跨平台,其注意真正兩個字,因為在我看來所謂的java跨平台不過是幌子,有種連JVM也跨平台試試(JAVA擁護者 別拍我哦,其實JAVA還是在OO領域很強大的),那真正二字就是因為,WxWidget提供了在本機源碼級的跨平台,從此你在讓客戶使用你軟件的時候不 必再說:「你必須給我安裝×××虛擬機或×××Framwork」,在一個平台下編譯的代碼,不用做任何更改,在另一個平台下就可以編譯通過並且發佈,這 豈不美哉,而且WxWidget所使用的界面元素幾乎都取自本地計算機,就是說你的見面客氣來很有親和力,最終發佈的二進制代碼文件愛你無論是靜態還是動 態鏈接都要比GTK之流小很多(見圖表)。有興趣一試的可以看看WxWidget開發環境配置



說了這麼多,總結如下,三種平台各有所長,如果你是windows程序員,那麼WxWidget對你而言沒有多大意義,如果你剛開始接觸C++,那WTL這個高品位新娘也許會讓你忙的團團轉,呵呵,大家自己斟酌吧
posted on 2008-12-21 22:13 pear_li 閱讀(3381) 評論(22)  編輯 收藏 引用 所屬分類: C++


評論


# re: WTL---WxWidget---MFC 何去何從 2008-12-22 09:14 路人丁

樓主還搞不清VC++和C++?
「C++程序員打交道最多的就是MFC了……C++十年才出了一個featurePack還不是很好用」應該改為「VC++程序員打交道最多的就是MFC了……VC++十年才出了一個featurePack還不是很好用」
  回覆  更多評論    

# re: WTL---WxWidget---MFC 何去何從 2008-12-22 09:21 路人乙

和路人丁有同感,樓主說的只是VC或MFC這範圍的C++程序員,
我個人感覺做得比較好的C++庫,是QT、ACE。
QT與wxWidget等四類GUI庫作過比較,贏得過最佳設計與最具表達力的稱號。

# re: WTL---WxWidget---MFC 何去何從[未登錄] 2008-12-22 10:30 momor

QT固然好,除非你做開源,不然你沒看過QTwindow上開發的商業授權費用是多少。另外大家也不要過於對字眼上太過敏感,畢竟我們是程序員不是搞文字工作的,基本從事windows平台的程序員比較多,當然MFC這種現成的東西接觸自然比較多了。
我最近也在弄wxWidgets的東西^^

知名 GUI lib 簡評 (GTK+, Qt, wxWidgets)

GTK+

GTK+ 主要用在 X Window 上,入門門檻較高
特色是,他用不具有物件功能的純"C" 語言,模擬物件導向。 所以寫起來比較複雜艱澀,而且充滿大量巨集,使用和除錯都不是很容易,但優點則是可以用 C,不需 C++,如果和 Win32 SDK 比較,不會難學多少,缺點是不易上手使用,而且文件比較缺,架構又非常複雜,且提供的東西比起其他無所不包的 library,是簡陋了一點,函數命名又臭又長。 對於簡單的程式,GTK+ 會顯得太複雜,但是當你開始想擴充其他 library 也都沒提供的進階功能,就會開始讚嘆GTK+ 的架構嚴謹,還有超乎想像的高度彈性。 同樣的東西要用 MFC 來做反而會要人命並且多國語言的支援良好,內部也全面使用 UTF-8,相容性好,又是 unicode能夠習慣的話,GTK+ 值得推薦,但沒有很建議學,畢竟不好學,要用到熟會需要比較久,而且那樣很多 C++ 的功能會用不到。 GTK+ 有 C++ 版本叫做 GTK--,沒用過但看文件覺得,並沒有比 gtk+ 簡單到哪裡去。 因為 gtk+ 本來就是物件導向,所以即使換了 c++ 語言,寫起來架構還是差不多的。 另外,gtk+ 有 Windows 版本,但缺點是,執行緩慢,不穩定,而且介面是使用 gtk+ 自己的,不是使用 Windows 內建的"Native" 原生圖形介面,看起來會不太習慣。 Mac OS X 下可用 X11 來執行 gtk+但那樣出來的程式是長得像 UNIX 程式,而不是美美的 OS X Aqua 外觀

wxWidgets 和 MFC 最接近, 命名習慣或架構都高度相似,會 MFC 幾乎不用重新學習有十餘年歷史,此外,他的物件封裝比 MFC 要好,提供的功能也多上太多,又跨平台一般知名的 MFC 程式都會選擇用 wxWidgets 改寫,來快速移植原程式到其他平台例如 eMule 用 wxWidgets 移植出 aMule, xMule, 還在開發中的 Filezilla 3...等

而他最主要的特色是,他是"跨平台"的 "Native" GUI toolkit在各種平台上都可寫出使用該平台內建 Native 原生圖形介面的程式。 在 Windows 上就長得跟其他 Windows程式一樣,在 Linux 下就使用 gtk+ 的圖形介面,在 Mac OS X 下就可以使用華麗的Aqua 外觀風格,這點是非常強悍。 不像 gtk+ 到其他系統都還是只能用 gtk+ 自己的缺點是,中文支援在有些地方會出問題,例如剪貼簿的操作。 得自己 patch但仍然相當推薦,即使是個龐大的 library,效能依舊不會太差,尤其在Windows 上執行速度並不輸 MFC,與其學 MFC,不如學 wxWidgets。



Qt
的功能,應該是這三者加上 MFC 之中最強大的,文件也很完整,又有 RAD 工具可以輔助開發,並且有商業公司做強力後盾。不但有 Windows/X Window/Mac 版本,甚至還有嵌入式系統可用的版本,穩定性還不錯,物件封裝也算良好,資源比 GTK+ 或wxWidgets 多得非常多,而且發行公司提供了相當多範例,算是一家以開放原始碼成功營利的模範公司。 知名的 KDE 整個是用他開發,證明了他的穩定性和強大功能。

缺點是如果你用他開發非 GPL 開放程式碼的軟體,必須以極昂貴的金額,購買商業版本。 而他的圖形介面並不完全是 "Native GUI",只是透過 theme 去模擬系統上的標準 GUI,所以看起來很像,卻會有些地方可以明顯看出破綻。 執行速度緩慢還有過於龐大則是另一個問題。 雖然封裝得很良好文件也齊全,並不代表他就很容易學還有一個嚴重問題是,他寫的不是標準 C++,他使用的 signal/slot 機制必須透過 Qt提供的 preprocessor 處理過才可以轉送給編譯器,這部份可能被限定用 qmake,算是一個可惜的地方,不過瑕不掩瑜,還是很推薦。忘了說,他內部也是 unicode,多國語言沒問題。

Unicode, MBCS and Generic text mappings

http://www.codeproject.com/KB/cpp/unicode.aspx

Unicode, MBCS and Generic text mappings

By Chris Maunder

Introduction

In order to allow your programs to be used in international markets it is worth making your application Unicode or MBCS aware. The Unicode character set is a "wide character" (2 bytes per character) set that contains every character available in every language, including all technical symbols and special publishing characters. Multibyte character set (MBCS) uses either 1 or 2 bytes per character and is used for character sets that contain large numbers of different characters (eg Asian language character sets).
Which character set you use depends on the language and the operating system. Unicode requires more space than MBCS since each character is 2 bytes. It is also faster than MBCS and is used by Windows NT as standard, so non-Unicode strings passed to and from the operating system must be translated, incurring overhead. However, Unicode is not supported on Win95 and so MBCS may be a better choice in this situation. Note that if you wish to develop applications in the Windows CE environment then all applications must be compiled in Unicode.

Using MBCS or Unicode

The best way to use Unicode or MBCS - or indeed even ASCII - in your programs is to use the generic text mapping macros provided by Visual C++. That way you can simply use a single define to swap between Unicode, MBCS and ASCII without having to do any recoding.
To use MBCS or Unicode you need only define either _MBCS or _UNICODE in your project. For Unicode you will also need to specify the entry point symbol in your Project settings as wWinMainCRTStartup. Please note that if both _MBCS and _UNICODE are defined then the result will be unpredictable.
Adjusting your project settings

Generic Text mappings and portable functions

The generic text mappings replace the standard char or LPSTR types with generic TCHAR or LPTSTR macros. These macros will map to different types and functions depending on whether you have compiled with Unicode or MBCS (or neither) defined. The simplest way to use the TCHAR type is to use the CString class - it is extremely flexible and does most of the work for you.
In conjunction with the generic character type, there is a set of generic string manipulation functions prefixed by _tcs. For instance, instead of using the strrev function in your code, you should use the _tcsrev function which will map to the correct function depending on which character set you have compiled for. The table below demonstrates:
#define Compiled Version Example
_UNICODE Unicode (wide-character) _tcsrev maps to _wcsrev
_MBCS Multibyte-character _tcsrev maps to _mbsrev
None (the default: neither _UNICODE nor _MBCS defined) SBCS (ASCII) _tcsrev maps to strrev
Each str* function has a corresponding tcs* function that should be used instead. See the TCHAR.H file for all the mapping and macros that are available. Just look up the online help for the string function in question in order to find the equivalent portable function.
Note: Do not use the str* family of functions with Unicode strings, since Unicode strings are likely to contain embedded null bytes.
The next important point is that each literal string should be enclosed by the TEXT() (or _T()) macro. This macro prepends a "L" in front of literal strings if the project is being compiled in Unicode, or does nothing if MBCS or ASCII is being used. For instance, the string _T("Hello") will be interpreted as "Hello" in MBCS or ASCII, and L"Hello" in Unicode. If you are working in Unicode and do not use the _T() macro, you may get compiler warnings.
Note that you can use ASCII and Unicode within the same program, but not within the same string.
All MFC functions except for database class member functions are Unicode aware. This is because many database drivers themselves do not handle Unicode, and so there was no point in writing Unicode aware MFC classes to wrap these drivers.

Converting between Generic types and ASCII

ATL provides a bunch of very useful macros for converting between different character format. The basic form of these macros is X2Y(), where X is the source format. Possible conversion formats are shown in the following table.
String Type Abbreviation
ASCII (LPSTR) A
WIDE (LPWSTR) W
OLE (LPOLESTR) OLE
Generic (LPTSTR) T
Const C
Thus, A2W converts an LPSTR to an LPWSTR, OLE2T converts an LPOLESTR to an LPTSTR, and so on.
There are also const forms (denoted by a C) that convert to a const string. For instance, A2CT converts from LPSTR to LPCTSTR.
When using the string conversion macros you need to include the USES_CONVERSION macro at the beginning of your function:
Collapse
void foo(LPSTR lpsz)
{
   USES_CONVERSION;
   
   ...
   LPTSTR szGeneric = A2T(lpsz)
   // Do something with szGeneric
   ...
}
Two caveats on using the conversion macros:
  1. Never use the conversion macros inside a tight loop. This will cause a lot of memory to be allocated each time the conversion is performed, and will result in slow code. Better to perform the conversion outside the loop and pass the converted value into the loop.

  2. Never return the result of the macros directly from a function, unless the return value implies making a copy of the data before returning. For instance, if you have a function that returns an LPOLESTR, then do not do the following:
    Collapse
    LPTSTR BadReturn(LPSTR lpsz)
    {
        USES_CONVERSION;
        // do something
        return A2T(lpsz);
    }
    Instead, you should return the value as a CString, which would imply a copy of the string would be made before the function returns:
    Collapse
    CString GoodReturn(LPSTR lpsz)
    {
        USES_CONVERSION;
        // do something
        return A2T(lpsz);
    }

Tips and Traps

The TRACE statement

The TRACE macros have a few cousins - namely the TRACE0, TRACE1, TRACE2 and TRACE3 macros. These macros allow you to specify a format string (as in the normal TRACE macro), and either 0,1,2 or 3 parameters, without the need to enclose your literal format string in the _T() macro. For instance,
Collapse
TRACE(_T("This is trace statement number %d\n"), 1);
can be written
Collapse
TRACE1("This is trace statement number %d\n", 1);

Viewing Unicode strings in the debugger

If you are using Unicode in your applciation and wish to view Unicode strings in the debugger, then you will need to go to Tools | Options | Debug and click on "Display Unicode Strings".

The Length of strings

Be careful when performing operations that depend on the size or length of a string. For instance, CString::GetLength returns the number of characters in a string, NOT the size in bytes. If you were to write the string to a CArchive object, then you would need to multiply the length of the string by the size of each character in the string to get the number of bytes to write:
Collapse
CString str = _T("Hello, World");
archive.Write( str, str.GetLength( ) * sizeof( TCHAR ) ); 

Reading and Writing ASCII text files

If you are using Unicode or MBCS then you need to be careful when writing ASCII files. The safest and easiest way to write text files is to use the CStdioFile class provided with MFC. Just use the CString class and the ReadString and WriteString member functions and nothing should go wrong. However, if you need to use the CFile class and it's associated Read and Write functions, then if you use the following code:
Collapse
CFile file(...); 
CString str = _T("This is some text"); 
file.Write( str, (str.GetLength()+1) * sizeof( TCHAR ) ); 
instead of
Collapse
CStdioFile file(...); 
CString str = _T("This is some text"); 
file.WriteString(str); 
then the results will be Significantly different. The two lines of text below are from a file created using the first and second code snippets respectively:
Unicode strings
(This text was viewed using WordPad)

Not all structures use the generic text mappings

For instance, the CHARFORMAT structure, if the RichEditControl version is less than 2.0, uses a char[] for the szFaceName field, instead of a TCHAR as would be expected. You must be careful not to blindly change "..." to _T("...") without first checking. In this case, you would probably need to convert from TCHAR to char before copying any data to the szFaceName field.

Copying text to the Clipboard

This is one area where you may need to use ASCII and Unicode in the same program, since the CF_TEXT format for the clipboard uses ASCII only. NT systems have the option of the CF_UNICODETEXT if you wish to use Unicode on the clipboard.

Installing the Unicode MFC libraries

The Unicode versions of the MFC libraries are not copied to your hard drive unless you select them during a Custom installation. They are not copied during other types of installation. If you attempt to build or run an MFC Unicode application without the MFC Unicode files, you may get errors.
(From the online docs) To copy the files to your hard drive, rerun Setup, choose Custom installation, clear all other components except "Microsoft Foundation Class Libraries," click the Details button, and select both "Static Library for Unicode" and "Shared Library for Unicode."

Writing libraries with Generic-Text-Mapping

http://www.jeffhung.net/blog/articles/jeffhung/522/

Writing libraries with Generic-Text-Mapping

最近在設計某 library 的 API,所以就想順便來講講怎麼設計符合 generic-text-mapping 的 library。簡單講,generic-text-mapping 是一種利用 preprocessor 的技巧,用一個 define symbol 決定 application 程式裡用的 character,是指 char 亦或是 wchar_t。若是後者,就是我們常在說的 unicode 程式。
個人覺得 generic-text-mapping 實在是 Microsoft 當年偷懶後的結果。在那個年代,這個技術確實是讓眾多既存程式快速升級成 unicode 程式的好方法,但在 unicode 支援已經普遍成熟的現在,卻反而形成了脫褲子放屁的尷尬[1]
言歸正傳。尷尬歸尷尬,畢竟我們寫的 library,還是不免需要在 Windows 上執行,讓其他 windows developer 使用,因此入境隨俗,從「惡」如流,還是必須的。如何呼叫使用了 generic-text-mapping 技術的 library,已經有太多文章闡述了[2],因此以下簡述如何寫作 generic-text-mapping 的 library 供他人使用。
同時提供兩個版本,最後才用 macro 選擇
以下的範例程式,採用並擴充 Microsoft 的 convention,後綴字(postfix)為 W 時,表示是 wchar_t 的版本;後綴字為 A 時,表示是 ANSI[3],即 char 的版本;後綴字為 G 時,表示是 Generic 的版本,使用 C++ template 技術寫就;後綴字為 T 時,表示是 explicit generic-text-mapping 的版本,正常來說,generic-text-mapping 的版本無後綴字,但有些時候,我們會需要明確指明其為 generic-text-mapping 的 function[4]
要寫 generic-text-mapping 的 function,我們必須要認清的一點就是,使用我們 library 的程式 (客戶程式,client code),會在 compile-time 的時候,利用我們提供的 macro,自動選擇其中一個版本。因為我們無法確定,client code 會使用哪一個版本,所以我們必須要同時提供 charwchar_t 兩個版本。
這個遲至 client code 的 compile-time 才發揮作用的 macro,存在於提供給 client code 使用的 header file 裡,寫法如下:
int StrToIntA(const char* str);int StrToIntW(const wchar_t* str);#ifdef _UNICODE#   define StrToInt(str) StrToIntW(str)#else#   define StrToInt(str) StrToIntA(str)#endif
這個 macro 很簡單,就是依據 _UNICODE 存在與否,對應到 WA 的版本。後面的文章,筆者就不再重複。
只維護一份程式邏輯
通常來說,我們應該盡可能地讓程式的邏輯,只在一個地方出現,否則,同樣的程式碼有兩份,便會有維護上的麻煩,需得時時確認兩個版本的程式邏輯,是一樣的。
因此,比較好的寫法,是以其中一個版本為主要版本,另外一個版本則即時將參數,轉碼後呼叫主要版本。例如:
bool PathExistA(const char* path){    struct stat sb;    if (stat(path, &sb) == 0) {        return true;    }    else {        switch (errno) {        case ENOENT:            return false;  // no such entry, path not exists        case EACCES:            return true;   // path exists, though we have no right to access it        default:            assert(false); // die for other errors, for demo only        }    }    assert(false); // NOT_REACHED    return false;}

bool PathExistW(const wchar_t* path){    char mbs_path[MAXPATHLEN + 1];    memset(mbs_path, 0, sizeof(mbs_path));    wcstombs(mbs_path, path, sizeof(mbs_path) - 1);    return PathExistA(mbs_path);}
依靠將參數轉碼以便「只維護一份程式邏輯」,有個問題就是,這些「細碎繁多」的轉碼動作,在有些系統上,會造成明顯的執行時間成本。如果速度是很重要的考量的話,您應該小心地調整您的程式的配置方式,盡可能地減少轉碼動作。
另外,不同 OS 所提供的系統 API 也有所不同,好比說 WinNT 是 unicode 核心,若呼叫上例的 PathExistW(), 反而等於是我們自行轉碼後,OS 又會再轉碼回 unicode,多做了無謂的轉碼動作,反而降低了效能。這其實是個兩難的問題,不同的 OS 之系統 API 皆不一樣,WinNT 是 unicode 核心,Win 95/98 是 MBCS 核心,而大部分的 Linux,若 encoding 有影響的話,據我所知,很多都是以 UTF-8 為主。因此,究竟要以哪一種版本為主,端視您的 library 的使用情形而定。
但小心函式行為的正確性
除了效率的問題之外,函式行為的正確性,也應該仔細思量。這點尤其發生在 byte string 與 character string 的差異上。前者是以 byte 為單位所串成的字串,後者則是以 character 為單位所串成的字串。兩者之間,關鍵的差異在於 character 的定義。舉例來說,假使我們用的是 UTF-8,UTF-8 字元的長度,從 1 個 byte 到 6 個 bytes 不等,並非固定是 1 個 byte,因此,strlen(utf8_string) 所抓出來的字串長度,實際上的意義為,「碰到 null byte 前的 byte 數」,而非「碰到 null utf-8 character 前的 character 數。」
所以,若我們要寫計算字串長度的函式,就必須先確定,我們要算的是 character 數,還是 byte 數。如果是要計算 byte 數,而通常來說,一串 byte 我們不會設計成用 null byte 結尾,所以一般我們要算的是 character 數。如此一來,我們應該要把 multi-byte string 先轉碼成 wide-character string,再來使用 wcslen() 計算 character 數,而不能直接使用 strlen() 計算[5],否則會造成函式行為的錯誤。程式寫法如下:
size_t StrLengthW(const wchar_t* str){    return wcslen(str);}

size_t StrLengthA(const char* str){    wchar_t wcs[4096]; // assume big enough, for demo only    memset(wcs, 0, sizeof(wcs));    mbstowcs(wcs, str, ((sizeof(wcs) / sizeof(wcs[0])) - 1));    return StrLengthW(wcs);}
在此例中,我們一改 PathExistW()PathExistA() 為主的作法,改成以 StrLengthW() 為主,為的就是函式行為的正確性。在決定以何者為主,何者為輔時,函式行為的正確性應優先考量,次之才是效率問題。
盡可能地使用系統 API
不過,也有可能,我們的 API 所要提供的功能,有直接的系統 API 對應,且該系統 API 有支援 generic-text-mapping。此時,我們應該盡可能地呼叫系統 API,而捨棄自行轉換(碼)的方式,因為通常這樣執行效率較快。例如:
int StrToIntW(const wchar_t* str){    return _wtoi(str);}

int StrToIntA(const char* str){    return atoi(str);}
由於這樣的函式,通常內容極其簡單,如上例都只有一行而已,故「只維護一份程式邏輯」這句話,我們可以忽略之。
使用 C++ template 自動產生版本
或者,我們寫的是 C++,可以用 template,只寫一份程式邏輯,而讓 compiler 來幫我們自動產生 charwchar_t 的兩個版本。例如:
/** * Find the position of the first occurrence of character @p c in string @p s. * * @param[in] s string to search for @p c * @param[in] c character to search for * @return Returns pointer to the position of the first occurrence of character *         @p c in string @p s.  If character @p c is not found in string @p s, *         returns @c 0. */template <typename CharT>CharT* StrFindCharG(CharT* s, CharT c) // the postfix G stands for Generic.{    while (*s) {        if (*s == c) {            return s;        }        ++s;    }    return 0; // not found.}

wchar_t* StrFindCharW(wchar_t* s, wchar_t c){    return StrFindCharG<wchar_t>(s, c);}

char* StrFindCharA(char* s, char c){    return StrFindCharG<char>(s, c);}
使用 C++ template 確實是比較好的方式,既不會有前述的效率問題,程式邏輯也還是只有一份,沒有 duplicated code 的維護問題。請大家多多投靠 C++ 吧,不要再過茹毛飲血的原始生活了。
使用 C++ template 還有一個好處就是,我們可以利用 specialization、partial specialization 甚至是 policy-based-design 等等技術,僅將會影響效率的部份,予以分開撰寫,而在主要函式裡,維持原本完整的程式邏輯[6]。如此一來,即可兼顧程式的維護性,也可以取得最佳的執行效能,而且這些利用 C++ template 技術所作的 optimization,還可以 reuse 供其他程式使用。
不過,在這裡必須強調的是,通常我們不會讓 template code 露出 DLL 外,因為這樣就失去了使用 DLL 的意義。因此,StrFindCharW()StrFindCharA() 還是有存在的必要,以確保 compiler 有確實產生兩個不同版本的 StrFindCharG()
別忘了 template 產生的是不同的實體,小心 side-effect
但是,正如前面所述,compiler 依據 template 所產生的,是兩份不同的函式實體,因此,我們必須要小心 side-effect 的問題。什麼意思呢?舉例來說,假設我們有個函式,是用來取得 accumulated access count,通常我們會這麼實做:
typedef unsigned int count_t;

count_t GetTagAccessCount(const char* tag){    // lock omitted, for demo only.    static map<string, count_t> tags_count = 0;

    // use map.insert() to ensure the count exist    pair<map<string, count_t>::iterator, bool> r        = tags_count.insert(make_pair(string(tag), 0));    ++(r.first->second);    return (r.first->second);}
但如果配上 generic-text-mapping 的話,char 要代換成 template parameter CharTstring 要代換成使用 CharT 為參數的 basic_string<CharT>,程式就會變成這個樣子:
typedef unsigned int count_t;

template <typename CharT>count_t GetTagAccessCountG(const CharT* tag){    // lock omitted, for demo only.    static map<basic_string<CharT>, count_t> tags_count = 0;

    // use map.insert() to ensure the count exist    pair<map<basic_string<CharT>, count_t>::iterator, bool> r        = tags_count.insert(make_pair(basic_string<CharT>(tag), 0));    ++(r.first->second);    return (r.first->second);}

count_t GetTagAccessCountW(const wchar_t* tag){    return GetTagAccessCountG<wchar_t>(tag);}

count_t GetTagAccessCountA(const char* tag){    return GetTagAccessCountG<char>(tag);}
此時,GetTagAccessCountW()GetTagAccessCountA() 分別 instantiated 各自的 GetTagAccessCountG<>(),template function 裡面的 static 變數 tags_count 便會有兩份。因為 client code 會利用 GetTagAccessCount() 這個 macro 選擇出適用的版本,而我們又無法確保所有的 client code 都是使用 charwchar_t 版本[7],因此他們所使用的 tags_count 是不同的,造成函式的行為錯誤。
碰到這種情況,我們也許必須退而求其次,放棄 template 法,改用「以一個版本為主,另一個版本即時轉碼後呼叫主要版本」的方法來實做。或者,將可能造成 side-effect 的程式片段,以另一個共用的 class 包裝起來之後,讓 template 使用。以我來說,會選擇使用第二種方法,C++ 強大的彈性,不好好利用,實在是太可惜了。
除了程式邏輯只要保留一份就好以外,最好文件也只有一份,但是...
筆者慣用的文件產生工具是 doxygen,doxygen 是一個強大的文件產生工具,讓我們得以使用如 javadoc 的方法,產生 C、C++、Java、PHP、Python、... 等語言的 API 文件。然而,使用 generic-text-mapping 技法時,doxygen 產生的文件,其長相卻不太理想。
理想上,最好是如 MSDN 的作法一般,將 StrLength()StrLengthA()StrLengthW() 甚至是 StrLengthT()StrLengthG() 等函式,集結於同一個頁面描述,並以 StrLength() 為名;至少,要做到把其他版本濾除掉,只做 StrLength() 即可。可是,因為 doxygen 實在是太聰明了,能夠看穿 StrLength() 實際上不是 function,而是 macro,所以我們根本無法把 StrLength() 的文件,作成像 function 一樣,並歸類在 function 類。
我們可以使用 @copydoc[8],讓 StrLengthA() 使用 StrLengthW() 的那一份文件源碼。但是,使 StrLength() 為主,並以 function 的方式呈現,筆者就真的找不到解法了。因此,目前筆者所能想出來的,最好的 doxygen 文件寫法如下:
/** * Count the length of given string @p str, in number of characters. * * @param[in] str string to count length * @return Returns the length, number of characters, of string @p str. */size_t StrLengthA(const char* str);

/** * @copydoc StrLengthA * @note This is the @c wchar_t version of StrLengthA(). */size_t StrLengthW(const wchar_t* str);


/** * @see Expand to StrLengthA() or StrLengthW() according to current *      generic-text-mapping configuration. * @hideinitializer */#if (defined(UNICODE) || defined(_UNICODE))#   define StrLength StrLengthW#else#   define StrLength StrLengthA#endif
由於我們根本不需要讓 StrLength() 如 macro 般呈現,且 doxygen 會做 C/C++ pre-processing,根據設定,會在文件上顯示 StrLength 會被展開成為 StrLengthW 或是 StrLengthA,因此使用 @hideinitializer 把 macro 的右邊隱藏起來。
總之,上面的寫法,是筆者目前可以想到的最好寫法,如果有人有更好的解法,筆者很想知道。要不然,看到這樣的文件,實在是覺得太醜了啊。
總結
本文介紹了在撰寫具備 generic-text-mapping 的 library 時,可以使用的手法,應該要注意的事項。為了「只維護一份程式邏輯」,我們可以有許多種的作法,如「以一個版本為主,另一個版本即時轉碼後呼叫主要版 本」,或是使用 C++ 的 template 語法。然而,我們也必須要注意到函式行為正確性的問題、程式的效率,以及可能因 side-effect 而存在的潛藏危機。最後,敬祝大家 happy coding,程式只寫一套最好。

  1. 既然都要用 generic-text-mapping 了,就表示知道了 i18n 的重要;而既然知道 i18n 很重要了,幹嘛還留個洞,留下使用 single-byte-character 的機會,而且還是預設值。
  2. The Code Project 的這篇《Unicode, MBCS and Generic text mappings》,有非常詳細的講解。
  3. 這個名字有其歷史包袱在,嘆。請參見《轉換 BIG5、GB2312、UTF-8、Unicode 與 wchar_t》這篇裡,「轉換工具:Windows API」這一段的說明。
  4. 例如會依據目前的 TCHAR 選擇是否要呼叫 mbstowcs()TcsToWcs() 這類的函式。這類函式,筆者將另行撰文說明。
  5. 實際上,C99 有定義 mbslen() 可用,不過我沒有測過 mbslen() 與先轉碼再 wcslen() 的速度何者較快就是了。
  6. 相關技巧有機會的話,筆者再另行撰文說明之。
  7. 想像一下不同參數編譯出來的 DLL,都來呼叫 GetTagAccessCount() 的情況。
  8. 筆者還是習慣使用如 javadoc 一般以 @ 開頭的文件指令,而不是 Doxygen/Qt 用的 \。這純粹是個人美感作祟的緣故。

【心得】Generic Text Mapping

http://www.programmer-club.com.tw/ShowSameTitleN/vcdotnet/2115.html

【心得】Generic Text Mapping


【一個常見的錯誤與錯誤的解決方法】

很多初學者從 VC++ 6.0 升級到 VC++ 2005 的時候, 經常發現到原本在 VC++ 6.0 版順利編譯並執行的程式突然無法編譯了. 像以下這個簡單的例子:

char ch[80];
CString str("Hello World");
strcpy(ch, str);

當用在新產生的 VC++ 2005 專案裡的時候, VC++ 2005 會發出類似以下的錯誤訊息:

error C2664: 'strcpy' : cannot convert parameter 2 from 'CString' to 'const char *'

有些初學者不明白這個錯誤訊息的原因, 也不去深入研究, 認為 typecasting 可以解決這個問題, 就做了這個更改:

strcpy(ch, (const char*)(LPCTSTR)str);

改了之後, 錯誤沒有了, 取而代之的是個警告:

warning C4996: 'strcpy' was declared deprecated

建議程式員應該用 strcpy_s() 這個安全的函式. 這個暫且不管它, 至少程式是可以編譯了.

但 typecasting 有解決到問題嗎? 答案是沒有, 上面的 typecast 叫編譯器不出聲, 把真正的問題隱藏了起來.

寫 C 程式幾乎完全沒有必要用到 typecast. 而大部份的 C++ 程式也不需要用到 typecast. 初學者的程式用到 typecast 幾乎等同於有潛在的錯誤或邏輯上的問題.


【編譯器的字集設定】

不管那個版本, VC++ 一共有三種字集的設定: ANSI, MBCS, 及 Unicode. 前兩者合稱「非-Unicode」設定.

C 及 C++ 語言有兩種字元類型, 一個就是我們常用的 char, 另一個是 wchar_t. 為了區分這兩者, 一般把 char 稱為「窄字元」, 把 wchar_t 稱為「寬字元」. 從程式語言的角度來看, char 跟 wchar_t 純粹是語言的類型, 跟 ASCII 或 Unicode 是兩回事. ASCII 及 Unicode 是編碼, 跟類型無關.

在 VC++ 裡, char 的長度是 8 位元, 使用 ASCII 編碼. wchar_t 的長度是 16 位元, 使用 Unicode 編碼.

【視窗系統的 API】

在視窗系統上, 大多數有字串或字元作參數的 API (如 MessageBox()) 都是有兩個版本的. 一個是窄字元版, 一個是寬字元版. 實際上, 視窗系統裡根本沒有 MessageBox() 這個 API, 它有的是 MessageBoxA() 及 MessageBoxW() 這兩個版本的 API.

以 MessageBox() 為例, 這兩個版本的宣告如下:

MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);

xxxA() 用的是 LPCSTR, 既 const char*, 一個「窄字元指標」.
xxxW() 用的是 LPWSTR, 既 const wchar_t*, 一個「寬字元指標」.

同時, 視窗的頭檔裡也有這個定義:

#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif

所 以當專案的字集設定是 Unicode 時, VC++ 會自動的設定兩個 preprocessor 的符號: UNICODE 及 _UNICODE. 在編譯時, preprocessor 會自動把 MessageBox() 變成 MessageBoxW(), 所呼叫的是 Unicode 的版本. 反之, 則呼叫 MessageBoxA(), 一個 非-Unicode 的版本.

MFC 的 CString 也是這樣子的. 在 Unicode 設定的專案下, 在編譯時, CString 會變成 CStringW, 一個寬字元的 CString; 如果是在 非-Unicode 的設定下, CString 則變成 CStringA, 一個窄字元的 CString.

從這個角度來看, MessageBox() 跟 CString 是「中性」的. 它可以變成「寬」, 也可以變成「窄」, 視專案的字集定義而定.

所以在使用 MessageBox() 或 CString 時, 要留意到專案的字集設定. 因為如果字集設定跟參數的類型不符, 如呼叫 MessageBoxW() 但傳給它的是窄字元, 那編譯器當然會發出錯誤訊息.

從這裡我們就可以知道上面的程式在 VC++ 6.0 可以正常編譯, 但在 2005 的專案裡卻發生錯誤的原因:
1. 因為程式裡「寬/窄」混合在一起,
2. 專案對字集的預設在 6.0 版跟 2005 版不同.

在 6.0 版上, 專案對「字集」的預設是 MBCS (Multi-Byte Character Set). 在 2005 版上, 專案對「字集」的預設是 Unicode.

混合式的寫法, 在轉換字集設定時一定會遇到問題. 所以這種寫法要避免.


【純「窄」或「寬」的程式】

當然我們可以不用管編譯器字集的設定, 而全用單一「寬」或「窄」的類型, 函式來寫我們的程式. 上面的例子用窄字元來寫的話是這樣子的:

char ch[80];
CStringA str("Hello World");
strcpy(ch, str);


用寬字元則是:

wchar_t ch[80];
CStringW str(L"Hello World");
wcscpy(ch, str);

wcscpy() 是 strcpy() 的「寬」版本.

但 是如果我們要用同一個專案來產生 Unicode 及 非-Unicode 的執行檔, 用這種比較極端的寫法就不是很方便. 同時, 大部份人都熟悉 CString 及 MessageBox() 而不是 CStringW 或 MessageBoxA(). 另一點, MSDN 上的資料大部份是 MessageBox() 而不是 MessageBoxA(). 原因很簡單, Microsoft 希望我們用「中性」的寫法而不是寬或窄的寫法.


【Generic Text Mapping】

像 MessageBox() 或 CString 這樣的函式定義或類型, 會依據專案字集的設定而自動選擇寬或窄版本的機制, 在視窗上有個專有名詞, 叫 "Generic Text Function" 或 "Generic Text Type". 因為它們是根據專案裡字集的設定來 map 到寬或窄的真實函式或類型, 它們也叫 "Generic Text Mapping".

同樣的, C 及 C++ 函式庫裡以字元, 字串為參數的函式, 結構也有兩個版本. 本文一開始例子裡的 strcpy() 是個「窄字元」版本的函式. 它相對的「寬字元」版是 wcscpy().


【C 標準函式庫】

雖 然語言標準並沒有提供 generic text mapping 的機制, 但 Windows 的頭檔 <tchar.h> 有提供 C 函式庫的 generic text mapping. strcpy()/wcscpy() 的 generic text function 是 _tcscpy(), 它是這樣定義的:

#ifdef UNICODE
#define _tcscpy wcscpy
#else
#define _tcscpy strcpy
#endif

該頭檔也定義了一系列的 generic text 類型:

http://msdn2.microsoft.com/en-us/library/se784sk6(VS.80).aspx

常數:

http://msdn2.microsoft.com/en-us/library/z94kex77(VS.80).aspx

函式:

http://msdn2.microsoft.com/en-us/library/tsbaswba(VS.80).aspx


所以除了之前所講的「全寬」, 「全窄」的寫法外, 多了一個叫「全 generic text mapping」的寫法:

_TCHAR ch[80];
CString str(_T("Hello World"));
_tcscpy(ch, str);

這也是微軟建議的做法. 因為在 Unicode 的專案設定下, preprocessor 將它們變成:

wchar_t ch[80];
CStringW str(L"Hello World");
wcscpy(ch, str);

而在 非-Unicode 專案的設定下, preprocessor 後, 編譯器所看到的是:

char ch[80];
CStringA str("Hello World");
strcpy(ch, str);

這樣同一個專案就可以以 Unicode 或 非-Unicode 來產生兩種不同的執行檔.

當然, 只要明白這個原理, 程式員並不一定要用 generic text 的做法. 如果專案只用單一種字集, 那混合式的寫法也是可以的.


【C++ 標準函式庫】

可惜的是微軟並沒有提供 C++ 的 generic text mapping. 如果你不想自己寫的話, 這裡有一個可以用:

http://www.codeproject.com/string/tstl_h.asp



【「寬」-「窄」之間的轉換】

並不是每個 API 都是用 generic text mapping 的, 有些 API 只有寬字元版本. 所以有時, 寬-窄字元/字串之間的轉換有其必要.

視窗有寬-窄字串轉換的 API - MultiByteToWideChar() 及 WideCharToMultiByte().

MFC/ATL 的使用者有一系列非常簡單的 macro 可以用 (頭檔是 <atlconv.h>):

http://msdn2.microsoft.com/en-us/library/87zae4a3(VS.80).aspx

上面的 A2W(), W2T() 等自 6.0 起就有了, 但它們有潛在的 stack overflow 的問題. 7.0 版的 ATL 用了新的 class 來做, 更正了之前的問題:

http://msdn2.microsoft.com/en-us/library/awt7k7f5(VS.80).aspx

Rules of memory management of openCV

Original page: http://www.andol.info/hci/993.htm

Rules of memory management of openCV
After several weeks’ hard working, finally, the memory leaking problem is fixed.
In the last post which listed nearly all kinds of variaties declare and delete methods, here i add several tips for memory check while it is leaking:
1) int, double, float … : these kinds of variaties don’t matter so much;
2) IplImage* : this really matters, especially when cvCloneImage is conducted, if the destination variaty was not cleared, the memory would fly up.
3) CvMemStorage: be careful, to delete this data you have to use not ‘cvReleaseData’ but ‘cvReleaseMemStorage’, one declaration, one delete operation.

OpenCV memory leaking management in C/C++

Original page: http://www.andol.info/hci/963.htm

OpenCV memory leaking management in C/C++

If you’re new to OpenCV, you need to know exactly how to manage all the huge amounts of memory you’re using. C/C++ isn’t a garbage collected language (like Java), so you need to manually release memory as soon as its use is over. If you don’t, your program could use up hundreds of MBs of highly valuable RAM… and often even crash (out-of-memory errors?)
It can be a daunting task to hunt exactly where memory needs to be released. So I’ve compiled this short list of places where you should look out for memory leaks.
Create it, then Release it
 
If you create something, make sure you release it before “returning”. This is probably the very first thing you should check when fixing memory leak problems with OpenCV. For example, if you do a cvCreateImage, make sure you do a cvReleaseImage. There are many things you can create. Here are some functions that “create” and their corresponding “release” functions
cvCreateImage cvReleaseImage
cvCreateImageHeader cvReleaseImageHeader
cvCreateMat cvReleaseMat
cvCreateMatND cvReleaseMatND
cvCreateData cvReleaseData
cvCreateSparseMat cvReleaseSparseMat
cvCreateMemStorage cvReleaseMemStorage
cvCreateGraphScanner cvReleaseGraphScanner
cvOpenFileStorage cvReleaseFileStorage
cvAlloc cvFree

One warning though: If you create something and want to return it, don’t release it. Lets say a function that creates a checkerboard image and returns it. If you release the image before returning it, you’re freeing all memory that stores the image data. And when you try accessing memory that isn’t yours, you get a crash.
Release returned structures
This is the second thing you should check for. Often, once you return a structure (say, an image).. you forget about it.
Multiple Memory Allocations
This is the third thing you should check for: Allocating memory, and then changing the pointer itself. Here’s some example code:
IplImage* image = cvCreateImage(whatever);
image = CreateCheckerBoard(whatever);

cvReleaseImage(&image);
This function creates a memory leak. First, you allocate some memory for image. Then, you call the function CreateCheckerBoard. This function itself creates new memory. And image now points to this new memory. The memory created in the first step is lost forever. No variable points to it. A memory leak. To fix this, you need to modify the code like this:
IplImage* image = NULL;
image = CreateCheckerBoard(whatever);

cvReleaseImage(&image);
If you return a sequence, release its storage
There are many instances where you use the CvSeq data structure. And often you might want to return this structure for further use. If you release its storage (a CvMemStorage structure) within the function itself, you’d free the memory where the sequence is stored. And then you’d try and access it in the calling function. Again, crash.
A temporary fix would be to just erasing the cvReleaseMemStorage statement… but that would mean lots of memory.
To fix this, you don’t release the memory in the function itself. You release it in the calling function like this:
cvReleaseMemStorage(&thesequence->storage);
storage is a member of the CvMemStorage structure that always points to the memory where its stored.
Again, this is just an example. There are more structures where a similar situation could arise.
Dependence on other structures
I quite recently discovered this memory leak. To explain this, I’ll use an example: Lets say you find out the contours of an image. OpenCV would return a “linked list” type structure calledCvSeq. You decide to access the third element of this linked list. OpenCV returns a pointer to the third element. All going great till this moment.
Now you decide to save all the points of this contour (the third element) in a data structure of your own. Since this is an array of points, you do something like:
mystructure->points = thirdcontour->points;
You set the pointer to equal to the thirdcontour. This is the bug. If you release the storage of the sequence (which you should), mystructure has a bad pointer. To fix this, allocate new memory to mystructure->points and then copy contents ofthirdcontour->points… something like this:

mystructure->points = (CvPoint*)malloc(sizeof(CvPoint) * thirdcontour->total);
memcpy(mystructure->points,thirdcontour->points,sizeof(CvPoint)*thirdcontour->total);
This creates new memory for your structure and then copies each element there. Once you’ve done this, you can release the storage of the sequence without fear.

Visual C++中調用DLL

摘 要:介紹了動態鏈接庫這種模塊復用方法及在VC中對它的調用,並給出了一個通過復用來實現數據加密的具體實例。

關鍵詞:VC DLL 模塊復用 數據加密

引言

模塊化思想貫穿於軟件工程各個發展階段,模塊復用是構建大系統的一種重要思想。模塊復用方法有:函數、函數庫、動態鏈接庫、COM。其都是基於模塊化的 基本思想。函數是最簡單的模塊化思想,也是後面方法的基礎,甚至是一個應用程序的基礎。函數庫是函數的組合,一般將一些功能相似的函數放在一起作為函數 庫,這種函數庫通常叫做靜態庫,其鏈接方式是靜態的。COM即組件對象模型,是一種集成技術,可以使程序在運行時把各種不相關的軟件程序混合在一起,而不 必考慮這些不相關的程序是用什麼語言編寫的,它也是一種標準或者稱為協議,負責將一個軟件模塊和另一個軟件連接起來。動態鏈接庫DLL(Dynamic Link Library)是一個可以被其它應用程序共享的程序模塊,其中封裝了一些可以被共享的例程和資源,其鏈接方式是動態的。動態鏈接庫文件的擴展名一般是 dll,也有可能是fon、sys和dry,它和可執行文件(.exe)非常相似,區別在於DLL中雖然包含了可執行代碼卻不能單獨執行,而應由 Windows應用程序直接或間接調用。Windows操作系統包含大量動態鏈接庫,其中最主要的是KERNEL32.DLL、USER32.DLL、 GDI32.DLL 。

DLL的調用

調用DLL,首先需要將DLL文件映像到用戶進程的地址空間中,然後才能進行函數調用,這個函數和進程內部一般函數的調用方法相同。Windows提供了兩種將DLL映像到進程地址空間的方法:

1、隱式的加載時鏈接

這種方法需要DLL工程經編譯產生的LIB文件,此文件中包含了DLL允許應用程序調用的所有函數的列表,當鏈接器發現應用程序調用了LIB文件列出的 某個函數,就會在應用程序的可執行文件的文件映像中加入一些信息,這些信息指出了包含這個函數的DLL文件的名字。當這個應用程序運行時,也就是它的可執 行文件被操作系統產生映像文件時,系統會查看這個映像文件中關於DLL的信息,然後將這個DLL文件映像到進程的地址空間。

系統通過DLL文件的名稱,試圖加載這個文件到進程地址空間時,它尋找DLL 文件的路徑按照先後順序如下:

·程序運行時的目錄,即可執行文件所在的目錄;

·當前程序工作目錄

·系統目錄:對於Windows95/98來說,可以調用GetSystemDirectory函數來得到,對於WindowsNT/2000來說,指 的是32位Windows的系統目錄,也可以調用GetSystemDirectory函數來得到,得到的值為SYSTEM32。

·Windows目錄

·列在PATH環境變量中的所有目錄

VC中加載DLL的LIB文件的方法有以下三種:

①LIB文件直接加入到工程文件列表中

在VC中打開File View一頁,選中工程名,單擊鼠標右鍵,然後選中"Add Files to Project"菜單,在彈出的文件對話框中選中要加入DLL的LIB文件即可。

②設置工程的 Project Settings來加載DLL的LIB文件

打開工程的 Project Settings菜單,選中Link,然後在Object/library modules下的文本框中輸入DLL的LIB文件。

③通過程序代碼的方式

加入預編譯指令#pragma comment (lib,"*.lib"),這種方法優點是可以利用條件預編譯指令鏈接不同版本的LIB文件。因為,在Debug方式下,產生的LIB文件是Debug 版本,如Regd.lib;在Release方式下,產生的LIB文件是Release版本,如Regr.lib。

當應用程序對DLL的LIB文件加載後,還需要把DLL對應的頭文件(*.h)包含到其中,在這個頭文件中給出了DLL中定義的函數原型,然後聲明。

2、顯式的運行時鏈接

隱式鏈接雖然實現較簡單,但除了必須的*.dll文件外還需要DLL的*.h文件和*.lib文件,在那些只提供*.dll文件的場合就無法使用,而只 能採用顯式鏈接的方式。這種方式通過調用API函數來完成對DLL的加載與卸載,其能更加有效地使用內存,在編寫大型應用程序時往往採用此方式。這種方法 編程具體實現步驟如下:

①使用Windows API函數Load Library或者MFC提供的AfxLoadLibrary將DLL模塊映像到進程的內存空間,對DLL模塊進行動態加載。

②使用GetProcAddress函數得到要調用DLL中的函數的指針。

③不用DLL時,用Free Library函數或者AfxFreeLibrary函數從進程的地址空間顯式卸載DLL。


http://dev.xuezhishi.net/program/VC/2007-06-20/17543.html