本文將介紹幾個 Boost 實用工具類,包括 tuple、static_assert、pool、random 和 program_options等等。須要對標準 STL 具有必定的瞭解才能充分理解本文的內容。
1.boost::tuple 類
有時,但願 C++ 函數返回多個不相關的值。在推出 STL 以前,實現此目的的方法是建立全部不相關變量的結構,並以指針或引用的形式返回它們或做爲參數傳遞給函數——可是任一種方法都不是表達程序員意圖的方法。STL引入了 pair,可將其用於聚合不相關的數據部分,但它一次只能處理兩個數據對象。爲了使用 int、 char 和 float 的元組(tuple ),能夠按以下方式返回 pair:
make_pair<int, pair<char, float> > (3, make_pair<char, float> ('a', 0.9));
隨着您添加更多的元素,建立元組結構將變得愈來愈困難。Boost tuple 類型派上了用場。要使用 boost::tuple,必須包括頭文件 tuple.hpp。要執行元組比較和組 I/O,您須要分別包括 tuple_comparison.hpp 和tuple_io.hpp。
第一個使用元組的程序
清單 1 使用 int、char 和 float 的元組並打印內容。
清單 1. 建立 Boost 元組並打印內容
#include <iostream>
#include <tuple.hpp>
#include <tuple_comparison.hpp>
#include <tuple_io.hpp>
using namespace boost;
int main ( )
{
tuple<int, char, float> t(2, 'a', 0.9);
std::cout << t << std::endl;
return 0;
}
此代碼的輸出爲 (2 a 0.9)。請注意,<< 運算符重載 std::ostream,以便經過轉儲每一個單獨的 tuple 元素來輸出元組。
與元組相關的重要事實
在使用元組時,務必牢記如下事實:
可以造成元組的元素數量目前僅限於 10 個。
元組能夠包含用戶定義的類類型,可是您必須負責確保那些類已經定義了正確的構造函數和拷貝構造函數 (copy constructor)。清單 2 顯示了產生編譯時錯誤的代碼部分,由於該拷貝構造函數是私有的。
清單 2. 用於元組的類必須具備正確的拷貝構造函數
#include <tuple.hpp>
#include <tuple_comparison.hpp>
#include <tuple_io.hpp>
#include <iostream>
using namespace std;
class X
{
int x;
X(const X& u) { x = u.x; }
public:
X(int y=5) : x(y) { }
};
int main ( )
{
boost::tuple<int, X> t(3, X(2));
return 0;
}
與 STL 提供的 make_pair 函數很是相似,Boost 提供了 make_tuple 例程。要從函數返回元組,您必須調用make_tuple。能夠建立具備臨時元素的元組;清單 3 的輸出爲 (4 0)。
清單 3.使用 make_tuple 來從函數返回元組
#include <tuple.hpp>
#include <tuple_comparison.hpp>
#include <tuple_io.hpp>
#include <iostream>
using namespace std;
boost::tuple<int, int>
divide_and_modulo(int a, int b)
{
return boost::make_tuple<int, int> (a/b, a%b);
}
int main ( )
{
boost::tuple<int, int> t = divide_and_modulo(8, 2);
cout << t << endl;
return 0;
}
要訪問元組的各個元素,您可使用 get 例程。此例程具備兩種變體,如清單 4 所示。請注意,還可使用 get 例程來設置元組的各個元素,雖然有些編譯器可能不支持此功能。
清單 4. 使用 boost::get 例程
#include <tuple.hpp>
#include <tuple_comparison.hpp>
#include <tuple_io.hpp>
#include <iostream>
using namespace std;
boost::tuple<int, int>
divide_and_modulo(int a, int b)
{
return boost::make_tuple<int, int> (a/b, a%b);
}
int main ( )
{
boost::tuple<int, int> t = divide_and_modulo(8, 2);
cout << t.get<0> () << endl; // prints 4
cout << boost::get<1>(t) << endl; // prints 0
boost::get<0>(t) = 9; // resets element 0 of t to 9
++boost::get<0>(t); // increments element 0 of t
cout << t.get<1>() << endl; // prints 10
return 0;
}
可使用 const 限定符來聲明元組,在這種狀況下,用於訪問特定元素的 get 調用將返回對 const 的引用。不能對以這種方式訪問的元素進行賦值(請參見清單 5)。
清單 5. 使用 const 限定符來聲明的元組不可修改
#include <tuple.hpp>
#include <tuple_comparison.hpp>
#include <tuple_io.hpp>
#include <iostream>
using namespace std;
int main ( )
{
const boost::tuple<int, char*> t(8, "Hello World!");
t.get<1> ()[0] = "Y"; // error!
boost::get<0>(t) = 9; // error!
return 0;
}
可使用關係運算符 ==、!=、<、>、<= 和 >= 對相同長度的元組進行比較。比較不一樣長度的元組會產生編譯時錯誤。這些運算符的工做原理是從左到右地比較兩個參與元組的每一個單獨的元素(請參見清單 6)。
清單 6. 關係運算符與元組
#include <tuple.hpp>
#include <tuple_comparison.hpp>
#include <tuple_io.hpp>
#include <iostream>
#include <string>
using namespace std;
int main ( )
{
boost::tuple<int, string> t(8, string ("Hello World!"));
boost::tuple<int, string> t2(8, string("Hello World!"));
cout << (t == t2) << endl;
boost::tuple<int, string> r(9, string ("Hello World!"));
boost::tuple<int, string> r2(8, string("Hello World!"));
cout << (r > r2) << endl;
boost::tuple<string, string> q(string ("AA"), string("BB"));
boost::tuple<string, string> q2(string("AA"), string ("CC"));
cout << (q < q2) << endl;
return 0;
}
清單 6 的輸出爲 1 1 1。請注意,若是您不是使用 string 或 int,而是使用沒有定義 ==、!= 等運算符的用戶定義的隨機類,則會產生編譯錯誤。
2.Boost 靜態斷言
斷言是 C/C++ 中的防錯性程序設計的一部分。最多見的用法以下:
assert(<some expression you expect to be true at this point in code>);
assert 例程僅在調試模式下有效。在發佈模式下,一般使用預處理器宏 ¨CDNDEBUG 來編譯代碼,其效果至關於assert 不存在。靜態斷言創建在這個基本概念之上,只不過靜態斷言僅在編譯時有效。此外,靜態斷言不生成任何代碼。
例如,假設您在一個整型變量中執行某個位操做,並預期其大小爲 4:這並不是在全部操做系統平臺上都是如此(請參見清單 7)。
清單 7. 使用 Boost 靜態斷言來驗證變量的大小
#include <boost/static_assert.hpp>
int main ( )
{
BOOST_STATIC_ASSERT(sizeof(int) == 4);
// … other code goes here
return 0;
}
要使用 BOOST_STATIC_ASSERT 宏,您必須包括 static_assert.hpp 頭文件。不須要諸如 DNDEBUG 等特定於編譯器的選項,而且您不須要向連接器提供庫——單憑該頭文件就足夠了。
若是斷言有效,則代碼將順利編譯。可是若是該假設無效,在某些 64 位平臺上就多是如此,則編譯器將生成錯誤消息並中止。使用 g++-3.4.4 進行編譯時的典型消息以下:
assert.cc: In function `int main()':
assert.cc:8: error: incomplete type `boost::STATIC_ASSERTION_FAILURE< false>'
used in nested name specifier
這確定不是最詳細的錯誤消息,可是它指出了具備錯誤假設的函數和確切行號。
下面是一些典型的現實情景,您應該在其中考慮使用靜態斷言:
靜態聲明的數組的邊界檢查
驗證原始和用戶定義的變量的大小
容許模板類或函數僅使用某些數據類型來進行實例化
Boost 靜態斷言的行爲
您能夠在類、函數或命名空間範圍中使用 Boost 靜態斷言;還能夠與模板一塊兒使用它們。清單 8 中的示例闡明瞭概念。
清單 8. 使用 Boost 靜態斷言來限制類實例化
#include <iostream>
#include <static_assert.hpp>
using namespace std;
using namespace boost;
template<class T>
class A
{
private:
T x, y;
BOOST_STATIC_ASSERT(numeric_limits<T>::is_signed);
public:
A(T x1, T y1) : x(x1), y(y1) { }
};
int main ( )
{
A<unsigned long> a(2, 1);
return 0;
}
在清單 8 中,僅當 T 有符號時,模板類 A 才能進行實例化。類 numeric_limits 是標準命名空間的一部分;它檢查基本類型在給定操做系統平臺上的屬性。在無符號(unsigned )的 long 類型的狀況下,專用變體numeric_limits<unsigned int> 的 is_signed 標誌爲 false。當您在類範圍中使用 BOOST_STATIC_ASSERT時,它是私有的、受保護的仍是公開的並不重要。
清單 9 將 BOOST_STATIC_ASSERT 與函數結合在一塊兒使用。該代碼確保在函數 f1 中處理的類型只能是 A 類型或其派生類型。經過使用 Boost 的靜態斷言宏和 is_convertible 例程(在boost/type_traits/is_convertible.hpp 中定義),此代碼確保不但願的類型不會最終調用此例程。
清單 9. 將函數限制爲僅處理特定的數據類型
#include <iostream>
#include <static_assert.hpp>
#include <boost/type_traits/is_convertible.hpp>
using namespace std;
using namespace boost;
struct A
{
int a;
float b;
};
struct B : public A
{
};
template <typename T>
int f1 (T y)
{
BOOST_STATIC_ASSERT ((is_convertible<T, A*>::value));
return 0;
}
int main ( )
{
f1<B*> (new B);
return 0;
}
3.使用 Boost 庫生成隨機數
隨機數生成用於各類各樣的計算機應用,例如安全和遊戲。UNIX 系統通常附帶了隨機數生成例程 rand 和 srand。一般,srand 使用新的種子值來初始化 rand(請參見清單 10)。
清單 10. 用於在傳統 UNIX 中生成隨機數的代碼
#include <stdlib.h>
#include <stdio.h>
int main ( )
{
srand(time(NULL)); // this introduces randomness
for (int i=0; i<10; i++)
printf("%d\n", rand());
return 0;
}
rand 例程返回一個介於 0 和 stdlib.h 中定義的 RAND_MAX 之間的數字。要了解 srand 所作的工做,能夠在將srand 例程註釋掉的狀況下編譯清單 11。當您這樣作時,您將觀察到 rand 並不真正是隨機的——可執行代碼每次打印同一組值。爲了在代碼中引入隨機性,您可使用 srand,此例程使用種子值來初始化 rand。因爲每次調用程序時的時間值是不一樣的,所以對於不一樣的調用,代碼打印的值不一樣。
使用 Boost 隨機數生成器
Boost 隨機數生成器位於 boost/random 文件夾中。此外,爲方便起見,boost/ 目錄中的 random.hpp 頭文件包括了 boost/random 文件夾中的全部其餘頭文件。
Boost 隨機接口劃分爲兩個部分:隨機數生成器和隨機數必須位於其中的分佈。本文討論 uniform_int 和uniform_real random-number 分佈以及 mt19937 隨機數生成器。清單 11 使用了 uniform_int 和uniform_real 分佈。
清單 11. 將 variate_generator 與 mt19937 引擎和 uniform_int 分佈一塊兒使用
#include <iostream>
#include <boost/random.hpp>
using namespace std;
using namespace boost;
int main ( )
{
uniform_int<> distribution (1, 100) ;
mt19937 engine ;
variate_generator<mt19937, uniform_int<> > myrandom (engine, distribution);
for (int i=0; i<100; ++i)
cout << myrandom() << endl;
return 0;
}
此代碼生成介於 1 和 100 之間(包括 1 和 100)的隨機數;用於實現隨機化的基礎引擎是 mt19937。variate_generator 爲您組合了該引擎和分佈。
清單 12 使用了另外一個引擎: kreutzer1986.
清單 12:組合 uniform_real 分佈和 kreutzer1986 引擎
#include <iostream>
#include <boost/random.hpp>
using namespace std;
using namespace boost;
int main ( )
{
uniform_real<> distribution(1, 2) ;
kreutzer1986 engine ;
variate_generator<kreutzer1986, uniform_real<> > myrandom (engine, distribution);
for (int i=0; i<100; ++i)
cout << myrandom() << endl;
return 0;
}
除了 uniform_int 和 uniform_real 分佈之外,Boost 還提供了幾個其餘分佈,包括二項式、泊松和正態分佈。
4.boost::pool 庫概述
Boost pool 庫引入了可用於實現快速內存分配的工具。正確的內存塊對齊能夠獲得保證。
根據 Boost 文檔所述,當您分配和釋放許多小型對象時,建議使用池。使用池的另外一個不太明顯的優勢在於,做爲程序員,您沒必要擔憂內存泄露:內存由 Boost 庫在內部自動進行管理。要使用 pool 庫,您沒必要在連接時提供特定的庫——單憑頭文件就足以完成連接了。
有多個接口對 pool 庫可用:
池接口——替代 malloc 進行工做的普通接口。要使用此接口,須要包括 boost/pool 文件夾中的 pool.hpp 頭文件。
對象池接口——有對象意識的接口,在對象建立和刪除過程當中分別相應地調用構造函數和析構函數。還可使用此接口建立普通對象,而不調用它們的構造函數。接口定義是在位於 boost/pool 目錄中的 object_pool.hpp 頭文件中提供的。清單 13 引入了 pool 和 object_pool 接口。請注意如下幾點:
pool 接口須要知道每一個單獨的元素而不是類型的大小,由於它是一個 malloc 風格的分配程序,不會調用構造函數。
pool 接口中的 malloc 例程返回 void*。
object-pool 接口須要類型信息,由於要調用構造函數。
object-pool 接口中的 malloc/construct 例程返回指向類型的指針。malloc 例程不調用構造函數,可是construct 要調用構造函數。
使用 pool 接口或 object-pool 接口來建立的元素的範圍與從中建立它們的池的範圍相同。
要從池接口中釋放內存,能夠調用 purge_memory 方法。該方法釋放您先前建立的內存塊,並使得從分配程序例程返回的全部指針失效。
要釋放各個元素,能夠調用 pool 接口中的 free 例程。例如,若是 t 是使用 pool 接口來建立的池,而且 m 是從 t分配的指針,則 t.free(m) 將把內存返回給 t(將其添加到 t 的空閒內存列表)。
清單 13. pool 和 object_pool 接口
#include <iostream>
#include <boost/pool/pool.hpp>
#include <boost/pool/object_pool.hpp>
using namespace std;
using namespace boost;
class A
{
public: A( ) { cout << "Declaring An"; }
~A( ) { cout << "Deleting An"; }
};
int main ( )
{
cout << "Init pool...n";
pool<> p(10 * sizeof(A));
for (int i=0; i<10; ++i)
A* a = (A*) p.malloc(); // Always returns sizeof(A)
p.purge_memory();
cout << "Init object pool...n";
object_pool<A> q;
for (int i=0; i<10; ++i)
A* a = q.construct(); // Calls A's constructor 10 times
return 0;
}
singleton_pool 接口——與 pool 接口幾乎相同,可是用做獨立池。獨立池的底層結構具備爲 malloc、free 等聲明的靜態成員函數,而且構造函數是私有的。獨立池聲明中的第一個參數稱爲標記— —它容許存在不一樣的獨立池集(例如,用於 int 的多個池,其中每一個池服務於不一樣的目的)。必須包括 singleton_pool.hpp 頭文件才能使用此接口。請參見清單 14。
清單 14. singleton_pool 接口
#include <iostream>
#include <boost/pool/singleton_pool.hpp>
using namespace std;
using namespace boost;
struct intpool { };
struct intpool2 { };
typedef boost::singleton_pool<intpool, sizeof(int)> ipool1;
typedef boost::singleton_pool<intpool2, sizeof(int)> ipool2;
int main ( )
{
cout << "Init singleton pool...n";
for (int i=0; i<10; ++i) {
int* q1 = (int*) ipool1::malloc();
int* q2 = (int*) ipool2::malloc();
}
ipool1::purge_memory();
ipool2::purge_memory();
return 0;
}
pool_alloc 接口——一般與 STL 容器結合在一塊兒使用。請考慮如下代碼片斷: #include <boost/pool/pool_alloc.hpp>
std::vector<int, boost::pool_allocator<int> > v;
std::list<double, boost::fast_pool_allocator<double> > L;
存在兩個分配程序:pool_allocator 和 fast_pool_allocator。第一個分配程序是通用分配,能夠知足針對任何數量的連續內存塊的請求。fast_pool_allocator 最適合於一次請求單個(一般較大)塊,可是也適用於通用分配,不過具備一些性能缺點。
5.boost::program_options 簡介
命令行處理是另外一個難點,開發人員一般不會採用結構化的方式來解決。其結果是從頭至尾維護開銷。Boost program_options 庫提供了簡化命令行處理的例程和數據結構。
清單 15 詳細描述了 boost::program_options 的使用。這是建議在您的代碼中使用的標準模板。
清單 15. 使用 boost::program_options
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main (int ac, char* av[])
{
boost::program_options::options_description options("command line options");
options.add_options() ("help", "Use -h or --help to list all arguments")
("file", boost::program_options::value<string> (),
"Provide input file name");
boost::program_options::variables_map vmap;
boost::program_options::store(
boost::program_options::parse_command_line(ac, av, options), vmap);
boost::program_options::notify(vmap);
if (vmap.count("help")) {
cout << options << endl;
}
return 0;
}
您必須包括 program_options.hpp 頭文件。清單 15 的工做方式以下:
options_description 類聲明全部的有效命令行選項。
使用方法 add_options,您能夠註冊命令和跟在命令後面的參數類型。在此例中,help 選項不須要任何參數,可是file 選項須要一個字符串參數。
variables_map 類在運行時存儲命令行選項及其參數。
Boost 的 parse_command_line 例程解析 argc 和 argv 參數。store 和 notify 方法幫助存儲 vmap 對象中的數據。
當您檢查 help 是否爲程序的恰當命令行選項(這是 vmap.count("help") 所作的工做)時,options 對象將被轉儲到 cout。這意味着運算符 << 是爲 options_description 類定義的。
下面是來自清單 15 的輸出:
[user@/home/user1] ./a.out --help
command line options:
--help Use -h or --help to list all arguments
--file arg Provide input file name
當您遇到其餘選項時,能夠採起進一步的操做。例如,下面的代碼片斷通過了修改,以打印您輸入的文件名:
…
if (vmap.count("file")) {
cout << "Setting input file to " << vmap["file"].as<string>() << ".n";
} else {
cout << "No file specifiedn";
}
…
請注意,variable_map 類在許多方面與哈希表很是類似。例如,要檢索 file 參數,您能夠調用 vmap["file"]。
提供多個參數和縮寫的命令選項
命令行處理一般同時須要同一個命令選項的短名稱和長名稱。此外,您一般必須屢次使用某個選項,以便收集該選項的全部參數。例如,您可能但願使用 ¨Ch 和 ¨Chelp 來打印可用的命令。清單 16 演示了這些功能。
清單 16. 使用較短的選項變體並容許屢次調用命令選項
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main (int ac, char* av[])
{
boost::program_options::options_description options("command line options");
options.add_options() ("help,h", "Use -h or --help to list all arguments")
("file", boost::program_options::value<vector<string> >( ),
"Provide input file name");
boost::program_options::variables_map vmap;
boost::program_options::store(
boost::program_options::parse_command_line(ac, av, options), vmap);
boost::program_options::notify(vmap);
if (vmap.count("help")) {
cout << options << endl;
}
if (vmap.count("file")) {
vector<string> ifiles(vmap["file"].as< vector<string> > ());
vector<string>::iterator vI;
cout << "Number of input files: " << ifiles.size() << endl;
cout << "Input file list: " << endl;
for(vI = ifiles.begin(); vI != ifiles.end(); ++vI)
cout << "t" << *vI << endl;
} else {
cout << "No file specifiedn";
}
return 0;
}
在使用 add_options 來添加命令選項時,較長和較短的選項之間使用逗號進行分隔。請注意,較長的選項 (help)必須在較短的選項 (h) 以前,代碼才能正常工做。與使用單個字符串不一樣,file 選項如今是使用一個字符串向量來定義的。若是指定了 ¨Cfile 選項屢次,則會將在全部指定中收集到的命令選項參數存儲在關聯的 vector<string>中。下面是使用不一樣的參數來屢次指定 ¨Ch 和 ¨Cfile 所得到的輸出:
[user@/home/user1] ./a.out -h
command line options:
-h [ --help ] Use -h or --help to list all arguments
--file arg Provide input file name
No file specified
[user@/home/user1] ./a.out --file abc --file pqr
Number of input files: 2
Input file list:
abc
pqr
解析位置選項
帶輸入參數可是不帶命令行選項來調用某個程序是很是廣泛的。您預期參數和命令行選項之間自動存在某種神奇關聯。這種行爲由 boost::program_options 提供支持。
請考慮清單 17。第一個參數轉換爲 --file=<first parameter>,第二個參數轉換爲 --do- file=<second parameter>。
清單 17. 將位置參數與命令行選項相關聯
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main (int ac, char* av[])
{
boost::program_options::options_description options("command line options");
options.add_options() ("help,h", "Use -h or --help to list all arguments")
("file", boost::program_options::value<string>(),
"Provide input file name")
("do-file", boost::program_options::value<string>(),
"Specify commands file");
boost::program_options::variables_map vmap;
boost::program_options::positional_options_description poptd;
poptd.add ("file", 1);
poptd.add("do-file", 2);
boost::program_options::store(
boost::program_options::command_line_parser(ac, av).
options(options).positional(poptd).run(), vmap);
boost::program_options::notify(vmap);
if (vmap.count("file")) {
cout << "file: " << vmap["file"].as<string> ( ) << endl;
}
if (vmap.count("do-file")) {
cout << "do- file: " << vmap["do-file"].as<string> ( ) << endl;
}
return 0;
}
下面是輸出內容:
[user@/home/user1] ./a.out file1 dofile1
file: file1
do-file: dofile1
清單 15 中使用的某些 API 在清單 17 中已發生更改。清單 17 引入了新的類 positional_options_description。該類的 add 方法(add("command option", N))將位置 N 處的輸入參數與命令行選項 "command option" 相關聯。所以,./a.out file1 在內部解析爲 ./a.out ¨Cfile=file1。另外一個區別在於調用 program_options::store方法的方式。與使用 parse_command_line 例程不一樣,Boost 庫要求您將 command_line_parser 例程與store 方法結合在一塊兒使用。
請注意,仍然可使用 ¨Cfile 和 ¨Cdo-file 選項來調用該程序。最後,若要將全部的輸入參數與同一個命令行選項相關聯,您須要使用值 -1 將該命令行選項添加到 positional_options_description 對象。下面是代碼:
…
boost::program_options::positional_options_description poptd;
poptd.add ("file", -1);
後面待續!!!
參考http://it.100xuexi.com/view/otdetail/20130917/78dbf753-e6f9-4335-ad88-018942e01934.html