聲明一下,寫下這些編程技巧,即不是什么祖?zhèn)髅丶,也不是什么必殺招或絕招,在此只為方便同仁們?cè)诰幊炭刂栖浖䲡r(shí),對(duì)此可以進(jìn)行適當(dāng)?shù)恼遄。以下展現(xiàn)的編程思想及奉上的源代碼都非常簡(jiǎn)易,但并不是隨手寫寫,可都是經(jīng)過實(shí)踐的。若沒有成功經(jīng)驗(yàn)作后盾,我也就沒有必要在此打字練五筆了。 事實(shí)上,正如一個(gè)編程大師所言( Michael Abrash ),當(dāng)你的軟件正常而且有效率的運(yùn)行起來時(shí),好像一切都是那么顯而易見。故,在此,我仍堅(jiān)持那句編程口號(hào),將事情變得越簡(jiǎn)單越好,越簡(jiǎn)單就越有效率,越穩(wěn)定。 在以下的介紹中,我將盡可能的展示本人的編程思想,最大可能的給出知其然也知其所然的解釋。若你有更好的見解,希望能得到你的指正。人長(zhǎng)大了明顯標(biāo)志就是變得不太負(fù)責(zé),而且不敢承認(rèn)自己還需要努力,害怕面對(duì)自己的錯(cuò)誤。若是這樣,放心,我還沒長(zhǎng)大。因?yàn)槲覠o法保證我能面面俱到。 關(guān)于源代碼的閱讀,需要讀者有一定的 C++ 編程基礎(chǔ),至少對(duì)以下表示形式不會(huì)產(chǎn)生誤解: const char *pString; // 指定 pString 邦定的數(shù)據(jù)不能被修改 char * const pString; // 指定 pString 的地址不能被修改 const char * const pString; // 含上面兩種指定功能 當(dāng)然,隨便提醒一下,這些源代碼若需要加入你的軟件工程當(dāng)中,還需要作一些調(diào)整和修改,因此,這些源代碼實(shí)質(zhì)上稱為偽代碼也可以,之所以展現(xiàn)它們,是讓程序員們有個(gè)可視化的快感,特別是那些認(rèn)為源代碼就是一切的程序員。 同時(shí),為了提高針對(duì)性,大部分控制卡調(diào)用的函數(shù)會(huì)明確指出是邦定哪些卡的,實(shí)際應(yīng)用時(shí),程序員可自行選擇,以體現(xiàn)一下自己的智商是可以寫寫軟件的。 留個(gè)電話: 0755-26434329 E_mail: support@leisai.com 有更濃興趣的上這個(gè) QQ 號(hào)嗎也行: 372161225 一、 控制卡類的單一實(shí)例實(shí)現(xiàn) 把控制卡類作一個(gè)類來處理,幾乎所有 C++ 程序員都為舉雙手表示贊同,故第一個(gè)什么都沒有的偽代碼就此產(chǎn)生,如下表現(xiàn): class CCtrlCard { public: …Function public: …attrib } 于是,用這個(gè) CctrlCard 可以產(chǎn)生 n 多個(gè)控制卡實(shí)例,只要內(nèi)存足夠。然而,針對(duì)現(xiàn)實(shí)世界,情況并不那么美好。通常情況下, PC 機(jī)內(nèi)只插同種類型的控制卡 1 到 2 張,在通過調(diào)用 d1000_board_init 或 d3000_board_init 函數(shù)時(shí),它們會(huì)負(fù)責(zé)返回有效卡數(shù) nCards ,然后從 0-nCards*4 - 1 自行按排好軸數(shù)。初始化函數(shù)就是 C++ 的 new 或 malloc 的操作,取得系統(tǒng)的資源,但是控制卡的資源與內(nèi)存不一樣,取得資源后必需要釋放才可以再次獲取,即控制卡資源是唯一的。 既然控制卡資源是唯一的,那么最好 Cctrlcard 產(chǎn)生的實(shí)例也是唯一的,這樣,我們可以方便的需要定義一個(gè)全局變量即可 : CctrlCard g_Dmcard; 在其它需要調(diào)用的地方,進(jìn)行外部呼叫: extern CctrlCard g_DmcCard; 以上方法實(shí)在太簡(jiǎn)單了,很多人都會(huì)開心起來。實(shí)質(zhì)上,方法還有很多,即然可以產(chǎn)生 n 多對(duì)實(shí)例,我們的核心是只要保證調(diào)用 board_init 函數(shù)一次即可,故也可以單獨(dú)定義一個(gè) InitBoard 函數(shù): class CctrlCard { public: static int InitBoard(); // 定義一個(gè)靜態(tài)函數(shù),以表警示 } int CctrlCard::InitBoard() { return d1000_board_init(); } 還有一種方法,情況稍加復(fù)雜,但表達(dá)的功能也要強(qiáng)一些,以下展現(xiàn)可以稍微安慰一下代碼狂。 Class CctrlCard { public: CctrlCard(); // 請(qǐng)注意這個(gè)構(gòu)造函數(shù)的定義 } CctrlCard::CctrlCard() {// 呵呵,也很明了 static int n(0); // 注意,是個(gè)靜態(tài)變量 n++; // 每次調(diào)用 CctrlCard 生成實(shí)例時(shí),都會(huì)計(jì)數(shù)一次 assert( n == 1 ); // 在 DEBUG 版本下,只有 n==1 的情況下可以通過 // 否則,會(huì)出現(xiàn)致命錯(cuò)誤,還好,它會(huì)告訴你錯(cuò)在哪個(gè)文件, // 哪一行,呵呵,是個(gè)好東東啊。 } 通過強(qiáng)行報(bào)警處理,當(dāng)你有 g_DmcCard 這個(gè)實(shí)例時(shí),其它的所有控制卡的定義都只能是以引用或指針的方式進(jìn)行了,不會(huì)再產(chǎn)生新有效的實(shí)例了,對(duì)于由小組編程的項(xiàng)目軟件,而你又恰好負(fù)責(zé)編程控制卡這一塊的話,以上的顯性報(bào)警,會(huì)讓其它人心領(lǐng)神會(huì)。當(dāng)然,你也可以將上面的方法加入到 InitBoard 當(dāng)中去,可以避你的無意識(shí)的多次調(diào)用了。 附:無意識(shí)的多次調(diào)用經(jīng)常發(fā)生,特別是那些對(duì) MFC 機(jī)制不明確的程序員,在多文檔框架下,不知道這個(gè) CctrlCard::InitBoard 函數(shù)到底是應(yīng)該放在 CmainFrame 的 OnCreate 里面,還是應(yīng)該放在 CchildFrame 的 OnCreate ,或者是 Cview 的 OnInitUpdate 里面進(jìn)行調(diào)用。 在一言難盡 MFC 的情況下,我建議兩個(gè)小方法: No.1 將 CctrlCard 的函數(shù)置于 Cmainframe 的 OnCreate 或者 Capp::Initstance 內(nèi)調(diào)用 No.2 將 InitBoard 函數(shù)稍加改造成這樣: Int CctrlCard::InitBoard() { static int n(-1000);// 注意, -1000 是控制卡函數(shù)不可能返回的值 if( n == -1000 ) n = d1000_board_init(); return n;// 這樣,即使多次調(diào)用也不樣怕了,呵呵,雕蟲小技也可以除蟲啊 } 必須額外聲明一下,我們不是不重視資源的釋放,而是作為一個(gè) C++ 程序員寫下這些代碼是基本的義務(wù)(這也是我為什么要交待讀者必須要有一定的 C++ 基礎(chǔ)): class CctrlCard { public: ~CctrlCard() {// 定義析構(gòu)函數(shù),在此釋放資源,對(duì)此,我不想再轉(zhuǎn)到讀者的眼球了 d1000_board_close(); } } 二、 數(shù)據(jù)結(jié)構(gòu)及數(shù)據(jù)類型的定義,部分相關(guān)聲明 調(diào)用控制卡驅(qū)動(dòng)函數(shù)時(shí),經(jīng)常會(huì)有如下形式: 單軸相對(duì)運(yùn)動(dòng) d1000_start_t_move( axis, pulse, start, speed, accel ); 單軸絕對(duì)運(yùn)動(dòng) d1000_start_ta_move( axis, pulse, start, speed, accel ); 兩軸相對(duì)插補(bǔ) d1000_start_t_line( axisArray, distArray, start, speed, accel ); 兩軸絕對(duì)插補(bǔ) d1000_start_ta_line( axisArray, distArray, start, speed, accel ); 圓弧相對(duì)插補(bǔ) d3000_start_t_arc( axisArray, C1, C2, E1,E2, dir, start, speed, accel ); 圓弧絕對(duì)插補(bǔ) d3000_start_ta_arc( axisArray, C1, C2, E1,E2, dir, start, speed, accel ); 以上的調(diào)用,很多重復(fù)枯燥,又不直觀,難于理解,并且在面向客戶時(shí),常常是指每分多少米,或者每秒多少毫米,很少有人問每秒多少脈沖,移動(dòng)多少脈沖作距離,故需要單位之間的換算。顯然,對(duì)于這些問題,我想,C++程序員應(yīng)該找到用武之地了,所以我們一步一步來,慢慢統(tǒng)一各個(gè)問題。實(shí)質(zhì)上,在以下的幾個(gè)技巧,也需要在此澄清一些概念。 我們先來幾個(gè)宏定義提高一下情緒: # define MAX_AXIS 4 // 最多軸數(shù) # define XCH 0 // 定義X軸的值 # define YCH 1 # define ZCH 2 # define UCH 3 …..( 其它以次類推 ) # define M_ABS 0x01 // 定義一個(gè)絕對(duì)標(biāo)志位 # define M_INP 0x02 // 定義一個(gè)插補(bǔ)位 接下來深入一點(diǎn)點(diǎn),再來幾個(gè)結(jié)構(gòu)定義: typedef struct tag_ARC { tag_ARC( double ox=0.0, double oy=0.0, double ex=0.0, double ey=0.0, int dir=0 ): ox(ox), oy(oy), ex(ex), ey(ey), dir(dir)// 定義這樣一個(gè)構(gòu)造函數(shù)需要勇氣,看似不合理,但是好用麻 { } double ox,oy; double ex,ey; int dir; }ARC; typedef struct tag_SPEED { tag_SPEED( double start=0.0, double speed=0.0, double accel=0.0, double decel=0.0, double scc=0.0 ) : start(start), speed(speed), accel(accel), decel(decel), scc(scc) { } double start; double speed; double accel; double decel; double scc; }SPEED; 以上兩個(gè) ARC 和 SPEED 的結(jié)構(gòu)定義,把幾個(gè)參數(shù)變成一個(gè)參數(shù)。比如要實(shí)現(xiàn)的單軸驅(qū)動(dòng)函數(shù),就變得非常明了: void Move( int nAxis, double fMM, const SPEED &speed, int nFlag = M_ABS );// 往后我們?cè)倬唧w完善其實(shí)現(xiàn)。 以上的結(jié)構(gòu)具有類的特性,但是由于其每個(gè)成員都可以給外部直接使用,故就不需要什么類的 public 及其析構(gòu)函數(shù)的定義了。之所以全都采用 double 的數(shù)據(jù)類型,是面向客戶習(xí)慣及單位計(jì)算方便的。 接下來是對(duì)控制卡常用的單位計(jì)算及部分常用變量的聲明: class Cctrlcard { public: …( 其它略去 ) public: // 屬性 mutable int ORGIN; // 指定原點(diǎn)狀態(tài)位 mutable int LIMIT_A, LIMIT_B; // 指定左右限位狀態(tài)位 private: // 以下的屬性不給外部訪問的 struct tag_AXIS{// 單軸屬性 double fUnitPM; // 脈沖當(dāng)量 long nRP; // 每轉(zhuǎn)脈沖數(shù) double fJourey; // 行程 }; tag_AXIS m_axis[MAX_AXIS]; }; 定義 ORGIN , LIMIT_A, LIMIT_B 為變量,是有兩個(gè)意義: No.1 當(dāng)你訪問它們的狀態(tài)時(shí),不需要每次調(diào)用 d1000_get_axis_status 函數(shù),你可以這樣: Int nStatus = d1000_get_axis( XCH ); If( nStatus & g_DmcCard.ORGIN == g_dmcCard.ORGIN ) If( nStatus & g_DmcCard.LIMIT_A == g_DmcCard.LIMIT_A ) If( nStatus & g_DmcCard.LIMIT_B == g_DmcCard.LIMIT_B ); No.2 你可以擴(kuò)展不同的卡,當(dāng)外部調(diào)用的程序邏輯已被確定時(shí),當(dāng)你需要從 DMC1000 控制卡升級(jí)到 DMC3000 控制卡時(shí),只需要給 ORGIN 等狀態(tài)位指定不同的值即可。指定狀態(tài)位的值也有一個(gè)小小的技巧,以 ORGIN 為例,在 DMC1000 控制卡,其位值在 2 位,則可以這樣: ORGIN = 1<<2; 在 DMC3000 控制卡,其值在第 9 位,則這樣: ORGIN = 1<<9; 方法都很簡(jiǎn)單,關(guān)鍵是要想得到。 對(duì)于 tag_AXIS 定義,引出幾個(gè)函數(shù)的聲明,專門為其服務(wù): void SetUP( nit nAxis, double fMM, double nPulse, double fMax );// 設(shè)定當(dāng)量 double P2M ( int nAxis, long nPulse ); // 脈沖轉(zhuǎn)成毫米 pulse to metric long M2P( int nAxis, double fMM ); // 毫米轉(zhuǎn)成脈沖 mitric to pulse 現(xiàn)在,我們?cè)倩剡^頭來完成 Move 函數(shù)的實(shí)現(xiàn),以便獲得一點(diǎn)點(diǎn)成就感,同時(shí)也展示一下以上的大堆表述是有其意義的。 void Move( int nAxis, double fMM, const SPEED &speed, int nFlag = M_ABS ) { ( nFlag & M_ABS == M_ABS ) ? d1000_start_ta_move( nAxis, // 絕對(duì) M2P( nAxis, fMM), M2P( nAxis, speed.start ), M2P( nAxis, speed.speed), Speed.accel ): // 注意是冒號(hào), ?: 是一個(gè)表達(dá)式 d1000_start_t_move( nAxis, // 相對(duì) M2P( nAxis, fMM), M2P( nAxis, speed.start ), M2P( nAxis, speed.speed), Speed.accel ); } 是不是很簡(jiǎn)單呢,當(dāng)外部調(diào)用時(shí),客戶的觀念就直接面對(duì) Metric 即可,如: Move( XCH, 10.0, SPEED(5,10,0.1), M_ABS );// 達(dá)到絕對(duì)位置 10.0 毫米處。 以上羅嗦了一大堆,對(duì)于剛開始 C++ 編程的程序員來說應(yīng)該是收益不小,對(duì)于高手,則希望能夠體會(huì)一下我的良苦用心。在以下的技巧介紹當(dāng)中,我將變得很簡(jiǎn)易。一般來講,程序員的基礎(chǔ)不是太差的話,至少能夠在 1 分鐘內(nèi)明白是什么道理。