天天看點

Boost使用入門 ---轉

suorce: http://blog.163.com/[email protected]/blog/static/6494412220086313853645/

0 摘要

--------------------------------------------------------------------------------

一直流傳這麼一個說法,想成為高手,一定要多讀高手寫的源代碼。哪些代碼是好材料呢?C++标準庫的源代碼?不,如果您讀過,就會發現:要麼是各種實作獨有的表達方式讓人摸不着頭腦,要麼是恐怖的代碼風格(如到處是下劃線)憋得人難受。Boost庫的代碼則相當清晰,注釋合理,命名規範,絕對是适合閱讀的典範。同時,Boost内容廣泛,數值計算、泛型程式設計、元程式設計、平台API……不妨從容選擇自己感興趣的部分,細細品味。

在本文中,我們将會介紹了Boost庫的下載下傳與安裝,并将體驗Boost庫中一個非常簡單實用的元件lexcial_cast。

1 Boost簡介

--------------------------------------------------------------------------------

Boost是什麼?一套開放源代碼、高度可移植的C++庫。

誰發起的?C++标準委員會庫工作組。是以,品質保證,不怕遇到假冒僞劣産品。

有些什麼呢?瞧瞧:

正規表達式,可以與POSIX API和Perl語言處理正規表達式的功能相媲美,而且還能支援各種字元類型(如char、wchar_t,甚至還可以是自定義字元類型);

多線程,想了很久的跨平台多線程庫了;

資料結構“圖”,再加上即将加入标準的hash_set、hash_map、hash_multiset、hash_multimap等等(事實上不少STL實作,如SGI STL,已經支援以上資料結構),C++對資料結構的支援已近完備;

python,沒錯,對Python語言的支援;

智能指針,與std::auto_ptr一起善加使用,可杜絕記憶體洩露,效率更不可和垃圾收集機制GC同日而語;

更有循環備援的CRC、可輕松定義傳回多個值函數的元組tuple、可容納不同類型值的any、對标準庫各方面的補充……

還在迅速擴大中,部分内容有望進入C++标準庫……

2 Boost下載下傳和Boost安裝

--------------------------------------------------------------------------------

去哪下載下傳Boost呢?英文http://www.boost.org (1),中文http://boost.c-view.org,可以找到一個.zip或.tar.gz格式的壓縮包。下載下傳完畢後,解壓到某個目錄,比如boost_1_26_0,裡面一般有這麼幾個子目錄:boost、libs、more、people、status、tools,看看沒問題就行了。

如果Boost更新時您懶得去下載下傳整個壓縮包,隻希望更新發生變動的檔案;或者您是一位跟我一樣的Boost Fans,希望跟蹤Boost的最新變化,不妨使用CVS方式。首先得有一個CVS用戶端軟體,比如CvsGui或http://sourceforge.net/projects/cvsgui/提供的WinCVS、gCVS和MacCVS,分别适用于Windows、Linux和MacOS平台。下載下傳、安裝、啟動三步曲。

如果您習慣于傳統CVS的指令行模式,那麼可在Admin→Command Line...→Command line settings中輸入下面一行2:

cvs -z3 -d:pserver:[email protected]:/cvsroot/boost checkout boost勾上下面的複選框,選擇本地目标目錄(比如可以建立一個C:\Boost,這憑個人愛好),再點選确定即可開始更新。如果是第一次運作,則可能需要一段時間下載下傳所有檔案。當然以後更新就隻需要很短的時間了。

如果您偏好GUI模式,請選擇Admin→Preferences...,在General的Enter CVS ROOT中填寫:

[email protected]:/cvsroot/boostAuthentication 選擇"passwd" file on the cvs server,同時Use version選擇cvs 1.10 (standard)。然後在WinCvs的HOME folder中填寫或選擇一個本地目标目錄,點選确定。選擇View→Browse Location→Change...換到本地目标目錄後,在Create→Check Module...→Checkout Settings的Enter the module name and path on the server中填寫boost,單擊确定即可。如果這一過程中要求輸入密碼,不必理會,直接回車就行。這是WinCVS 1.2的情況。如果您下載下傳的是新的版本,請注意各項設定大同小異,如前面的Authentication選擇pserver、不需要設定Use version等。

然後設定編譯器。

以Windows常用內建環境為例。Microsoft Visual C++ 6.0,可在工具→選擇→目錄處把Boost的路徑(如前面的boost_1_26_0)添加到Include Files搜尋路徑中。而對于Borland C++ Builder 5.0,則是在Project→Options→Directories/Conditionals→Include Path中添加Boost的路徑。還有一種比較常用的Dev-C++ 4.0(内置GNU C++,可從http://www.bloodshed.net處免費下載下傳),可在Options→Compile Options→Directories→C++ include files處添加Boost的路徑即可。其他IDE類似。至于指令行方式,則需在編譯時對相應的頭檔案路徑參數(Borland C++ Compiler、GNU C++是-I,VC++的cl是/I)給出Boost路徑。

做到這一步,恭喜您,大部分Boost庫就可以用了。

為什麼不是全部?首先,目前還沒有一個能完全符合C++标準的編譯器,是以Boost庫中的元件或多或少不可用,詳細資訊請看Boost網站上“編譯器支援情況(Compiler Status)”一文。另外,有些庫需要Build相應的lib或dll檔案。不過這樣的庫很少,主要是由于平台相關性的原因,如處理正規表達式的 regex庫、支援python語言的python庫等,而建構庫的過程相當煩瑣,需要使用Jam工具(可以簡單提一下:在 tools/build/jam_src/builds目錄下有三個檔案win32-borlandc.mk、win32-gcc.mk、win32- visualc.mk,分别是适用于Windows平台下的Borland C++ Compiler、GNU C++和Visual C++的mak檔案。如果在Unix平台,則應使用tools/build/Makefile。用指令行工具make或nmake來做出Jam執行檔案,然後再用Jam來建構庫,詳細内容可見Boost.Build文檔)。我個人的建議是,不用急着去建構lib或dll。真的需要使用這些庫時,再make 随庫提供的mak檔案即可。雖然Boost.Jam也許是Boost庫未來發展的方向,不過畢竟絕大部分庫都無須建構,可以直接使用。

3 Boost元件lexical_cast

--------------------------------------------------------------------------------

這次我們先挑個簡單實用的Boost元件,看看Boost能給我們帶來怎樣的便利。

3.1 字元串→數值

在CSDN論壇上經常看到詢問如何在字元串類型和數值類型間進行轉換的問題,也看到了許多不同的答案。下面先讨論一下從字元串類型到數值類型的轉換。

如何将字元串"123"轉換為int類型整數123?答案是,用标準C的庫函數atoi;

如果要轉換為long類型呢?标準C的庫函數atol;

如何将"123.12"轉換為double類型呢?标準C的庫函數atod;

如果要轉換為long double類型呢?标準C的庫函數atold;

……

後來有朋友開始使用标準庫中的string類,問這個如何轉換為數值?有朋友答曰,請先轉換為const char*。我很佩服作答者有數學家的思維:把陌生的問題轉化成熟悉的問題。(曾經有一則笑話,好事者問數學家:知道如何燒水嗎?答:知道。把水壺加滿水,點火燒。又問:如果水壺裡已經有水了呢?答:先倒掉,就轉化為我熟悉的問題了……)

不,不,這樣是C的做法,不是C++。那麼,C++該怎麼做呢?使用Boost Conversion Library所提供的函數lexical_cast(需要引入頭檔案boost/lexical_cast.hpp)無疑是最簡單友善的。如:

#include <boost/lexical_cast.hpp>

#include <iostream>

int main()

{

        using boost::lexical_cast;

        int a = lexical_cast<int>("123");

        double b = lexical_cast<double>("123.12");

        std::cout<<a<<std::endl

        std::cout<<b<<std::endl;

        return 0;

}一個函數就簡潔地解決了所有的問題。

3.2 數值→字元串

那麼從數值類型到字元串類型呢?

用itoa?不對吧,标準C/C++裡根本沒有這個函數。即使在Windows平台下某些編譯器提供了該函數3,沒有任何移植性不說,還隻能解決int類型(也許其他函數還可以解決long、unsigned long等類型),浮點類型又怎麼辦?當然,辦法還是有,那就是:sprintf。

char s[100];

sprintf(s, "%f", 123.123456);不知道諸位對C裡的scanf/printf系列印象如何,總之阿炯我肯定記不住那些稀奇古怪的參數,而且如果寫錯了參數,就會得到莫名其妙的輸出結果,調試起來可就要命了(我更讨厭的是字元數組,空間開100呢,又怕太小裝不下;開100000呢,總覺得太浪費,心裡憋氣,好在C++标準為我們提供了string這樣的字元串類)。這時候,lexical_cast就出來幫忙啦。

#include <boost/lexical_cast.hpp>

#include <string>

#include <iostream>

int main()

{

        using std::string;

        const double d = 123.12;

        string s = boost::lexical_cast<string>(d);

        std::cout<<s<<std::endl;

        return 0;

}跟前面一樣簡單。

3.3 異常

如果轉換失敗,則會有異常bad_lexical_cast抛出。該異常類是标準異常類bad_cast的子類。

#include <boost/lexical_cast.hpp>

#include <iostream>

int main()

{

        using std::cout;

        using std::endl;

        int i;

        try{

                i = boost::lexical_cast<int>("abcd");

        }

        catch(boost::bad_lexical_cast& e)

        {

                cout<<e.what()<<endl;

                return 1;

        }

        cout<<i<<endl;

        return 0;

}顯然“abcd”并不能轉換為一個int類型的數值,于是抛出異常,捕捉後輸出“bad lexical cast: source type value could not be interpreted as target”這樣的資訊。

3.4 注意事項

lexical_cast依賴于字元流std::stringstream(會自動引入頭檔案4),其原理相當簡單:把源類型讀入到字元流中,再寫到目标類型中,就大功告成。例如

int d = boost::lexical_cast<int>("123");就相當于

int d;

std::stringstream s;

s<<"123";

s>>d;既然是使用了字元流,當然就有些随之而來的問題,需要特别指出5。

由于Visual C++ 6的本地化(locale)部分實作有問題,是以如果使用了非預設的locale,可能會莫名其妙地抛出異常。當然,一般情況下我們并不需要去改變預設的locale,是以問題不是很大。

輸入資料必須“完整”地轉換,否則抛出bad_lexical_cast異常。例如

int i = boost::lexical_cast<int>("123.123"); // this will throw 便會抛出異常。因為“123.123”隻能“部分”地轉換為123,不能“完整”地轉換為123.123。

浮點數的精度問題。

std::string s = boost::lexical_cast<std::string>(123.1234567);以上語句預想的結果是得到“123.1234567”,但是實際上我們隻會得到“123.123”,因為預設情況下std::stringstream的精度是6(這是C語言程式庫中的“前輩”printf留下的傳統)。這可以說是boost::lexical_cast的一個bug。怎麼辦呢?權宜之計,可以這麼做:打開頭檔案<boost/lexical_cast.hpp>,注意對照修改6:

#include <boost/limits.hpp>

//...

template<typename Target, typename Source>

Target lexical_cast(Source arg) {

        //...

        Target result;

        interpreter.precision(std::numeric_limits<Source>::digits10);

        if( !(interpreter << arg) ||

        !(interpreter >> result) ||

        !(interpreter >> std::ws).eof())

        //...

}即可得到正确結果。當然,理論上效率會有一點點損失,不過幾乎可以忽略不計。

4 小結

--------------------------------------------------------------------------------

我們已經體驗了boost::lexcial_cast。當然,lexical_cast不僅僅局限于字元串類型與數值類型之間的轉換:可在任意可輸出到stringstream的類型和任意可從stringstream輸入的類型間轉換。這次的了解盡管很粗略,不過畢竟我們已經“走進Boost”,而不僅僅是“走近”。以後,我們可以自行領略Boost的動人之處啦。

5 注釋

--------------------------------------------------------------------------------

[1] 如果您通路Boost英文網站出現DNS錯誤,不妨試試http://64.226.201.52/。

[2] 請參考Boost文檔中的“下載下傳與安裝說明(Boost Download and Installation)”部分。

[3] Borland C++ Builder提供了itoa,而Microsoft Visual C++提供了一個功能相同的函數,不過名字是_itoa。

[4] 有些不符合标準的标準庫實作中,字元流類名是strstream,在頭檔案中。而标準規定的是stringstream,在頭檔案中。

[5] 請參考http://groups.yahoo.com/group/boost/message/15023的讨論。

[6] 非常感謝Andrew Koenig和Bjarne Stroustrup兩位的指教和幫助。最開始我的想法是,指定最大精度,加入interpreter.precision(15)之類的語句,然而又擔心移植性的問題。Andrew Koenig先生給出了非常明确的解釋:You are quite correct that 15 is not portable across all floating-point implementations. However, it is portable across all implementations that support IEEE floating-point arithmetic, which is most computers that are in common use today. If you want to do better than that, you might consider using numeric_limits::digits10, which is the number of significant base-10 digits that can be accurately represented in a double.(中文大意是,誠然,15并非可移植到所有浮點實作中,但對于支援IEEE浮點運算的實作來說,則的确是可移植的,而且,這也是現今絕大部分計算機所使用的。如果想做得更好一點,則可以考慮使用numeric_limits::digits10,就能表示出10進制下double能精确表達的位數。)

From:http://www.stlchina.org/twiki/bin/view.pl/Main/BoostEnterBoost