• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 24,  comments - 62,  trackbacks - 0

            QQ在許多公司內(nèi)部被禁止使用,為了能使用QQ,稍微懂點兒計算機的人都知道用代理。QQ提供了socket和http代理這兩種功能,socket代理功能強大,但一般公司對外允許連接的端口號比較有限,難以利用。大多數(shù)公司是允許連接外部的80端口的,這樣使用QQ的http代理是可行的。但是找到能用的QQ代理有點兒麻煩,因此下面的Python代碼提供了自動進行QQ代理驗證的功能。

            import urllib2
            import socket
            import re
            
            f = urllib2.urlopen('http://www.proxycn.com/html_proxy/http-1.html')
            content = f.read()
            f.close()
            ipPattern = re.compile(r'(\d+\.\d+\.\d+\.\d+):80')
            ipList = ipPattern.findall(content)
            print ipList
            requestData = "CONNECT http.tencent.com:443 HTTP/1.1\x0d\x0a"
            requestData += "Accept: */*\x0d\x0aContent-Type: text/html\x0d\x0a"
            requestData += "Proxy-Connection: Keep-Alive\x0d\x0a"
            requestData += "Content-length: 0\x0d\x0a\x0d\x0a"
            for ip in ipList:
            
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    s.connect((ip,80))
                    s.send(requestData)
                    data = s.recv(64)
                    if data.find("200 Connection established")!= -1:
                        print ip, 'good'
                        # A vailable proxy is found once, then exit the program
                        s.close
                        exit(0)
                    else:
                        print ip, 'bad'
                except socket.error:
                    print ip, 'dead'
                finally:
                    s.close

            程序在找到一個可用的QQ代理后退出,用good標識。另兩種代理服務(wù)器的狀態(tài)是dead,說明本地無法連接到代理服務(wù)器,或是bad,能與代理服務(wù)器建立連接,但是代理不能與QQ服務(wù)器通訊。

            代碼思路

            通過代理中國獲取到80端口的代理服務(wù)器列表,使用了urllib2模塊獲取頁面數(shù)據(jù),然后正則表達式解析出80端口的IP地址存入list中。接下來的幾行代碼簡單,但是很重要,使用較為底層的socket對象,構(gòu)造合適的數(shù)據(jù)包通過代理,請求與QQ服務(wù)器連接,通過讀取的返回數(shù)據(jù)包來驗證連接是否能建立。

            這里主要涉及到了HTTP協(xié)議的CONNECT的概念,很多人可能認為http代理只是為web瀏覽提供服務(wù),其實CONNECT方法允許允許用戶建立TCP連接到任何端口,這意味著代理不僅可用于HTTP,還可用于FTP,QQ等其他協(xié)議。只是網(wǎng)上提供CONNECT方法的代理服務(wù)器比較少,我有時候掃了一大堆,也沒有找到一個可用的代理。反過來說,有時候你找到的能瀏覽網(wǎng)頁的http服務(wù)器,未必能用在QQ上,QQ需要的是能CONNECT的代理。網(wǎng)頁瀏覽一般只使用HTTP協(xié)議的GET或POST方法,提供這兩種方法的服務(wù)器就多了。

            了解了代碼的原理,稍做改動,就可以用于其他類型的代理的驗證了,需要的是一些基本網(wǎng)絡(luò)知識和數(shù)據(jù)報的發(fā)送和接收。

            posted @ 2008-07-02 22:13 len 閱讀(1620) | 評論 (1)編輯 收藏

            Subversion的屬性是非常好用的功能,它將一些工作自動化,實現(xiàn)為受版本控制的源文件添加元信息的作用。屬性是外部不可見的,可以簡單認為是附加上在文件上的信息,和文件大小之類的信息是一樣的,只不過他是通過subversion來管理的。屬性的名稱和值可以是你希望的任何值,限制就是名稱必須是可讀的文本,并且最好的一點是這些屬性也是版本化的,就像你的文本文件內(nèi)容,你可以像提交文本修改一樣修改、提交和恢復屬性修改,當你更新時也會接收到別人的屬性修改—你不必為適應(yīng)屬性改變你的工作流程。

            Subversion保留了一組名稱以svn:開頭的屬性,來預定義一些有用的功能。比如你常會看到一些人的源代碼底部有像下面之類標識的文字:

            $Id: main_window.py 68 2008-06-30 02:05:05Z Len $

            這就使用了Subversion 中的 svn:keywords的自動屬性,它讓將發(fā)生在源代碼中的一些屬性的變化自動地更新到源代碼中。這行字的意思是表示,main_windows.py 這個源代碼文件最后被用戶 len 更新于 2008-6-30 02:05:05Z,修訂版本號為 68。要實現(xiàn)這樣的自動更新,你只要對需要這樣屬性的文件上使用下面這行指令。

            > svn propset svn:keywords "Id" main_window.py

            或者使用TortoiseSVN中的Properties的操作按鈕,方便地增加新的屬性。接著需要在源代碼文件中需要 Subversion 進行自動更新的地方插入 $Id$ 這樣的 Keyword,那么在你下次進行提交更新時,該$Id$ 就會被 Subversion 自動替換為$Id: main_window.py 68 2008-06-30 02:05:05Z Len $ 這樣的格式。
            Subversion 中可以使用的Keyword 包括下面這些:

            • Id
              上面介紹過的綜合的格式
            • LastChangedDate
              最后被修改的時間,縮寫為 Date。
            • LastChangedBy
              最后修改該源代碼文件的用戶名,縮寫為 Author。
            • LastChangedRevision
              最后修訂的版本號,縮寫為 Revision。

            如果想每次向Subversion服務(wù)器提交文件修改時,都要設(shè)置文件的屬性,則需要進行Subversion配置的修改。配置文件在你用戶的主目錄下,在Windows下應(yīng)類似于C:\Documents and Settings\Len\Application Data\Subversion\config文件,Len是Windows用戶名,注意Application Data是隱藏文件夾,需要顯示全部文件才能看到。接著如下相應(yīng)的修改,對你想要處理的文件做配置。

            enable-auto-props = yes   
            [auto-props]   
            *.c = svn:keywords=Id   
            *.py = svn:keywords=Id 

            對于開源項目,常見其源文件頭部有著版權(quán)聲明的文本,這些操作大多也是通Subversion的屬性功能來完成的,有關(guān)更詳細的介紹和操作指南,可參見Subversion中文手冊中的屬性章節(jié)。

            posted @ 2008-06-30 12:06 len 閱讀(1880) | 評論 (0)編輯 收藏

            這個類表示在單獨的控制線程中運行的活動。有兩種方法可以指定這種活動,給構(gòu)造函數(shù)傳遞回調(diào)對象,或者在子類中重寫run() 方法。其他方法(除了構(gòu)造函數(shù))都不應(yīng)在子類中被重寫。換句話說,在子類中只有__init__()run()方法被重寫。

            一旦線程對象被創(chuàng)建,它的活動需要通過調(diào)用線程的start()方法來啟動。這方法再調(diào)用控制線程中的run方法。

            一旦線程被激活,則這線程被認為是'alive'(活動)。當它的run()方法終止時-正常退出或拋出未處理的異常,則活動狀態(tài)停止。isAlive()方法測試線程是否是活動的。

            一個線程能調(diào)用別的線程的join()方法。這將阻塞調(diào)用線程,直到擁有join()方法的線程的調(diào)用終止。

            線程有名字。名字能傳給構(gòu)造函數(shù),通過setName()方法設(shè)置,用getName()方法獲取。

            線程能被標識為'daemon thread'(守護線程).這標志的特點是當剩下的全是守護線程時,則Python程序退出。它的初始值繼承于創(chuàng)建線程。這標志用setDaemon()方法設(shè)置,用isDaemon()獲取。

            存在'main thread'(主線程),它對應(yīng)于Python程序的初始控制線程。它不是后臺線程。

            有可能存在'dummy thread objects'(啞線程對象)被創(chuàng)建。這些線程對應(yīng)于'alien threads'(外部線程),它們在Python的線程模型之外被啟動,像直接從C語言代碼中啟動。啞線程對象只有有限的功能,它們總是被認為是活動的,守護線程,不能使用join()方法。它們從不能被刪除,既然它無法監(jiān)測到外部線程的中止。

            class Thread(group=None, target=None, name=None, args=(), kwargs={})

            構(gòu)造函數(shù)能帶有關(guān)鍵字參數(shù)被調(diào)用。這些參數(shù)是:

            group 應(yīng)當為 None,為將來實現(xiàn)ThreadGroup類的擴展而保留。

            target 是被 run()方法調(diào)用的回調(diào)對象. 默認應(yīng)為None, 意味著沒有對象被調(diào)用。

            name 為線程名字。默認,形式為'Thread-N'的唯一的名字被創(chuàng)建,其中N 是比較小的十進制數(shù)。

            args是目標調(diào)用參數(shù)的tuple,默認為()。

            kwargs是目標調(diào)用的參數(shù)的關(guān)鍵字dictionary,默認為{}。

            如果子線程重寫了構(gòu)造函數(shù),它應(yīng)保證調(diào)用基類的構(gòu)造函數(shù)(Thread.__init__()),在線程中進行其他工作之前。

            start()
            啟動線程活動。

            在每個線程對象中最多被調(diào)用一次。它安排對象的run() 被調(diào)用在一單獨的控制線程中。

            run()

            用以表示線程活動的方法。

            你可能在子類重寫這方法。標準的 run()方法調(diào)用作為target傳遞給對象構(gòu)造函數(shù)的回調(diào)對象。如果存在參數(shù),一系列關(guān)鍵字參數(shù)從argskwargs參數(shù)相應(yīng)地起作用。

            join([timeout])
            等待至線程中止。這阻塞調(diào)用線程直至線程的join() 方法被調(diào)用中止-正常退出或者拋出未處理的異常-或者是可選的超時發(fā)生。

            timeout參數(shù)未被設(shè)置或者不是None,它應(yīng)當是浮點數(shù)指明以秒計的操作超時值。因為join()總是返回None,你必須調(diào)用isAlive()來判別超時是否發(fā)生。

            timeout 參數(shù)沒有被指定或者是None時,操作將被阻塞直至線程中止。

            線程能被join()許多次。

            線程不能調(diào)用自身的join(),因為這將會引起死鎖。

            在線程啟動之前嘗試調(diào)用join()會發(fā)生錯誤。

            getName()
            返回線程名。
            setName(name)
            設(shè)置線程名。

            這名字是只用來進行標識目的的字符串。它沒有其他作用。多個線程可以取同一名字。最初的名字通過構(gòu)造函數(shù)設(shè)置。

            isAlive()
            返回線程是否活動的。

            大致上,線程從 start()調(diào)用開始那點至它的run()方法中止返回時,都被認為是活動的。模塊函數(shù)enumerate()返回活動線程的列表。

            isDaemon()
            返回線程的守護線程標志。
            setDaemon(daemonic)
            設(shè)置守護線程標志為布爾值daemonic。它必須在start()調(diào)用之前被調(diào)用。

            初始值繼承至創(chuàng)建線程。

            當沒有活動的非守護線程時,整個Python程序退出。

            參見:Python Library Reference

            posted @ 2008-06-24 16:54 len 閱讀(9179) | 評論 (0)編輯 收藏

            介紹

            cURL是一個利用URL語法的文件傳輸工具,是基于libcurl的前端命令行工具。它支持很多協(xié)議:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。 它同樣支持HTTPS認證,HTTP POST方法, HTTP PUT方法, FTP上傳, kerberos認證, HTTP上傳, 代理服務(wù)器, cookies, 用戶名/密碼認證, 下載文件斷點續(xù)傳, 上載文件斷點續(xù)傳, http代理服務(wù)器管道( proxy tunneling), 甚至它還支持IPv6, socks5代理服務(wù)器,通過http代理服務(wù)器上傳文件到FTP服務(wù)器等等,功能十分強大。

            除了使用curl命令行直接進行相關(guān)的網(wǎng)絡(luò)操作,你也可以自由地使用libcurl,它是用C語言編寫的,可以綁定到眾多的編程語言中,如C,C++,PHP,Python,Perl,Java等等。你可以很方便地利用libcurl,在程序中進行一些網(wǎng)絡(luò)傳輸工作,來代替一些語言的內(nèi)置,使你的知識可重用。在Unix工作環(huán)境下,你可以用curl代替wget和ftp等工具,并能將這種學習經(jīng)驗遷移到將來使用libcurl來完成一些自動化任務(wù)。

            curl是瑞典curl組織開發(fā)的,可以通過http://curl.haxx.se/來獲取更詳細的信息和下載文件。

            curl命令行工具使用

            curl太強大了,只能對其HTTP的部分作一簡單的介紹,其他選項可以參見其附帶的手冊。它的后端庫的使用也非常方便,主要也是在選項設(shè)置上,跟命令行基本無異。

            用法

            curl [選項] [URL...]

            URL 語法

            URL語法是跟協(xié)議相關(guān)的,具體細節(jié)可參見RFC 3986
            可以指定多個URLs或者部分URL地址,通過花括號{}進行分割:
            http://site.{one,two,three}.com
            或者用[]使用字母序:
            ftp://ftp.numericals.com/file[1-100].txt
            ftp://ftp.numericals.com/file[001-100].txt (有前導零)
            ftp://ftp.letters.com/file[a-z]].txt

            當前序列嵌套不被支持,但是還是可以使用下列的樣式:
            http://any.org/archive[1996-1999]/vol[1-4]/part{a,b,c}.html
            可以在命令行指定任意數(shù)量的URLs,它們將以指定的順序被取回。

            從curl7.15.1開始指定可以范圍步長,所以可以得到第n個數(shù)或字母:
            http://www.numericals.com/file[1-100:10].txt
            http://www.letters.com/file[a-z:2].txt

            如果使用了protocal://前綴,curl會將嘗試你想使用的協(xié)議。它默認使用HTTP,但是其他一些協(xié)議也常被用作主機名。比如說,以"ftp"打頭的主機名,curl會假定你想使用ftp協(xié)議。

            Curl會嘗試對多文件傳輸使用重連接,可以使從同一服務(wù)器獲取很多文件時,不會進行多次的連接。這種做法改進了速度,當然這只會在同一命令行中指定的文件啟用,而不會在獨立的Curl調(diào)用時使用。

            進度指示器

            curl通常在操作時會顯示一個進度指示器,來指明當前的傳輸量,傳輸速度和預計的剩余時間等等。

            但是,即然curl默認將數(shù)據(jù)顯示在終端,如果你調(diào)用curl進行操作,它會將數(shù)據(jù)打印到終端上,這時它會禁用掉進度指示器,否則這些會將輸出信息搞亂掉。

            如果在進行HTTP的POST或PUT請求時,你想將輸出重定向到文件中,可以使用shell的重定向符(>),或者類似的-o[file]選項。

            但是在FTP上傳并不會這樣,這些操作不會將數(shù)據(jù)插入到終端中。

            如果想使用進度欄,而不是常規(guī)的指示器,那么-#會非常有幫助。

            常用的HTTP選項

            -A/--user-agent<agent string>
            (HTTP)指定用戶代理字符串發(fā)送給HTTP服務(wù)器。如果這個字段沒有被設(shè)為"Mozilla/4.0",某些CGIs將不會正常工作。如果在字符串中存在空白字符,需要用單引號標識。這個字段值也可用-H/--header選項進行設(shè)置。
            如果這選項被多次設(shè)置,最后的設(shè)置將起作用。

            -b/--cookie<name=data>
            (HTTP)將data作為cookie傳給HTTP服務(wù)器,這數(shù)據(jù)當然是在使用了"Set-Cookie:"后,先前從服務(wù)端接收到的。這數(shù)據(jù)應(yīng)是"NAME1=VALUE1;NAME2=VALUE2"的格式。
            如果沒有"="字符,它將會當作先前存儲cookie行的文件名,如果它能被匹配的話。使用這選項,也能激活"cookie parser",它使curl記錄傳入的cookies數(shù)據(jù)。將它與-L/--locaion選項組合將會更加便利。被讀取cookie的文件格式應(yīng)當是文本HTTP頭或者Netscape/Mozilla cookie文件格式。
            注意:被-b/--cookie指定的文件只能作為輸入使用。沒有cookie會存儲在這文件中。為了存儲cookie,應(yīng)使用-c/--cookie-jar選項或者直接將HTTP頭輸出到文件中,用-D/--dump-header選項。這選項可以設(shè)置多次,但是只有最后一個起作用。

            -connect-timeout<seconds>
            以秒計的最大超時,用于進行服務(wù)器連接時。這只在連接階段起作用,一旦curl連接建立,這選項將不再起作用。

            -c/--cookie-jar<file name>
            指定在完成一系列操作后,需要將全部的cookie信息保存到哪個文件中。Curl會將先前讀取的cookie的信息和從服務(wù)器返回的信息一起保存。如果沒有cookie信息,則不會進行寫文件。cookie信息的文件將與Netscape cookie文件格式保存。如果文件名被設(shè)置為'-',則將cookie打印至終端。
            注意:如果cookie-jar不能被創(chuàng)建寫入,整個curl操作也不會失改,甚至不會向你報告錯誤.使用-v將會得到警告顯示,但也只能在可能導致發(fā)生致命錯誤的情況才會顯示。

            --create-dirs
            這與-o選項配合使用,curl會在需要時建立本地文件夾結(jié)構(gòu)。這選項會創(chuàng)建在-o選項中涉及到的文件夾。如果-o選項中的文件名沒有使用到文件夾,或者所需的文件夾已經(jīng)存在,則不會有文件夾創(chuàng)建。

            -D/--dump-header<file>
            將協(xié)議頭寫到指定的文件中。當你想存儲HTTP站點發(fā)給你的數(shù)據(jù)時,這選項非常有用。在協(xié)議頭中的cookie將來可以用curl的另外調(diào)用來進行讀取,那就是-b/--cookie選項。但是-c/--cookie-jar選項將會是更好的存儲cookie信息的方法。
            當使用FTP協(xié)議時,ftp服務(wù)器的應(yīng)答信息將相應(yīng)地當作成協(xié)議頭,然后被存儲。

            -p/-proxytunnel
            當HTTP代理被設(shè)置后(-x/--proxy),選項會使不是HTTP協(xié)議的傳輸試圖通過代理隧道,而不是表現(xiàn)得HTTP類似的操作形為。代理隧道的方法是通過HTTP服務(wù)器直接使用CONNECT請求,讓代理直接連接到curl隧道所請求的遠程端口號的方式來實現(xiàn)的。

            -o/--output<file>
            將輸出信息打印到文件中,而不是終端。可使用{}或者[]取回多個文檔,可在file指定格式中的'#'跟一數(shù)字,這樣這變量將會由取回的URL字符串所取代。如下:
            curl http://{one,two}.site.com -o "file_#1.txt"
            如果有多個變量,可以寫成下面的樣子:
            curl http://{site.host}.host[1-5].com -o "#1_#2"
            你可對任意數(shù)量的URL使用同樣多的這個選項

            -x/--proxy<proxyhost[:port]>
            使用指定的HTTP代理,如果端口號沒有被指定,默認為1080.
            這選項會覆蓋環(huán)境變量中代理服務(wù)器的設(shè)置。如果環(huán)境變量中設(shè)置了代理,可將proxy設(shè)置為空字符串,來覆蓋環(huán)境變量中的設(shè)置。
            注意:所有通過HTTP代理的操作都會自動轉(zhuǎn)化為HTTP協(xié)議。這意味著一些特定協(xié)議的操作將會變得無效。這不會有問題,如果在設(shè)置了-p/--proxytunnel選項來通過代理隧道進行操作。

            簡單示例

            獲取cppblog首頁,打印至終端

            >curl http://www.shnenglu.com
            重定向,保存到文件cppblog.html
            >curl http://www.shnenglu.com
            作用同上,使用選項
            >curl -o baidu.html http://www.baidu.com
            使用http代理,可指定IP和端口
            >curl -x 202.127.98.43:80 -o baidu.html http:www.baidu.com
            在訪問一些論壇時,常常要求啟用cookie,因為這些網(wǎng)站需要啟用cookie來記錄sessioin信息,這時需要選項-D,將cookie信息保存起來
            >curl -o cpp.html -c len@cppblog.com[1].txt http://www.shnenglu.com

            先前保存的cookie信息返回給網(wǎng)站,這通常會傳回你的一些用戶信息。
            >curl -o cpp.html -c len@cppblog.com[2].txt -b len@cppblog.com[1].txt http://www.shnenglu.com
            posted @ 2008-06-21 16:33 len 閱讀(7195) | 評論 (1)編輯 收藏

            介紹

            命令行接口是普遍,基礎(chǔ)的人機交互接口,從命令行提取程序的運行時選項的方法有很多。你可以自己編寫相對應(yīng)的完整的解析函數(shù),或許你有豐富的C語言編程經(jīng)驗,熟知getopt()函數(shù)的用法,又或許使用Python的你已經(jīng)在使用optparse庫來簡化這一工作。大家在平時不斷地談及到“不要重復造輪子”,那就需要掌握一些順手的庫,這里介紹一種C++方式來解析命令行選項的方法,就是使用Boost.Program_options庫。

            program_options提供程序員一種方便的命令行和配置文件進行程序選項設(shè)置的方法。使用program_options庫而不是你自己動手寫相關(guān)的解析代碼,因為它更簡單,聲明程序選項的語法簡潔,并且?guī)熳陨硪卜浅P ⑦x項值轉(zhuǎn)換為適合的類型值的工作也都能自動完成。庫有著完備的錯誤檢查機制,如果自己手寫解析代碼時,就可能會錯過對一些出錯情況的檢查了。最后,選項值不僅能從命令行獲取,也能從配置文件,甚至于環(huán)境變量中提取,而這些選擇不會增加明顯的工作量。

            示例說明

            以下面簡單的hello程序進行說明,默認打印hello world,如果傳入-p選項,就會打印出人的姓名,另外通過傳入-h選項,可以打印出幫助選項。略微看一眼代碼文件和相應(yīng)的屏幕輸入輸出,然后我們再一起來看看這些是如何發(fā)生的。

            //hello.cpp 
            #include <iostream>
            #include <string>
            #include <boost/program_options.hpp>
            
            using namespace std;
            int main(int argc, char* argv[])
            {
                using namespace boost::program_options;
                //聲明需要的選項
                options_description desc("Allowed options");
                desc.add_options()
                    ("help,h", "produce help message")
                    ("person,p", value<string>()->default_value("world"), "who")
                    ;
            
                variables_map vm;        
                store(parse_command_line(argc, argv, desc), vm);
                notify(vm);    
            
                if (vm.count("help")) {
                    cout << desc;
                    return 0;
                }
                cout << "Hello " << vm["person"].as<string>() << endl;
                return 0;
            }
            

            下面是在Windows命令提示符窗口上的輸入輸出結(jié)果,其中">"表示提示符。

            >hello 
            Hello world
            
            >hello -h
            Allowed options:
              -h [ --help ]                produce help message
              -p [ --person ] arg (=world) who
            
            >hello --person len
            Hello len
            

            首先通過options_description類聲明了需要的選項,add_options返回了定義了operator()的特殊的代理對象。這個調(diào)用看起來有點奇怪,其參數(shù)依次為選項名,選項值,以及選項的描述。注意到示例中的選項名為"help,h",是因為聲明了具有短選項名和長選項名的選項,這跟gnu程序的命令行具有一致性。當然你可以省略短選項名,但是這樣就不能用命令選項簡寫了。第二個選項的聲明,定義了選項值為string類型,其默認值為world.

            接下來,聲明了variables_map類的對象,它主要用來存儲選項值,并且能儲存任意類型的值。然后,store,parse_command_line和notify函數(shù)使vm能存儲在命令行中發(fā)現(xiàn)的選項。

            最后我們就自由地使用這些選項了,variables_map類的使用就像使用std::map一樣,除了它必須用as方法去獲取值。如果as方法調(diào)用的指定類型與實際存儲的類型不同,就會有異常拋出。

            具有編程的你可能有這樣的經(jīng)驗,使用cl或gcc對源文件進行編譯時,可直接將源文件名放置在命令行中,而無需什么選項字母,如gcc a.c之類的。prgram_options也能處理這種情況,在庫中被稱為"positional options"(位置選項),但這需要程序員的一點兒幫助才能完成。看下面的經(jīng)過對應(yīng)修改的代碼,我們無需傳入"-p"選項,就能可指定"person"選項值

                positional_options_description p;
                p.add("person", -1);
                store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
            
            >hello len
            Hello len
            

            前面新增的兩行是為了說明所有的位置選項都應(yīng)被解釋成"person"選項,這里還采用了command_line_parser類來解析命令行,而不是用parse_command_line函數(shù)。后者只是對前者類的簡單封裝,但是現(xiàn)在我們需要傳入一些額外的信息,所以要使用類本身。

            選項復合來源

            一般來說,在命令行上指定所有選項,對用戶來說是非常煩人的。如果有些選項要應(yīng)用于每次運行,那該怎么辦呢。我們當然希望能創(chuàng)建出帶有些常用設(shè)置的選項文件,跟命令行一起應(yīng)用于程序中。當然這一切需要將命令行與配置文件中的值結(jié)合起來。比如,在命令行中指定的某些選項值應(yīng)該能覆蓋配置文件中的對應(yīng)值,或者將這些值組合起來。

            下面的代碼段將選項通過文件讀取,這文件是文本格式,可用"#"表示注釋,格式如命令行中的參數(shù)一樣,選項=值

                ifstream ifs("config.cfg");
                store(parse_config_file(ifs,config),vm);
                notify(vm);
            

            參考

            Boost.prgram_options庫文檔

            posted @ 2008-06-15 21:03 len 閱讀(13650) | 評論 (0)編輯 收藏

            涉及到軟件:Xmanager 1.3.9 / Windows xp, ubuntu hardy

            第一步,在ubuntu機器上配置好gdm,修改/etc/gdm/gdm.conf-custom,對照添加如下內(nèi)容:

            [security]
            DisallowTCP=false
            [xdmcp]
            Enable=true
            

            第二步,性能調(diào)優(yōu)。這步非常關(guān)鍵,不然使用Xmanager登陸速度非常慢,且會報錯,主要原因是gnome使用Esound進行聲音數(shù)據(jù)的傳送,需要使用TCP 16001端口。所以我建設(shè)在ubuntu關(guān)掉混音選項。
            系統(tǒng)-首選項-音效-音效,將“允許軟件混音”不要勾選上,
            系統(tǒng)-首選項-字體-字體渲染,選擇"單色",在“細節(jié)”的“字體渲染細節(jié)”中的平滑和微調(diào)選項,都選擇"無"。

            有用的參考:http://www.netsarang.com/products/xmg_faq.html

            posted @ 2008-06-01 21:10 len 閱讀(7715) | 評論 (0)編輯 收藏

            Vim是功能強大的文本編輯器,但是每個工具都有其針對的適用群體。如果你只是偶爾做些文本編輯工作的話,那靈活而又顯得繁瑣的設(shè)置,以及特別的操作方式可能不適合你。但是你是跟我一樣,是個平平凡凡的程序員,每天要花費大量時間在寫代碼,把弄著各式各樣的程序語言:C\C++,Python,Tcl,Html,Xml,...,那么你可能需要像Vim這樣的工具,即使你要在它上面花費些時間去熟悉和適應(yīng)它。

            先講述一下,我跟Vim相處的過程,這是個從認識,到拋棄,到再認識,到再學習,到喜歡的過程。最早接觸到Vim是在Solaris上,需要修改編輯一些配置文件,看著其他工程師們手指隨意地在鍵盤上敲擊,就完成內(nèi)容的修改,根本沒有動用到什么鼠標,那是好生羨慕。嚴格意義來說,那時候碰到還不是Vim,只是VI而已。在終端上工作,沒有什么Notepad之類的程序,只好把指令抄在紙上,查查網(wǎng)上的資料,學會了h,j,k,l,w,q,e,這幾個簡單指類來進行簡單的文本查看工作,僅此而己。后來在Windows上安裝了VIM,但是掙騰了幾下,沒有適應(yīng)過來,也就只好使用UltraEdit了。UltraEdit對一般的純文本,按Windows習慣來說是蠻好使的。再后來,玩了會兒ruby,又裝起了Vim,但是那時候的對Vim的使用也只是限于上面的簡單的指令,再加上Vim的插件,來完成語法高亮,ruby中的MVC文件的方便跳轉(zhuǎn)而已,還是沒有習慣Vim,有時候還是不經(jīng)意用UltraEdit來打開查看編輯文件。直到最近,需要編寫Docbook,以及用Python,才真正花費了大量時間來學習使用Vim,才真正認識到到它的可愛。

            接著說說,我為什么使用Vim,覺得值得學習它,喜歡它的理由吧,純粹以自己的觀點來敘述。

            ?跨平臺性,無論在Windows,Linux,Solaris,FreeBSD等等操作系統(tǒng)上,以及一些名都沒有聽過的系統(tǒng)上,你都可以找到它。這樣就保證了你的學習投資的保值性,就拿UltraEdit做對比吧,即使你在UltraEdit上學會靈活運用許多功能,到了Linux上,你在這部分學習投資就沒有價值了,你可能需要找其他稱手的編輯器,然后再進行學習一些功能。特別在一些古老的大型機上的系統(tǒng)上,即使沒有Vim,一般來說,還有Vi的,這樣一般簡單的操作命令還是可復用的。如果你確定你一直只呆在Windows上可忽略這一點。

            開源免費,Vim是開源軟件,意味著你可以自由使用,修改,查看它的代碼。我對FreeSoftware,Open Source,Copyright,這些都是持中間立場的。對于自由查看,修改程序代的保證,有總比沒有好。對于盜版軟件,你有能力還是不要使用的好。正是這一特性,也是促使我放棄UE,投向Vim的重要原因。如果你對于使用盜版軟件蠻不在乎,或你有財力購買正版軟件,也可忽視這一條。

            支持多種編程語言,Vim是程序員的編輯器,當然對程序員是非常友好的。它對C,C++,Python,Perl,Tcl,Ruby,PHP等等,以及一大堆我沒有聽過見過的語言,以語法著色,代碼縮進等基本支持,還有一些其他特性。比如,我在編輯XML時,它能提供自動封閉標記的支持。因此如果你有對多種格式的文本編輯需要,那么你就有了一個編輯的大平臺,不需用再裝一大堆針對某個格式特定的編輯器了。正如跨平臺性一樣,你只要一次投資,多次回報。如果你專注于某一格式文件的工作,那這一點同樣對于你來說是沒有用的。

            高效地編輯,Vim的操作方式相對于Windows上呆久了的人來說,是蠻奇特的,這一點我深有體會。但是正如很多人講的那樣,你掌握了其操作后,發(fā)現(xiàn)它會大大增進你的編輯速度。你的雙手根本不用離開鍵盤,就完成了許多事情,可以讓鼠標歇會兒了。如果你特別鐘愛鼠標,或只偶爾打打字,那么我說的這點,同樣對你沒有用。

            靈活的設(shè)置,Vvim可自定義的地方太多了,你可以自定義鍵盤映射,語法著色,縮進,格式等等。所以你在網(wǎng)上可以看到許多人貼著自己的vimrc配置文件,配置著自己喜歡的作業(yè)環(huán)境。如果你需要開盒即用的工具,那么這點對你的吸引力就不大了。

            安裝

            可到VIM官網(wǎng),選擇Self-install executable形式的安裝包下載安裝。

            幫助

            幫助非常重要,VIM帶有我認為非常好的幫助系統(tǒng),可以獲取你需要的任何有關(guān)VIM的詳細信息。使用幫助非常簡單,只需要:help安裝即可。安裝后程序帶的是英文幫助,如果你對英文不是特別適應(yīng)的話,可以去http://vimcdoc.sourceforge.net/下載安裝中文幫助,或像我一樣直接使用在線的中文幫助

            操作方法

            對于基本操作方法,通過Vim的教程,你應(yīng)該能很好的掌握了。一些常見的設(shè)置,關(guān)于特定類型的配置,因人而異,不想多述。我會列出一些認為比較好的參考文章,置于文尾供參考。但在下面,我還是在Windows下的Vim的使用做點說明,或許你現(xiàn)在用不上。

            Vim文件夾結(jié)構(gòu)

            安裝完Vim后,你在其安裝目錄下應(yīng)有vim$ver($ver是版本號)和vimfiles兩個文件夾。其中vim$ver是vim的程序運行時目錄,在里面會看到gvim.exe(vim的GUI),vim.exe,xxd.exe等程序,一大堆的dll動態(tài)鏈接庫,還有就是color(語法著色),doc(幫助說明),indent(縮進)等文件夾。在vimfiles內(nèi),也會看到color,doc,indent等類似的文件夾,但它們里面沒有文件。vim$ver和vimfiles兩者有什么區(qū)別呢,vim$ver是運行時文件目錄,vimfiles相當于個人配置目錄,常常有文章說在linux下將什么插件放進.vim下的plugin等等之類的,其.vim在windows下就相當于vimfiles。

            標簽頁

            Tabpage是Vim后增的功能,類似于UltraEdit的標簽頁。也想在Windows下使用Untraledit一樣,在同一個VIM實例中打開多個文件的話,需要做些小修改。在注冊表中刪除"HKCR\*\shellex\ContextMenuHandlers\gvim\"主鍵,然后在Shell下新項"Vim 編輯",再在其下新建command項,然后修改其值為$vimruntime\gvim.exe -p --remote-tab-silent "%1",其中$vimruntime修改為你系統(tǒng)中VIM實際運行目錄。如果你不知道$vimruntime的值,可以打開gvim,輸入:echo $vimruntime。你想雙擊關(guān)聯(lián)文件,也在同一實例打開的話,查找注冊表中g(shù)vim相關(guān)項,將$vimruntime\gvim.exe改為上述的值即可,主要是HKLM\software\classes\application\gvim.exe\shell\edit\command下的值。

            文件編碼

            具體可參見"Vim實用技術(shù):實用技巧"。我推薦內(nèi)部編碼使用utf-8,以支持國際化,即encoding=utf-8。這需要在_vimrc中進行設(shè)置,網(wǎng)上常有人啟用這一選項后Vim菜單和消息出現(xiàn)亂碼。據(jù)我的經(jīng)驗,需要將這encoding=utf-8寫在_vimrc最開頭,然后設(shè)置language message,可參見我的_vimrc文件。

            vimrc文件

            Vim使用中,配置文件vimrc是非常重要的,用:echo $myvimrc,來查看你的vimrc在哪里。
            如果這為空的話,你可以在$vim目錄,建一新的_vimrc文件。
            我的vimrc文件

            set encoding=utf-8
            set termencoding=gbk
            set nocompatible          " We're running Vim
            set nobackup		"We don't need the backup file
            set showmatch		"Show where the bracket match
            set showcmd
            set ruler		"Show the line and column number 
            set hlsearch		"Highlight the search key
            set backspace=indent,eol,start
            set fileencodings=ucs-bom,utf-8,chinese
            set guifont=courier_new:h10
            set autoindent
            syntax on           " Enable syntax highlighting
            filetype plugin indent on " Enable filetype-specific indenting and plugins
            language message zh_CN.utf-8 " Use chinese message
            color zellner		" Color theme

            其中termencoding=gbk是因為windows中的“命令提示符”窗口只能使用gbk編碼,不能像Gnome中的Console那樣用utf-8。不設(shè)置的情況下,使用“命令提示符”下的vim,而不是gvim時,會出亂碼。在設(shè)置文件中的色彩和字體,可以先在gvim菜單中設(shè)置,然后將你所喜好的,添加到_vimrc文件中。看到我的vimrc文件,你是不是感覺特別短。因為我把許多跟文件類型的相關(guān)設(shè)置放在其對應(yīng)的腳本里,扔在vimfiles文件夾了。在vimrc里,例如常見的空格,制表符,縮進都沒有在這配置。

            雜項

            Vim中一些內(nèi)置的變量,你都可以通過:echo varname來查看值,比如::echo $myvimrc
            這些變量,注意大小寫,常用的有
            $VIM Vim的安裝目錄
            $vimruntime Vim運行時目錄
            $myvimrc 用戶的_vimrc文件
            $home 用戶的主目錄
            我常常使用:e $myvimrc來編輯我的vimrc文件,非常方便。

            對一些帶值的配置選項,你可以用:set optionname來查看其當前值,或用:set optionname=val來更改其值.比如:set fileformat查看文件格式,因為dos,unix,mac對于換行是不一樣的。:set filemat=unix的話,換行將用LF,而不是dox\windows下的CR,LF。

            參考鏈接

            IBM開發(fā)中心非常實在的Vim實用技術(shù)系列:
            Vim實用技術(shù)(1)-實用技巧
            Vim實用技術(shù)(2)-常用插件
            Vim實用技術(shù)(3)-定制Vim

            Easwy的博客,里面有用的信息,更多的Vim資源鏈接
            Vim專欄

            posted @ 2008-05-25 20:19 len 閱讀(5851) | 評論 (13)編輯 收藏

            Boost.Lambda是什么?

            Boost Lambda庫是C++模板庫,以C++語言實現(xiàn)了lambda抽象.Lambda這個術(shù)語來自函數(shù)編程語言和lambda閉包理論,lambda抽象實際上定義了匿名函數(shù).了解過C#新引入的匿數(shù)函數(shù)特性或Lisp編程的人,對這些概念理解會有很大幫助.Lambda庫設(shè)計的主要動機是為STL算法提供靈活方便的定義匿名函數(shù)對象的機制.這個Lambda庫究竟是有什么用呢?代碼勝千言!看下面將STL容器中的元素打印到標準輸出上的代碼.

            for_each(a.begin(), a.end(), std::cout << _1 << ' ');

            表達式std::cout << _1 << ' '定義了一元函數(shù)對象.變量_1是函數(shù)的形參,是實參的占位符.每次for_each的迭代中,函數(shù)帶著實際的參數(shù)被調(diào)用,實際參數(shù)取代了占位符,然后函數(shù)體里的內(nèi)容被執(zhí)行.Lambda庫的核心就是讓你能像上面所展示的那樣,在STL算法的調(diào)用點,定義小的匿名函數(shù)對象.

            Lambda庫的安裝

            Lambda庫只由頭文件組成,這就意味著你不需要進行任何編譯,連接,生成二進制庫的動作,只需要boost庫頭文件路徑包含進你的工程中即可使用.

            與現(xiàn)代的C++語言一樣,在使用時你需要聲明用到的名字空間,把下列的代碼包含在你的源文件頭:

            using namespace boost::lambda;

            Boost Lambda庫的動機

            在標準模板庫STL成為標準C++的一部分后,典型的STL算法對容器中元素的操作大都是通過函數(shù)對象(function objects)完成的.這些函數(shù)作為實參傳入STL算法.

            任何C++中以函數(shù)調(diào)用語法被調(diào)用的對象都是函數(shù)對象.STL對某些常見情況預置了些函數(shù)對象.比如:plus,less,not1下面就是標準plus模板的一種可能實現(xiàn):
            template <class T> 
            struct plus : public binary_function <T, T, T> {
              T operator()(const T& i, const T& j) const {
                return i + j; 
              }
            };
            

            基類binary_function<T, T, T>包含了參數(shù)和函數(shù)對象返回類型的類型定義,這樣可使得函數(shù)對象可配接.

            除了上面提到的基本的函數(shù)對象外,STL還包含了binder模板,將可配接的二元函數(shù)中的某個實參固定為常量值,來創(chuàng)建一個一元函數(shù)對象.比如:

            class plus_1 {
              int _i;
            public:
              plus_1(const int& i) : _i(i) {}
              int operator()(const int& j) { return _i + j; }
            };
            

            上面的代碼顯性地創(chuàng)建了一個函數(shù)對象,將其參數(shù)加1.這樣的功能可用plus模板與binder模板(bind1st來等效地實現(xiàn).舉例來說,下面的兩行表達式創(chuàng)建了一個函數(shù)對象,當它被調(diào)用時,將返回1與調(diào)用參數(shù)的和.

            plus_1(1)
            bind1st(plus<int>(), 1)
            

            plus<int>就是計算兩個數(shù)之和的函數(shù)對象.bind1st使被調(diào)用的函數(shù)對象的第一個參數(shù)綁定到常量1.作為上面函數(shù)對象的使用示例,下面的代碼就是將容器a中的元素加1后,輸出到標準輸出設(shè)備:

            transform(a.begin(), a.end(), ostream_iterator<int>(cout),
                      bind1st(plus<int>(), 1));
            

            為了使binder更加通用,STL包含了適配器(adaptors)用于函數(shù)引用與指針,以及成員函數(shù)的配接.

            所有這些工具都有一個目標,就是為了能在STL算法的調(diào)用點有可能指定一個匿名的函數(shù),換句說,就是能夠使部分代碼片斷作為參數(shù)傳給調(diào)用算法函數(shù).但是,標準庫在這方面只做了部分工作.上面的例子說明用標準庫工具進行匿名函數(shù)的定義還是很麻煩的.復雜的函數(shù)調(diào)用表達式,適配器,函數(shù)組合符都使理解變得困難.另外,在運用標準庫這些方法時還有明顯的限束.比如,標準C++98中的binder只允許二元函數(shù)的一個參數(shù)被綁定,而沒有對3參數(shù),4參數(shù)的綁定.這種情況在TR1實施后,引進了通用的binder后可能改善,對于使用MSVC的程序員,有興趣還可以查看下微軟針對VS2008發(fā)布的TR1增強包.

            但是不管怎樣,Lambda庫提供了針對這些問題比較優(yōu)雅的解決方法:

            • 對匿名函數(shù)以直觀的語義進行創(chuàng)建,上面的例子可改寫成:

              transform(a.begin(), a.end(), ostream_iterator<int>(cout), 
                        1 + _1);
              

              更直觀點:

              for_each(a.begin(), a.end(), cout << (1 + _1));
              
            • 絕大部分對函數(shù)參數(shù)綁定的限制被去除,在實際C++代碼中可以綁定任意的參數(shù)

            • 分離的函數(shù)組合操作不再需要了,函數(shù)組合被隱性地支持.

            Lambda表達式介紹

            Lambda表達在函數(shù)式編程語言中很常見.在不同語言中,它們的語法有著很大不同,但是lambda表達式的基本形式是:

            lambda x1...xn.e

            lambda表達式定義了匿名函數(shù),并由下列的元素組成

            • 函數(shù)的參數(shù):x1...xn
            • 表達式e,以參數(shù)x1...xn的形式計算函數(shù)的值

            一個簡單的lambda表達式的例子是:

            (lambda x y.x+y) 2 3 = 2 + 3 = 5 

            在lambda表達式的C++版本中,表達式中x1...xn不需要,已預定義形式化的參數(shù).在現(xiàn)在Boost.Lambda庫中,存在三個這樣的預定義的參數(shù),叫做占位符:_1,_2,和_3.它們分別指代在lambda表達式中的第一,二,三個參數(shù).比如,下面這樣的lambda表達式:

            lambda x y.x+y

            C++定義的形式將會是這樣:

            _1 + _2

            因此在C++中的lambda表達式?jīng)]有語義上所謂的關(guān)鍵字.占位符作為運算符使用時就隱性地意味著運算符調(diào)用是個lambda表達式.但是只有在作為運算符調(diào)用才是這樣.當Lambda表達式包含函數(shù)調(diào)用,控制結(jié)構(gòu),轉(zhuǎn)換時就需要特殊的語法調(diào)用了.更為重要的是,作為函數(shù)調(diào)用是需封裝成binder函數(shù)的形式.比如,下面這個lambda表達式:

            lambda x y.foo(x,y)

            不應(yīng)寫成foo(_1,_2),對應(yīng)的C++結(jié)構(gòu)應(yīng)如下:

            bind(foo, _1, _2)

            對于這種表達式,更傾向于作為綁定表達式bind expressions

            lambda表達式定義了C++的函數(shù)對象,因此,對于函數(shù)調(diào)用的形式跟其他的函數(shù)對象一樣,比如:(_1 + _2)(i, j).

            性能

            性能,運行效率,總是C++程序員關(guān)心的話題.理論上,相對于手寫循環(huán)代碼,使用STL算法和Lambda函數(shù)對象的所有運行開銷,可以通過編譯優(yōu)化消除掉.這種優(yōu)化取決于編譯器,實際中的編譯器大都能做到.測試表明,性能會有下降,但是影響不大,對于代碼的效率和簡潔之間的權(quán)衡,只能由程序員自己做出判斷了.

            Lambda庫的設(shè)計與實現(xiàn)中大量運用了模板技術(shù),造成對于同一模板需要大量的遞歸實例化.這一因素可能使構(gòu)建復雜邏輯的lambda表達式,不是一個非常理想的做法.因為編譯這些表達式需要大量的內(nèi)存,從而使編譯時間變得非常慢,這在一些大型項目中會更加突出.還有在發(fā)生編誤錯誤時,引發(fā)的大量錯誤信息,不能有效地指出真正錯誤之處.最后點,C++標準建議模板的嵌套層次不要超過17層來防止導致無限遞歸,而復雜的Lambda表達式模板會很容易超過這一限制.雖然大多數(shù)編譯器允許更深層次的模板嵌套,但是通常需要顯性地傳入一個命令行參數(shù)才能做到.

            參考

            大多數(shù)內(nèi)容是從Boost.Lambday庫在線文檔參考翻譯而成

            posted @ 2008-05-18 16:03 len 閱讀(8647) | 評論 (5)編輯 收藏

            "Designing Qt-Style C++ APIs" by Matthias Ettrich

            http://doc.trolltech.com/qq/qq13-apis.html

            翻譯這篇文章的目的不是讓人了解Qt,而是讓人試著學習點C++編程的軟技能。我從原文中得到的一些風格上的體會,也希望你能從中有所收獲.(譯者注)
            我們在Trolltech做了大量研究來改進Qt開發(fā)體驗.在這篇文章中,我將分享我們的一些成果,呈現(xiàn)我們在進行Qt 4設(shè)計時所使遵循的原現(xiàn),并向你展示如何將它們應(yīng)用到你的代碼中.

            設(shè)計應(yīng)用程序接口(APIs)是有難度的.它是像跟設(shè)計編程語言一樣困難的藝術(shù).要遵循許多不同的的原則,這些原則中的許多還彼此沖突.

            現(xiàn)今的計算機教育過多關(guān)注于算法和數(shù)據(jù)結(jié)構(gòu),很少去關(guān)注隱藏在程序設(shè)計語言和程序框架后面的那些設(shè)計原則.這使得程序員們面對日益重要的任務(wù),創(chuàng)建可復用的組件,毫無準備.

            在面向?qū)ο笳Z言出現(xiàn)前,通用的可復用的代碼大都由庫提供者而不是應(yīng)用程序開發(fā)者來編寫.在Qt世界中,這種情況已發(fā)生了很大的變化.在用Qt編程其實就是在寫新的組件.典型的Qt應(yīng)用程序都存在某些自定義的組件,在整個應(yīng)用程序中被復用.相同的組件常常作為其他程序的一部分被開發(fā)出來.KDE,K桌面環(huán)境,甚至使用許多附加庫,來進一步擴展Qt,實現(xiàn)許多額外的類.

            但是一個優(yōu)秀,高效的C++ API究竟是怎樣子呢?它的好壞取決于許多因素,比如說,手頭上的任務(wù)和特定目標群體.優(yōu)秀的API具有很多特性,它們的一些是普遍所要期望的,另一些是針對特定問題域的.

            優(yōu)秀API的六個特性

            API對于程序員就相當于GUI對于最終用戶.API中'P'代表程序員(Programmer),而不是程序(Program),強調(diào)這一點是為了說明API是讓程序員使用的,程序員是人而不機器.

            我們認為APIs應(yīng)當精簡而完備,具有清晰簡單的語義,直觀,易記且應(yīng)使代碼具有可讀性.

            • 精簡性:精簡的API具有盡可能少的類和公共成員.這使得理解,記憶,調(diào)試,更改API更加容易.
            • 完備性:完備的API意味著擁有應(yīng)具有的期望功能.這可能使與API保持精簡性相沖突.還有,如果成員函數(shù)放在不相匹配的類中,那么許多使用這個功能函數(shù)的潛在用戶會找不到它.
            • 清晰簡單的語義:正如與其他設(shè)計工作一樣,你應(yīng)該準守最小驚議原則.讓通常的任務(wù)簡單,罕見的任務(wù)應(yīng)盡可能簡單,但它不應(yīng)成為重點.解決特定的問題.不要使解決方法具有普適作用,當它們不需要的時候.
            • 直觀性:與計算機有關(guān)的其他事情一樣,API應(yīng)具有直觀性.不同經(jīng)歷和背景會導致對哪些是直觀,哪些不是直觀的不同看法.如果對非專業(yè)的用戶在不需要閱讀文檔下能立即使用API,或?qū)@個API不了解的程序員能理解使用了API的代碼,那么這API就是具有直觀性.
            • 易記:為了使API容易記憶,使用一致且精準的命名規(guī)范.使用容易識別的模式和概念,避免使用縮寫.
            • 能生成可讀生代碼:代碼只寫一遍,卻要閱讀許多遍(調(diào)試或更改).可讀性的代碼有時候可能需要多敲些字,但是從產(chǎn)品生命周期中可節(jié)省很多時間.

            最后,請記住:不同的用戶使用API的不同部分.當簡單地使用Qt類的實例可能有直觀性,但這有可能使用戶在閱讀完有關(guān)文檔后,才能嘗試使用其中部分功能.

            方便性陷阱

            通常的誤讀是越少的代碼越能使你達到編寫更好的API這一目的.請記住,代碼只寫一遍,卻要一遍又一遍地去理解閱讀它.比如:

            QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
                                              0, "volume");
            

            可以會比下面的代碼更難閱讀(甚至于編寫)

            QSlider *slider = new QSlider(Qt::Vertical);
            slider->setRange(12, 18);
            slider->setPageStep(3);
            slider->setValue(13);
            slider->setObjectName("volume");
            

            布爾參數(shù)陷阱

            布爾參數(shù)常常導致難以閱讀的代碼.特別地,增加某個bool參數(shù)到現(xiàn)存的函數(shù)一般都會是個錯誤的決定.在Qt中,傳統(tǒng)的例子是repaint(),它帶有一個可選的布爾參數(shù),來指定背景是否刪除(默認是刪除).這就導致了代碼會像這樣子:

            widget->repaint(false);

            初學者可能會按字面義理解為,"不要重繪!"

            自然的想法是bool參數(shù)節(jié)省了一個函數(shù),因此減少了代碼的臃腫.事實上,這增加了代碼的臃腫,有多少Q(mào)t用戶真正知道下面這三行代碼在做什么呢?

            widget->repaint();
            widget->repaint(true);
            widget->repaint(false);
            

            好一點的API代碼可能看起來像這樣:

            widget->repaint();
            widget->repaintWithoutErasing();
            

            在Qt 4中,我們解決這個問題的辦法是,簡單地去除掉不刪除widget而進行重繪的可能性.Qt 4對雙重緩沖的原生支持,會使這功能被廢棄掉.

            這里有些例子:

            widget->setSizePolicy(QSizePolicy::Fixed,
                                      QSizePolicy::Expanding, true);
            textEdit->insert("Where's Waldo?", true, true, false);
            QRegExp rx("moc_*.c??", false, true);
            

            顯然的解決辦法就是將bool 參數(shù)用枚舉類型來替換.這就是我們在Qt 4中Qstring中的大小寫敏感所做的,比較下面兩個例子:

            str.replace("%USER%", user, false);               // Qt 3
            str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4
            

            靜態(tài)多態(tài)

            相似的類應(yīng)該有相似的API.在某種程度上,這能用繼承來實現(xiàn),也就是運用運行時多態(tài)機制.但是多態(tài)也能發(fā)生在設(shè)計時.比如,你將QListBox與QComboBox交換,QSlider與QSpinBox交換,你會發(fā)現(xiàn)API的相似性會使這種替換變得比較容易.這就是我們所謂的"靜態(tài)多態(tài)".

            靜態(tài)多態(tài)也能使記憶API和編程模式更加容易.因而,對一組相關(guān)類的相似API有時候比為每個類設(shè)計獨特完美的API會更好.

            命名藝術(shù)

            命名有時候是設(shè)計API中最重要的事情了.某個類應(yīng)叫什么名字,某個成員函數(shù)又應(yīng)叫什么名字,都需要好好思考.?

            通常的命名規(guī)則

            有少許規(guī)則對所有類型的命名都適應(yīng).首先,正如我早先所提到的,不要用縮寫.甚至對用"prev"代表"previous"這樣明顯的縮寫也不會在長期中受益,因為用戶必須記住哪些名字是縮寫.

            如果連API自身都不能保持統(tǒng)一,事情自然會變得更壞.比如,Qt 3中有activatePreviousWindow()函數(shù),也有fetchPrev()函數(shù).堅持"沒有縮寫"這條規(guī)則,會使創(chuàng)建一致的API更加簡單.

            在設(shè)計類中,另一重要但是不明顯的規(guī)則是盡量保持子類中名字的簡潔易懂.在Qt 3中,這個原則并不總是被遵守.為了說明這一點,我們舉下QToolButton的例子.如果你在Qt 3中對QToolButton調(diào)用call name(), caption(), text(), 或 textLabel()成員函數(shù)時,你希望會發(fā)生什么?那就在Qt設(shè)計器中試試QToolButton吧.

            • name 屬性繼承自QObject,用來在調(diào)試和測試中指代對象的內(nèi)部名稱.
            • caption 屬性繼承自QWidget,指代窗體的標題.對于QToolButton沒有什么意思,既然它們都是由父窗體創(chuàng)建的.
            • text 屬性繼承自QButton,通常用于按鈕中,除非useTextLabel為真.
            • textLabel 屬性 在QToolButton中聲明,如果useTextLabel為真,則顯示在按鈕上.

            為了可讀性的關(guān)系,在Qt4中name 被稱為objectName ,caption被稱為windowTitle,在QToolButton中為了使text明晰,不再有textLabel屬性.

            命名類

            不應(yīng)為每個不同的類尋求完美的名字,而是將類進行分給.比如,在Qt 4中所有跟模型有關(guān)的視類的部件都用View后綴(QlistView,QTableView,QTreeView),相應(yīng)的基于部件的類用Widget后綴代替(QListWidget,QTableWidget,QTreeWidge).

            枚舉類型和值類型命名

            當設(shè)計枚舉時,我們應(yīng)當記住C++中(不像Java或C#),枚舉值在使用時不帶類型名.下面的例子說明了對枚舉值取太一般化的名字的危害:

            namespace Qt
            {
                enum Corner { TopLeft, BottomRight, ... };
                enum CaseSensitivity { Insensitive, Sensitive };
                ...
            };
                
                tabWidget->setCornerWidget(widget, Qt::TopLeft);
                str.indexOf("$(QTDIR)", Qt::Insensitive);
            

            在上面這行中,Insensitive這個名字什么意思呢?為枚舉類型命名具有指導的原則是最好在每個枚舉值中重復枚舉類型的名字.

            namespace Qt
            {
                enum Corner { TopLeftCorner, BottomRightCorner, ... };
                enum CaseSensitivity { CaseInsensitive,
                                          CaseSensitive };
                ...
            };
                
            tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
            str.indexOf("$(QTDIR)", Qt::CaseInsensitive);
                

            但枚舉值之間是一種"或"關(guān)系和被用作標志位時,傳統(tǒng)的解決方法是將"或"結(jié)果存為int,這樣做是類型不安全的.Qt 4提供了一模板類QFlags<T>,其中T是枚舉類型.Qt為標志類型名稱提供了便利,你能用Qt::Alignment 來代替QFlags<Qt::AlignmentFlag>.

            為了方便,我們給枚舉類型單數(shù)形式的名稱(只有當只含一個標志位時),給"flags"類型復數(shù)形式的名稱,比如:

            enum RectangleEdge { LeftEdge, RightEdge, ... };
            typedef QFlags<RectangleEdge> RectangleEdges;
                

            在某些情況下,"flags"類型有單數(shù)形式的名稱.在這種情況下,枚舉類型以Flag后綴標識:

            enum AlignmentFlag { AlignLeft, AlignTop, ... };
            typedef QFlags<AlignmentFlag> Alignment;
            

            函數(shù)和參數(shù)的命名

            函數(shù)命名中的一條規(guī)則就是應(yīng)能從它的名字清楚地看出函數(shù)是否著副作用.在Qt 3中,常函數(shù)QString::simplifyWhiteSpace()就違反了這規(guī)則.即然它返回QString,而不是像它的名字所表述的那樣修改字符串. 在Qt 4中,這個函數(shù)被重命名為QString::simplified().

            參數(shù)名對于程序員來說是重要的信息來源,即使它們不出現(xiàn)在調(diào)用API的代碼中.既然現(xiàn)代的IDE會在程序員編碼時顯示這些參數(shù),所以非常值得在頭文件中給這些參數(shù)取恰當?shù)拿郑谖臋n中同樣使用相同的名字

            給布爾型的getter,setter,屬性的命名

            給布爾型的getter,setter,屬性取個恰當?shù)拿挚偸翘貏e困難.getter應(yīng)該叫checked() 或者還是叫isChecked(),取scrollBarsEnabled()還是areScrollBarEnabled()

            在Qt 4中,我們對于getter的函數(shù)使用下面的指導原則

            • 形容詞就使用is-前綴.比如:
              • isChecked()
              • isDown()
              • isEmpty()
              • isMovingEnabled()
              但是形容詞應(yīng)用到復數(shù)形式的名詞沒有前綴:
              • scrollBarsEnabled(), not areScrollBarsEnabled()
            • 動詞沒有前綴,也不使用第三人稱的(-s):
              • acceptDrops(), not acceptsDrops()
              • allColumnsShowFocus()
            • 名詞性的通常沒有前綴:
              • autoCompletion(), 不用isAutoCompletion()
              • boundaryChecking()
              有時候沒有前綴會產(chǎn)生誤導,在這種情就加上前綴is-:
              • isOpenGLAvailable(), not openGL()
              • isDialog(), not dialog()
              (如果函數(shù)叫做dialog(),我們通常會認定它會返回QDialog*類型)

            setter的命名可以從這推知,只要去掉is前綴,在名字前面加set前綴就可以了.比如setDown()setScrollBarsEnabled().屬性的名字跟getter一樣,就是沒有is前綴

            指針或引用?

            對于向外傳參,是使用指針,還是引用更好呢?

            void getHsv(int *h, int *s, int *v) const
            void getHsv(int &h, int &s, int &v) const
            

            絕大多數(shù)C++書籍都推薦無論何時都盡可能使用引用,因為從大多數(shù)情況來說,引用比指針有著所謂的"安全和優(yōu)雅".相比而方,在Trolltech,我們更趨向于指針,因為它使用戶代碼更具可讀性.比較下面的代碼:

            color.getHsv(&h, &s, &v);
            color.getHsv(h, s, v);
            

            只有第一行代碼能更清楚地說明h,s,v在函數(shù)被調(diào)用后,其值極有可能被修改.

            案例分析:QProgressBar

            為了在實際代碼中說明這些概念,我們以QProgressBar在Qt3和Qt4中的比較進行研究.在Qt 3中:

            class QProgressBar : public QWidget
            {
              ...
                public:
                    int totalSteps() const;
                    int progress() const;
                
                    const QString &progressString() const;
                    bool percentageVisible() const;
                    void setPercentageVisible(bool);
                
                    void setCenterIndicator(bool on);
                    bool centerIndicator() const;
                
                    void setIndicatorFollowsStyle(bool);
                    bool indicatorFollowsStyle() const;
                
                public slots:
                    void reset();
                    virtual void setTotalSteps(int totalSteps);
                    virtual void setProgress(int progress);
                    void setProgress(int progress, int totalSteps);
                
                protected:
                    virtual bool setIndicator(QString &progressStr,
                                              int progress,
                                              int totalSteps);
                    ...
                };
                

            對這個API進行改進的關(guān)鍵之處就是需要觀察到Qt 4中QProgressBar與QAbstractSpinBox,以及它的子類,QSpinBox,QSlider,和QDial有著相似性.解決的辦法呢?將其中的progress和totalSteps用minimun,maximum和value替換.

            增加valueChanged()的信號量.增加setRange()這一方便的函數(shù).

            接下來需要到progressString, percentageindicator實際上都指代同一東西:顯示在進度欄上的文本.通常這一文本是一百分數(shù),但是它能被setIndicator()設(shè)置成任何值.這里是新的API:

            virtual QString text() const;
            void setTextVisible(bool visible);
            bool isTextVisible() const;
            

            默認,這文本是百分比指示器.這可以用重新實現(xiàn)的text()進行改變.

            在Qt 3中,setCenterIndicator()setIndicatorFollowsStyle()是兩個影響對齊方式的函數(shù).它們現(xiàn)在都被一個高級的函數(shù)所取代,setAlignment()

            void setAlignment(Qt::Alignment alignment);
            

            如果程序員沒有調(diào)用 setAlignment(),對齊是基于的樣式?jīng)Q定的.對于Motif樣式,文本顯示在中間,而對于其他樣式,文本是右對齊的.

            這里是改進過的QProgressBar:

            class QProgressBar : public QWidget32
            {
                    ...
                public:
                    void setMinimum(int minimum);
                    int minimum() const;
                    void setMaximum(int maximum);
                    int maximum() const;
                    void setRange(int minimum, int maximum);
                    int value() const;
                
                    virtual QString text() const;
                    void setTextVisible(bool visible);
                    bool isTextVisible() const;
                    Qt::Alignment alignment() const;
                    void setAlignment(Qt::Alignment alignment);
                
                public slots:
                    void reset();
                    void setValue(int value);
                
                signals:
                    void valueChanged(int value);
                    ...
            };

            怎樣寫出正確的APIs

            APIs需要質(zhì)量保證.最早的版本一般都不是很好的,你必須測試它.通過調(diào)用這個API的代碼作為測試事例,來驗證代碼具有可讀性.

            另外的技巧包括讓人在沒有文檔和類文檔化(類的概述和函數(shù)說明)的情況下能夠使用這個API.

            當你陷入麻煩中時,文檔化也是好的辦法找出一個合適的命名:試著為這些類,函數(shù),枚舉值標住文檔,然后使用浮現(xiàn)在你腦中的第一個詞匯.如果你找不到精準的名字去表述,那很有可能這個東西就不應(yīng)存在.如果任何辦法都失敗了,而且你確信這個概念是有用的,那就發(fā)明一個新的名字吧.最后,不管怎么說,"widget", "event", "focus", and "buddy"這些詞總會能用上一個.

            posted @ 2008-05-11 20:07 len 閱讀(7854) | 評論 (6)編輯 收藏

            Glade是針對GTK+工具箱與GNOME桌面開發(fā)環(huán)境的快速圖形界面開發(fā)工具.用Glade設(shè)計的用戶接口以XML的文件形式保存,然后根據(jù)需要由程序通過libglade庫文件來動態(tài)加載.因為使用了libglade庫,Glade XML文件能夠被C,C++,Java,Perl,Python,C#等等語言所支持.針對其他未涉及的語言的支持也是方便的.

            在網(wǎng)上可以見到某些關(guān)于Glade的教程,大都是關(guān)于Linux平臺和Glade 2的,因為原先Glade作為快速開發(fā)工具,集成代碼生成功能,生成C文件.所以常常有初學者對網(wǎng)上某些教程所提及的"generate"(生成代碼)功能表示迷惑,在新版本的Glade-3上找不到對應(yīng)的功能.

            新版本的Glade-3是對原先Glade代碼的完全重寫.一個顯著的變化就是去除了代碼生成功能.這樣做是有原因的,即然代碼生成功能不被提倡使用,而是更鼓勵使用libglade功能.但是如果你真需要代碼生成功能的話,它還是可以做為插件來提供的.另一個顯著的不同是glade-3設(shè)計用來最大化使用GObject的自省機制(GObject introspection),來使外部工具箱和部件的控制,信號和屬性的集成更加容易.

            如果看過Say Hello to GTK+的話,可能感覺那樣的窗體程序太簡單了.那么現(xiàn)在讓我們借助Glade弄點兒復雜一點兒的界面吧.首先來瞧瞧Glade長什么樣,下圖就是Glade在windows下的界面.左邊的窗體的小部件選擇器,相當于調(diào)色板.中間是主菜單,右邊的是屬性窗體.

            glade_win

            現(xiàn)在開始創(chuàng)建一個類似于文本編輯器的圖形界面.按照上圖標注的順序,依次添加window部件,vertical box部件,menu bar部件,text view部件和Status部件.vertical box設(shè)置三行,它是用來進行界面布局,分割空間用,這是gtk+設(shè)計與傳統(tǒng)的windows UI設(shè)計很不同的地方.后三個部件是放置vertical box中的,最后設(shè)計完成圖形如下.保存取名為win.glade.如果你感興趣的話,可以用文件編輯器打開這個文件看看,正如所說的那樣,它是一個xml格式的文本文件.

            win

            現(xiàn)在我們設(shè)置相關(guān)的頭文件和庫文件,編輯一個glade.c文件,添加進以下的代碼,運行看看,會出現(xiàn)如上圖的對話框.雖然這個對話框什么都不干,但是通過Glade,我們能較為容易地設(shè)計界面,而不用通過gtk函數(shù),一個一個將控件實現(xiàn).

            #include <gtk/gtk.h>
            #include <glade/glade.h> int main(int argc, char* argv[]) { GladeXML *gxml; GtkWidget *window; gtk_init (&argc, &argv); gxml = glade_xml_new ("win.glade", NULL, NULL); window = glade_xml_get_widget (gxml, "hello"); g_object_unref (G_OBJECT (gxml)); gtk_widget_show (window); gtk_main (); return 0; }
            posted @ 2008-03-27 20:49 len 閱讀(10231) | 評論 (8)編輯 收藏
            僅列出標題
            共3頁: 1 2 3 

            <2025年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            www.久久热.com| 久久精品国产69国产精品亚洲| 久久露脸国产精品| 亚洲人成无码网站久久99热国产| 久久久中文字幕日本| 伊人色综合久久天天人手人婷| 亚洲AV无码成人网站久久精品大| 91久久精品视频| 亚洲AV无码一区东京热久久| 色狠狠久久AV五月综合| 久久艹国产| 久久男人Av资源网站无码软件| 久久中文字幕视频、最近更新| 人妻精品久久久久中文字幕一冢本 | 久久精品国产亚洲av水果派| 久久久久无码专区亚洲av| 69久久精品无码一区二区| 无码八A片人妻少妇久久| 久久天天躁狠狠躁夜夜avapp | 亚洲精品乱码久久久久久| 久久91精品综合国产首页| 久久久亚洲欧洲日产国码二区| 久久久艹| 精品国产婷婷久久久| 狠狠狠色丁香婷婷综合久久五月| 久久国产欧美日韩精品免费| 国产精品福利一区二区久久| 囯产精品久久久久久久久蜜桃| 亚洲国产精品嫩草影院久久 | 91精品国产综合久久精品| 久久久久亚洲AV成人网人人网站| 久久93精品国产91久久综合| 999久久久国产精品| 中文字幕久久欲求不满| 77777亚洲午夜久久多喷| 国产人久久人人人人爽| 欧美噜噜久久久XXX| 久久精品中文騷妇女内射| 99精品国产99久久久久久97| av色综合久久天堂av色综合在| 日韩精品久久无码人妻中文字幕|