Add New Notes

This commit is contained in:
geekard
2012-08-08 14:26:04 +08:00
commit 5ef7c20052
2374 changed files with 276187 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-19T22:22:45+08:00
====== BOOST FOREACH 介绍 ======
Created Sunday 19 February 2012
http://www.cnblogs.com/sld666666/archive/2011/05/30/2063140.html
“Make simple things easy.”
-- Larry Wall
c++中,写一个循环去迭代一个序列是很单调的。
1 string hello("hello, boost!");
2
3 for (int i = 0; i != hello.size(); ++i)
4 {
5 cout<<hello.at(i);
6 }
我们可以用__std:for_each__,但是这样并没有减少代码了,而且让很多功能分离太远
1 void print (char ch)
2 {
3 cout << ch;
4 }
5 int _tmain(int argc, _TCHAR* argv[])
6 {
7 string hello("hello, boost!");
8 __for_each__(hello.begin(), hello.end(),print);
9
10 cout<<endl;
11
12 return 0;
13 }
BOOST_FOREACH 是为了__易用性和高效性__而设计的。它不进行动态的内存分配,没有虚拟函数调用或通过函数指针的调用。这样可以生成近似于最优化的代码。
1 string hello("hello, boost!");
2 __BOOST_FOREACH(char ch, hello)__
3 {
4 cout<< ch;
5 }
6
7 cout<<endl;
BOOST_FOREACH **支持所有序列式容器。**
当然,为了更漂亮我们可以这样改造
1 #define foreach BOOST_FOREACH
2 #define reverse_foreach BOOST_REVERSE_FOREACH

View File

@@ -0,0 +1,727 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T16:15:11+08:00
====== Boost Serialization 库 ======
Created Tuesday 14 February 2012
http://www.ibm.com/developerworks/cn/aix/library/au-boostserialization/
Arpan Sen, 独立作家
简介: Boost C++ 库让编写优秀的代码变得很容易,但出了问题时该怎么办?本文将介绍 Boost Serialization 库,了解如何在您的代码中采用__序列化技术__,让以后的调试变得更容易。
发布日期: 2012 年 1 月 04 日
级别: 中级
原创语言: 英文
访问情况 : 516 次浏览
评论: 0 (查看 | 添加评论 - 登录)
平均分 0 星 共 0 个评分 平均分 (0个评分)
为本文评分
===== 简介 =====
当您熬了几个通宵才编出的程序突然在客户站点上崩溃时,您可能会感到束手无策,因为__没有测试用例__可用来帮您再现灾难现场,因此也就无法进行调试。这是很多人都很熟悉的场景,但对于此问题,人们关心的更多的是__如何解决它__? 只是__转储堆栈追踪信息__显然并不是一个好办法。您需要**深入了解代码的数据结构**并检查它们的值。
Boost Serialization 是一个解决方案。__可以将程序内容转出到归档文件(文本或 XML 文件)中,并从该归档文件中恢复数据,重新生成一个崩溃之前的代码快照__。这听起来还不错吧?让我们接着往下看。
Serialization 源来自标准 Boost 安装包(参阅 参考资料)。与其他 Boost 库不同的是,Serialization 并__不是一个只包含头文件的库,因此需要lianjie它__。为此,请参阅安装包中的构建说明(参阅 参考资料)。如果您喜欢利用现成的安装,请参见 boostpro(仍请参阅 参考资料)。在本文中,我使用的是 1.46.1 版本的 Boost ,并使用 gcc-4.3.4 编译代码。
===== 使用了 Boost Serialization 的 Hello World =====
在执行更重要的任务之前,我们先来验证一下概念。在以下的 清单 1 中,您会看到一个字符串,它的**值被转储到一个归档文件中**。在以下的 清单 2 中,将此归档文件的内容恢复,以验证此字符串的值是否与原来相符。
清单 1. 将字符串内容保存到文本归档文件中
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <fstream>
void save()
{
std::ofstream file("archive.txt");
__boost::archive::text_oarchive__ oa(file);
std::string s = "Hello World!\n";
oa << s;
}
int main()
{
save();
}
现在将内容加载回来。
清单 2. 将字符串的内容加载到文本归档文件中
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <fstream>
void load()
{
std::ifstream file("archive.txt");
__boost::archive::text_iarchive__ ia(file);
std::string s;
ia >> s;
std::cout << s << std::endl;
}
int main()
{
load();
}
不出所料,清单 2 的输出内容是 “Hello World”。
现在,我们仔细看一下代码。Boost 创建了一个**文本归档文件**(一个文本文件),它含有需要转储的内容。为了转储这些内容,您需要创建了一个 text_oarchive。为了恢复内容,您还创建了一个 text_iarchive,并分别在头文件 text_oarchive.hpp 和 text_iarchive.hpp 中声明它。转储和恢复内容很直观,使用 << 和 >> 运算符,其工作原理非常__类似于流 I/O__,除了要将内容转储到文件中,然后过一段时间以后再从该文件中恢复。
不过,您可能想只用一个 & 运算符完成转储和恢复操作,而不是使用上述的两个不同运算符。下面的 清单 3 显示了相关的使用方法。
清单 3. 使用 & 运算符执行 “转储 - 恢复” 操作
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <fstream>
void save()
{
std::ofstream file("archive.txt");
boost::archive::text_oarchive oa(file);
std::string s = "Hello World!\n";
** oa & s; **// has same effect as oa << s;
}
void load()
{
std::ifstream file("archive.txt");
boost::archive::text_iarchive ia(file);
std::string s;
** ia & s;**
std::cout << s << std::endl;
}
我们来看一下转储的文本文件:
22 serialization::archive 9 13 Hello World!
请注意,在以后的 Boost 版本中,文本文件的内容和格式可能有所不同,因此应用程序代码最好不要依赖于内部归档文件内容。
===== 创建一个 XML 归档文件 =====
如果您想使用 __XML 归档文件__,而不是文本归档文件,则必须包含来自 Boost 源的头文件 xml_iarchive.hpp 和 xml_oarchive.hpp。这些头文件声明或定义了 XML 归档文件语义。但是,该 “转储 - 恢复” 操作与应用于文本归档文件的 “转储 - 恢复” 操作仍有些微不同之处:**需要将数据打包到一个名为 BOOST_SERIALIZATION_NVP 的宏中**。下面的 清单 4 提供了相关的代码。
清单 4. 从 XML 归档文件执行 “转储 - 恢复” 操作
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <iostream>
#include <fstream>
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
std::string s = "Hello World!\n";
__oa & BOOST_SERIALIZATION_NVP(s);__
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
std::string s;
__ia & BOOST_SERIALIZATION_NVP(s); __
std::cout << s << std::endl;
}
清单 5 显示了 XML 归档文件的内容。变量名可充当标记 (<s>Hello World!</s>)。
清单 5. XML 归档文件的内容
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<s>Hello World!</s>
</boost_serialization>
===== 还有其他哪些内容可以序列化? =====
以下内容才是精华。无需额外编码,就可以将 C++ 编程语言中的很多元素序列化。__类、类指针、数组和 Standard Template Library (STL) 集合__都可以被序列化。下面的 清单 6 提供了一个包含数组的样例。
清单 6. 对整数数组执行 “转储 - 恢复” 操作
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
int array1[] = {34, 78, 22, 1, 910};
oa & BOOST_SERIALIZATION_NVP(array1);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
int restored[5]; // Need to specify expected array size
ia >> BOOST_SERIALIZATION_NVP(restored);
__ std::ostream_iterator<int> oi(std::cout, " ");__
std::copy(a, a+5, oi);
}
int main()
{
save();
load();
}
这很简单。转储过程与字符串类一样;但在恢复过程中,需要指定预期的数组大小。否则,程序会崩溃。清单 7 提供了 清单 6 中代码的已转储的 XML 归档文件。
清单 7. 通过数组转储创建的 XML 归档文件
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<array1>
<count>5</count>
<item>34</item>
<item>78</item>
<item>22</item>
<item>1</item>
<item>910</item>
</array1>
</boost_serialization>
是否可以仅通过指定指针 int* restored 完成此操作并为您恢复数组?答案是否定的。必须每次都指定大小。如果认真回答此问题的话,答案是对基本类型的指针进行序列化非常复杂。
===== 序列化 STL 集合 =====
要序列化 STL 列表和向量,则必须了解每个 STL 类型,应用程序代码必须包含来自 Serialization 源的具有类似名称的头文件。对于列表,需要包含 boost/serialization/list.hpp 等等。请注意,对于列表和向量,在将信息加载回来的过程中,不需要提供任何大小和范围信息,这也是相对于具有相同功能的应用程序容器,人们更喜欢使用 STL 容器的另一个原因。清单 8 显示了用于序列化 STL 集合的代码。
清单 8. 序列化 STL 集合
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
float array[] = {34.2, 78.1, 22.221, 1.0, -910.88};
std::list<float> L1(array, array+5);
std::vector<float> V1(array, array+5);
oa & BOOST_SERIALIZATION_NVP(L1);
oa & BOOST_SERIALIZATION_NVP(V1);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
std::list<float> L2;
ia >> BOOST_SERIALIZATION_NVP(L2); // No size/range needed
std::vector<float> V2;
ia >> BOOST_SERIALIZATION_NVP(V2); // No size/range needed
std::ostream_iterator<float> oi(std::cout, " ");
std::copy(L2.begin(), L2.end(), oi);
std::copy(V2.begin(), V2.end(), oi);
}
清单 9 显示了一个 XML 归档文件。
清单 9. 使用 STL 容器的转储归档文件
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<L1>
<count>5</count>
<item_version>0</item_version>
<item>34.200001</item>
<item>78.099998</item>
<item>22.221001</item>
<item>1</item>
<item>-910.88</item>
</L1>
<V1>
<count>5</count>
<item_version>0</item_version>
<item>34.200001</item>
<item>78.099998</item>
<item>22.221001</item>
<item>1</item>
<item>-910.88</item>
</V1>
</boost_serialization>
回页首
===== 序列化自己的类型 =====
是否想序列化自己的类型?毫无疑问您是愿意的!我们将采用一个表示日期的结构作为一个简单的示例:
typedef struct date {
unsigned int m_day;
unsigned int m_month;
unsigned int m_year;
} date;
要对某个类进行序列化,则必须__在类定义中定义一个名为 serialize 的方法__。在转储和恢复类的过程中会调用该方法。以下是对 serialize 方法的声明:
template<class Archive>
void serialize(Archive& archive, const unsigned int version)
{
//… your custom code here
}
在第二段代码片段中,serialize 是一个模板函数,第一个参数应该是对 Boost 归档文件的引用。那么,XML 归档文件的代码会是什么样呢?请看以下代码:
template<class Archive>
void serialize(Archive& archive, const unsigned int version)
{
archive & BOOST_SERIALIZATION_NVP(m_day);
archive & BOOST_SERIALIZATION_NVP(m_month);
archive & BOOST_SERIALIZATION_NVP(m_year);
}
下面的 清单 10 提供了完整代码。
清单 10. 日期类型的 “转储 - 恢复”
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <iostream>
#include <fstream>
typedef struct date {
unsigned int m_day;
unsigned int m_month;
unsigned int m_year;
date( int d, int m, int y) : m_day(d), m_month(m), m_year(y)
{}
date() : m_day(1), m_month(1), m_year(2000)
{}
friend std::ostream& operator << (std::ostream& out, date& d)
{
out << "day:" << d.m_day
<< " month:" << d.m_month
<< " year:" << d.m_year;
return out;
}
template<class Archive>
void serialize(Archive& archive, const unsigned int version)
{
archive & BOOST_SERIALIZATION_NVP(m_day);
archive & BOOST_SERIALIZATION_NVP(m_month);
archive & BOOST_SERIALIZATION_NVP(m_year);
}
} date;
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
date d(15, 8, 1947);
oa & BOOST_SERIALIZATION_NVP(d);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
date dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
std::cout << dr;
}
请注意,除了定义 serialize 方法之外,并没有为了处理用户定义类型而执行任何特殊操作。以上代码运行正常,但有一个很明显的问题:可能需要序列化来自第三方的类型,而它们的类声明可能是无法修改的。对于这种情况,您__可以使用 serialize 的非侵入性(non-intrusive)版本,以便可以在类范围之外定义该方法__。下面的 清单11 显示了 date 类的非侵入性 serialize 方法。请注意,如果 serialize 方法已在全局范围内定义,该代码仍然有效;但是,好的编程实践是在相关命名空间内定义该方法。
清单 11. serialize 方法的非侵入性版本
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive& archive, date& d, const unsigned int version)
{
archive & BOOST_SERIALIZATION___NVP__(d.m_day);
archive & BOOST_SERIALIZATION_NVP(d.m_month);
archive & BOOST_SERIALIZATION_NVP(d.m_year);
}
} // namespace serialization
} // namespace boost
清单 12 显示了 date 类型的 XML 归档文件。
清单 12. 用户定义类型的 XML 归档文件
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<d class_id="0" tracking_level="0" version="0">
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d>
</boost_serialization>
===== 处理类层次结构 =====
类通常是从其他类派生的,因此您需要找到一种能够__在序列化派生类的同时序列化基类的方法__。对于基类和派生类,都必须定义 serialize 方法。另外,还需要调整派生类的 serialize 定义,如 清单 13 所示。
清单 13. 序列化基类
template<class Archive>
void serialize(Archive& archive, const unsigned int version)
{
// serialize base class information
archive & boost::serialization::base_object<Base Class>(*this);
// serialize derived class members
archive & derived-class-member1;
archive & derived-class-member2;
// …
}
直接在派生类的 serialize 方法中调用基类的 serialize 方法是一个很糟糕的想法。尽管能够这么做,但是无法追踪类版本控制(稍后讲述),或者无法消除生成的归档文件中的冗余。避免这种错误的一种推荐编码方式是在所有类中将 serialize 方法设置为 private,并在所有将要序列化的类中使用 friend class boost::serialization::access 声明。
===== 通过基类指针转储派生类 =====
通过指针转储派生类是完全有可能的;但是,该类和派生类都应该有各自的已定义的 serialize 方法。还有,您需要在转储和恢复过程中调用以下方法。
<archive name>.register_type<derived-type name>( )
假设 date 类是从一个名为 base 的类派生出来的。清单 14 显示了应该在 save 和 load 方法中加入哪些代码。
清单 14. 使用基类指针实现序列化
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
oa.register_type<date>( );
base* b = new date(15, 8, 1947);
oa & BOOST_SERIALIZATION_NVP(b);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
ia.register_type<date>( );
base *dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
date* dr2 = dynamic_cast<date*> (dr);
std::cout << dr2;
}
在这里,在转储和恢复过程中使用了 base 指针。但序列化的实际上是 date 对象。在转储和恢复之前,已经在这两个类中注册了 date 类型。
===== 在执行 “转储 - 恢复” 操作过程中使用对象指针 =====
可以使用对象指针进行转储和恢复。这样做很有意思。您期望什么样的 XML 归档文件的内容?当然,转储指针值不会有问题。您需要转储实际对象,然后将其还原。此外,指向同一个对象的多个指针又该如何呢?如果 XML 归档文件具有同一个已转储对象的多个副本,那么显然不太好。Boost Serialization 的一大优势是无论在何处(包括用于指针),所用的语法几乎都是一样的。以下的 清单15 是 清单 10 的修改版。
清单 15. 使用指针执行 “转储 - 恢复” 操作
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
date* d = new date(15, 8, 1947);
std::cout << d << std::endl;
oa & BOOST_SERIALIZATION_NVP(d);
// … other code follows
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
date* dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
std::cout << dr << std::endl;
std::cout << *dr;
}
请注意,在此清单中,d 和 dr 的值是不同的,但内容相同。清单 16 显示了 清单 15 代码的 XML 归档文件。
清单 16. 使用指针的 XML 归档文件
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<d class_id="0" tracking_level="1" version="0" object_id="_0">
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d>
</boost_serialization>
现在,请考虑以下情况:将两个指针转储到同一个对象并观察用于相同对象的归档文件是什么样子的。清单 17 对 清单 15 中的代码稍微进行了一些修改。
清单 17. 使用指针执行 “转储 - 恢复” 操作
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
date* d = new date(15, 8, 1947);
std::cout << d << std::endl;
oa & BOOST_SERIALIZATION_NVP(d);
date* d2 = d;
oa & BOOST_SERIALIZATION_NVP(d2);
// … other code follows
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
date* dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
std::cout << dr << std::endl;
std::cout << *dr;
date* dr2;
ia >> BOOST_SERIALIZATION_NVP(dr2);
std::cout << dr2 << std::endl;
std::cout << *dr2;
}
下面的 清单 18 提供了 清单 17 中代码的 XML 归档文件。观察如何处理第二个指针;此外,只有一个对象被转储。
清单 18. 包含作为指针的 d2 的 XML 归档文件
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<d class_id="0" tracking_level="1" version="0" object_id="_0">
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d>
<d2 class_id_reference="0" object_id_reference="_0"></d2>
</boost_serialization>
对于引用的处理方法与用户应用程序代码中一样。尽管如此,请注意,在恢复过程中,创建了两个独特的对象。因此,归档文件应该保存两个具有相同值的对象。与使用指针时的情况不同,以下是归档文件的内容,其中的 d2 是清单 17 中的一个引用(参见下面的 清单 19)。
清单 19.包含作为 d 的引用 d2 的 XML 归档文件
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<d class_id="0" tracking_level="0" version="0">
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d>
<d2>
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d2>
</boost_serialization>
===== 将 serialize 拆分成 save 和 load =====
有时候,您不想使用同样的 serialize 方法来转储和恢复对象。在这种情况下,您可以将 serialize 方法拆分成两个方法,即 save 和 load,它们具有类似的签名。这两个方法都是之前定义的 serialize 方法的一部分。此外,需要添加 BOOST_SERIALIZATION_SPLIT_MEMBER 宏作为类定义的一部分。清单 20 显示了这些方法是什么样子。
清单 20. 将 serialize 方法拆分成 save 和 load 方法
template<class Archive>
void save(Archive& archive, const unsigned int version) const
{
//…
}
template<class Archive>
void load(Archive& archive, const unsigned int version)
{
//…
}
BOOST_SERIALIZATION_SPLIT_MEMBER( ) // must be part of class
注意 save 方法签名后的 const。如果没有 const 限定符,该代码将无法编译。对于 date 类,清单 21 显示了这些方法现在是什么样子。
清单 21. date 类的 save 和 load 方法
template<class Archive>
void save(Archive& archive, const unsigned int version) const
{
archive << BOOST_SERIALIZATION_NVP(m_day);
archive << BOOST_SERIALIZATION_NVP(m_month);
archive << BOOST_SERIALIZATION_NVP(m_year)
}
template<class Archive>
void load(Archive& archive, const unsigned int version)
{
archive >> BOOST_SERIALIZATION_NVP(m_day);
archive >> BOOST_SERIALIZATION_NVP(m_month);
archive >> BOOST_SERIALIZATION_NVP(m_year)
}
BOOST_SERIALIZATION_SPLIT_MEMBER( ) // must be part of class
===== 了解版本控制 =====
serialize、save 和 load 的方法签名都使用无符号整数版本作为最后一个参数。这些数字有什么用?随着时间变化,类的内部变量名称可能发生变化,添加新的字段或移除已有字段,等等。这是软件开发过程中的自然进程,除了归档文件仍然保存着关于数据类型原有状态的信息。为了规避这个问题,需要使用版本号。
我们举一个 date 类的例子。假设您在 date 类中引入一个名为 m_tag、类型为 string 的字段。该类以前的版本以版本 0 的形式在归档文件中转储,如 清单 12 所示。下面的 清单 22 显示了该类的 load 方法(您可能已经使用了serialize,但这里使用 load 提供了更清晰的实现)。
清单 22. 使用版本控制处理更新的类字段
template<class Archive>
void load(Archive& archive, const unsigned int version)
{
archive >> BOOST_SERIALIZATION_NVP(m_day);
archive >> BOOST_SERIALIZATION_NVP(m_month);
archive >> BOOST_SERIALIZATION_NVP(m_year);
if (version > 0)
archive >> BOOST_SERIALIZATION_NVP(m_tag);
}
很明显,合理使用版本控制可以让该代码与早先版本软件中的旧归档文件一起使用。
===== 使用共享指针 =====
共享指针是一项经常使用且功能极其强大的编程技术。再次强调,Boost Serialization 一个主要优势在于能轻松__将共享指针序列化__,并保持目前所知的语法不变。唯一要注意的是,必须在应用程序代码中包含 boost/serialization/shared_ptr.hpp 头文件。从修改 清单 15 并使用 boost::shared_ptr 指针代替普通指针开始。代码如下面的 清单 23 所示。
清单 23. 使用共享指针执行 “转储 - 恢复” 操作
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
boost::shared_ptr<date> d (new date(15, 8, 1947));
oa & BOOST_SERIALIZATION_NVP(d);
// … other code follows
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
boost::shared_ptr<date> dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
std::cout << *dr;
}
序列化搞定了一切?还没有。关于 Serialization 支持什么,不支持什么,还有一些使用限制。例如,如果指向栈对象的指针的转储早于实际对象,那么 Boost Serialization 就会崩溃。需要先转储对象,然后才能转储指向对象的指针(请注意,指针可以单独转储,不需要先转储对象)。参见 清单 24 中的示例:
清单 24. 指向栈对象的指针需要在实际对象之后转储
void save()
{
std::ofstream file("archive.xml");
boost::archive::xml_oarchive oa(file);
date d(15, 8, 1947);
std::cout << d << std::endl;
date* d2 = &d;
oa & BOOST_SERIALIZATION_NVP(d);
oa & BOOST_SERIALIZATION_NVP(d2);
}
void load()
{
std::ifstream file("archive.xml");
boost::archive::xml_iarchive ia(file);
date dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
std::cout << dr << std::endl;
date* dr2;
ia >> BOOST_SERIALIZATION_NVP(dr2);
std::cout << dr2 << std::endl;
}
在此清单中,无法在 d 之前转储 d2。如果查看 XML 归档文件,就会很清楚这一点:d2 作为 d 的一个引用进行转储的(参见 清单 25)。
清单 25. 在 XML 归档文件中,对象及其指针均被转储
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<d class_id="0" tracking_level="1" version="0" object_id="_0">
<d.m_day>15</d.m_day>
<d.m_month>8</d.m_month>
<d.m_year>1947</d.m_year>
</d>
<d2 class_id_reference="0" object_id_reference="_0"></d2>
</boost_serialization>
如果有多个指针指向同一个对象,Serialization 会使用 class_id_reference 将指针与原对象关联起来(每个对象都有一个唯一的类 ID)。原对象的每个后续指针都会将 object_id_reference 改成 _1、_2,以此类推。
===== 结束语 =====
本文到这就结束了。您已经了解了 Boost Serialization 究竟是什么;如何创建和使用文本与 XML 归档文件;以及还有如何转储和恢复普通的旧的数据类型(STL 集合、类、类指针、共享指针和数组)。本文还简单介绍了如何使用序列化和版本控制来处理类的层次结构。序列化是一个功能强大的工具。在代码中,可以通过对序列化进行善加利用使调试变得更轻松。

View File

@@ -0,0 +1,692 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T19:26:41+08:00
====== Boost Thread学习笔记 ======
Created Tuesday 14 February 2012
http://www.blogjava.net/LittleDS/archive/2008/05/18/201236.html
thread自然是boost::thread库的主角,但thread类的实现总体上是比较简单的,前面已经说过,thread只是一个跨平台的线程封装库,其中按照所使用的编译选项的不同,分别决定使用 Windows线程API还是pthread,或者Macintosh Carbon平台的thread实现。以下只讨论Windows,即使用 BOOST_HAS_WINTHREADS的情况。
thread类提供了两种构造函数:
thread::thread()
thread::thread(const function0<void>& threadfunc)
第一种构造函数用于调用GetCurrentThread构造一个当前线程的thread对象,第二种则通过传入一个函数或者一个functor来创建一个新的线程。第二种情况下,thread类在其构造函数中间接调用CreateThread来创建线程,并将线程句柄保存到成员变量m_thread中,并执行传入的函数,或执行functor的operator ()方法来启动工作线程。
我们可以用以下三种方式启动一个新线程:
1、传递一个工作函数来构造一个工作线程
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <iostream>
4
5 boost::mutex io_mutex;
6
7 void count() // worker function
8 {
9 for (int i = 0; i < 10; ++i)
10 {
11 boost::mutex::scoped_lock lock(io_mutex);
12 std::cout << i << std::endl;
13 }
14 }
15
16 int main(int argc, char* argv[])
17 {
18 boost::thread thrd1(&count);
19 boost::thread thrd2(&count);
20 thrd1.join();
21 thrd2.join();
22
23 return 0;
24 }
25
2、传递一个functor对象来构造一个工作线程
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <iostream>
4
5 boost::mutex io_mutex;
6
7 struct count
8 {
9 count(int id) : id(id) { }
10
11 void operator()()
12 {
13 for (int i = 0; i < 10; ++i)
14 {
15 boost::mutex::scoped_lock lock(io_mutex); // lock io, will be explained soon.
16 std::cout << id << ": " << i << std::endl;
17 }
18 }
19
20 int id;
21 };
22
23 int main(int argc, char* argv[])
24 {
25 boost::thread thrd1(count(1));
26 boost::thread thrd2(count(2));
27 thrd1.join();
28 thrd2.join();
29 return 0;
30 }
31
3、无需将类设计成一个functor,借助bind来构造functor对象以创建工作线程
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <boost/bind.hpp>
4 #include <iostream>
5
6 boost::mutex io_mutex;
7
8 struct count
9 {
10 static int num;
11 int id;
12
13 count() : id(num++) {}
14
15 int do_count(int n)
16 {
17 for (int i = 0; i < n; ++i)
18 {
19 boost::mutex::scoped_lock lock(io_mutex);
20 std::cout << id << ": " << i << std::endl;
21 }
22 return id;
23 }
24 };
25
26 int count::num = 1;
27
28 int main(int argc, char* argv[])
29 {
30 count c1;
31 boost::thread thrd1(boost::bind(&count::do_count, &c1, 10));
32 thrd1.join();
33 return 0;
34 }
其中bind是一个函数模板,它可以根据后面的实例化参数构造出一个functor来,上面的boost::bind(&count::do_count, &c1, 10)其实等价于返回了一个functor:
struct countFunctor
{
int operator() ()
{
(&c1)->do_count(10); // just a hint, not actual code
}
};
因此,以后就跟2中是一样的了。
除了thread,boost::thread另一个重要组成部分是mutex,以及工作在mutex上的boost::mutex::scoped_lock、condition和barrier,这些都是为实现线程同步提供的。
mutex
boost提供的mutex有6种:
boost::mutex
boost::try_mutex
boost::timed_mutex
boost::recursive_mutex
boost::recursive_try_mutex
boost::recursive_timed_mutex
下面仅对boost::mutex进行分析。
mutex类是一个CriticalSection(临界区)封装类,它在构造函数中新建一个临界区并InitializeCriticalSection,然后用一个成员变量
void* m_mutex;
来保存该临界区结构。
除此之外,mutex还提供了do_lock、do_unlock等方法,这些方法分别调用EnterCriticalSection、 LeaveCriticalSection来修改成员变量m_mutex(CRITICAL_SECTION结构指针)的状态,但这些方法都是private的,以防止我们直接对mutex进行锁操作,所有的锁操作都必须通过mutex的友元类detail::thread::lock_ops<mutex>来完成,比较有意思的是,lock_ops的所有方法:lock、unlock、trylock等都是static的,如lock_ops<Mutex>::lock的实现:
1 template <typename Mutex>
2 class lock_ops : private noncopyable
3 {
4
5 public:
6 static void lock(Mutex& m)
7 {
8 m.do_lock();
9 }
10
11 }
boost::thread的设计者为什么会这么设计呢?我想大概是:
1、boost::thread的设计者不希望被我们直接操作mutex,改变其状态,所以mutex的所有方法都是private的(除了构造函数,析构函数)。
2、虽然我们可以通过lock_ops来修改mutex的状态,如:
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <boost/thread/detail/lock.hpp>
4
5 int main()
6 {
7 boost::mutex mt;
8 //mt.do_lock(); // Error! Can not access private member!
9
10 boost::detail::thread::lock_ops<boost::mutex>::lock(mt);
11
12 return 0;
13 }
但是,这是不推荐的,因为mutex、scoped_lock、condition、barrier是一套完整的类系,它们是相互协同工作的,像上面这么操作没有办法与后面的几个类协同工作。
scoped_lock
上面说过,不应该直接用lock_ops来操作mutex对象,那么,应该用什么呢?答案就是scoped_lock。与存在多种mutex一样,存在多种与mutex对应的scoped_lock:
scoped_lock
scoped_try_lock
scoped_timed_lock
这里我们只讨论scoped_lock。
scoped_lock是定义在namespace boost::detail::thread下的,为了方便我们使用(也为了方便设计者),mutex使用了下面的typedef:
typedef detail::thread::scoped_lock<mutex> scoped_lock;
这样我们就可以通过:
boost::mutex::scoped_lock
来使用scoped_lock类模板了。
由于scoped_lock的作用仅在于对mutex加锁/解锁(即使mutex EnterCriticalSection/LeaveCriticalSection),因此,它的接口也很简单,除了构造函数外,仅有lock/unlock/locked(判断是否已加锁),及类型转换操作符void*,一般我们不需要显式调用这些方法,因为scoped_lock的构造函数是这样定义的:
1 explicit scoped_lock(Mutex& mx, bool initially_locked=true)
2 : m_mutex(mx), m_locked(false)
3 {
4 if (initially_locked) lock();
5 }
注:m_mutex是一个mutex的引用。
因此,当我们不指定initially_locked参数构造一个scoped_lock对象时,scoped_lock会自动对所绑定的mutex加锁,而析构函数会检查是否加锁,若已加锁,则解锁;当然,有些情况下,我们可能不需要构造时自动加锁,这样就需要自己调用lock方法。后面的condition、barrier也会调用scoped_lock的lock、unlock方法来实现部分方法。
正因为scoped_lock具有可在构造时加锁,析构时解锁的特性,我们经常会使用局部变量来实现对mutex的独占访问。
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <iostream>
4
5 boost::mutex io_mutex;
6
7 void count() // worker function
8 {
9 for (int i = 0; i < 10; ++i)
10 {
11 boost::mutex::scoped_lock lock(io_mutex);
12 std::cout << i << std::endl;
13 }
14 }
15
16 int main(int argc, char* argv[])
17 {
18 boost::thread thrd1(&count);
19 boost::thread thrd2(&count);
20 thrd1.join();
21 thrd2.join();
22
23 return 0;
24 }
在每次输出信息时,为了防止整个输出过程被其它线程打乱,通过对io_mutex加锁(进入临界区),从而保证了输出的正确性。
在使用 scoped_lock时,我们有时候需要使用全局锁(定义一个全局mutex,当需要独占访问全局资源时,以该全局mutex为参数构造一个 scoped_lock对象即可。全局mutex可以是全局变量,也可以是类的静态方法等),有时候则需要使用对象锁(将mutex定义成类的成员变量),应该根据需要进行合理选择。
Java的synchronized可用于对方法加锁,对代码段加锁,对对象加锁,对类加锁(仍然是对象级的),这几种加锁方式都可以通过上面讲的对象锁来模拟;相反,在Java中实现全局锁好像有点麻烦,必须将请求封装到类中,以转换成上面的四种 synchronized形式之一。
condition
condition的接口如下:
1 class condition : private boost::noncopyable // Exposition only
2 {
3 public:
4 // construct/copy/destruct
5 condition();
6 ~condition();
7
8 // notification
9 void notify_one();
10 void notify_all();
11
12 // waiting
13 template<typename ScopedLock> void wait(ScopedLock&);
14 template<typename ScopedLock, typename Pred> void wait(ScopedLock&, Pred);
15 template<typename ScopedLock>
16 bool timed_wait(ScopedLock&, const boost::xtime&);
17 template<typename ScopedLock, typename Pred>
18 bool timed_wait(ScopedLock&, Pred);
19 };
其中wait用于等待某个condition的发生,而timed_wait则提供具有超时的wait功能,notify_one用于唤醒一个等待该condition发生的线程,notify_all则用于唤醒所有等待该condition发生的线程。
由于condition的语义相对较为复杂,它的实现也是整个boost::thread库中最复杂的(对Windows版本而言,对支持pthread的版本而言,由于pthread已经提供了pthread_cond_t,使得condition实现起来也十分简单),下面对wait和notify_one进行简要分析。
condition内部包含了一个condition_impl对象,由该对象执行来处理实际的wait、notify_one...等操作。
下面先对condition_impl进行简要分析。
condition_impl在其构造函数中会创建两个Semaphore(信号量):m_gate、m_queue,及一个Mutex(互斥体,跟boost::mutex类似,但boost::mutex是基于CriticalSection<临界区>的):m_mutex,其中:
m_queue
相当于当前所有等待线程的等待队列,构造函数中调用CreateSemaphore来创建Semaphore时,lMaximumCount参数被指定为(std::numeric_limits<long>::max)(),即便如此,condition的实现者为了防止出现大量等待线程的情况(以至于超过了long的最大值),在线程因执行condition::wait进入等待状态时会先:
WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
以等待被唤醒,但很难想象什么样的应用需要处理这么多线程。
m_mutex
用于内部同步的控制。
但对于m_gate我很奇怪,我仔细研究了一下condition_imp的实现,还是不明白作者引入m_gate这个变量的用意何在,既然已经有了用于同步控制的m_mutex,再引入一个m_gate实在让我有点不解。
以下是condition::wait调用的do_wait方法简化后的代码:
1 template <typename M>
2 void do_wait(M& mutex)
3 {
4 m_impl.enter_wait();
5 lock_ops::unlock(mutex, state); //对传入的scoped_lock对象解锁,以便别的线程可以对其进行加锁,并执行某些处理,否则,本线程等待的condition永远不会发生(因为没有线程可以获得访问资源的权利以使condition发生)
6 m_impl.do_wait(); //执行等待操作,等待其它线程执行notify_one或notify_all操作以获得
7 lock_ops::lock(mutex, state); //重新对scoped_lock对象加锁,获得独占访问资源的权利
8 }
condition::timed_wait的实现方法与此类似,而notify_one、notify_all仅将调用请求转发给m_impl,就不多讲了。
虽然condition的内部实现比较复杂,但使用起来还是比较方便的。下面是一个使用condition的多Producer-多Consumer同步的例子:
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <boost/thread/condition.hpp>
4 #include <boost/thread/xtime.hpp>
5
6 #include <iostream>
7 #include <time.h> // for time()
8
9 #include <Windows.h> // for Sleep, change it for other platform, we can use
10 // boost::thread::sleep, but it's too inconvenient.
11
12 typedef boost::mutex::scoped_lock scoped_lock;
13 boost::mutex io_mutex;
14
15 class Product
16 {
17 int num;
18 public:
19 Product(int num) : num(num) {}
20
21 friend std::ostream& operator<< (std::ostream& os, Product& product)
22 {
23 return os << product.num;
24 }
25 };
26
27 class Mediator
28 {
29 private:
30 boost::condition cond;
31 boost::mutex mutex;
32
33 Product** pSlot; // product buffer/slot
34 unsigned int slotCount, // buffer size
35 productCount; // current product count
36 bool stopFlag; // should all thread stop or not
37
38 public:
39 Mediator(const int slotCount) : slotCount(slotCount), stopFlag(false), productCount(0)
40 {
41 pSlot = new Product*[slotCount];
42 }
43
44 virtual ~Mediator()
45 {
46 for (int i = 0; i < static_cast<int>(productCount); i++)
47 {
48 delete pSlot[i];
49 }
50 delete [] pSlot;
51 }
52
53 bool Stop() const { return stopFlag; }
54 void Stop(bool) { stopFlag = true; }
55
56 void NotifyAll() // notify all blocked thread to exit
57 {
58 cond.notify_all();
59 }
60
61 bool Put( Product* pProduct)
62 {
63 scoped_lock lock(mutex);
64 if (productCount == slotCount)
65 {
66 {
67 scoped_lock lock(io_mutex);
68 std::cout << "Buffer is full. Waiting" << std::endl;
69 }
70 while (!stopFlag && (productCount == slotCount))
71 cond.wait(lock);
72 }
73 if (stopFlag) // it may be notified by main thread to quit.
74 return false;
75
76 pSlot[ productCount++ ] = pProduct;
77 cond.notify_one(); // this call may cause *pProduct to be changed if it wakes up a consumer
78
79 return true;
80 }
81
82 bool Get(Product** ppProduct)
83 {
84 scoped_lock lock(mutex);
85 if (productCount == 0)
86 {
87 {
88 scoped_lock lock(io_mutex);
89 std::cout << "Buffer is empty. Waiting" << std::endl;
90 }
91 while (!stopFlag && (productCount == 0))
92 cond.wait(lock);
93 }
94 if (stopFlag) // it may be notified by main thread to quit.
95 {
96 *ppProduct = NULL;
97 return false;
98 }
99
100 *ppProduct = pSlot[--productCount];
101 cond.notify_one();
102
103 return true;
104 }
105 };
106
107 class Producer
108 {
109 private:
110 Mediator* pMediator;
111 static unsigned int num;
112 unsigned int id; // Producer id
113
114 public:
115 Producer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }
116
117 void operator() ()
118 {
119 Product* pProduct;
120 srand( (unsigned)time( NULL ) + id ); // each thread need to srand differently
121 while (!pMediator->Stop())
122 {
123 pProduct = new Product( rand() % 100 );
124 // must print product info before call Put, as Put may wake up a consumer
125 // and cause *pProuct to be changed
126 {
127 scoped_lock lock(io_mutex);
128 std::cout << "Producer[" << id << "] produces Product["
129 << *pProduct << "]" << std::endl;
130 }
131 if (!pMediator->Put(pProduct)) // this function only fails when it is notified by main thread to exit
132 delete pProduct;
133
134 Sleep(100);
135 }
136 }
137 };
138
139 unsigned int Producer::num = 1;
140
141 class Consumer
142 {
143 private:
144 Mediator* pMediator;
145 static unsigned int num;
146 unsigned int id; // Consumer id
147
148 public:
149 Consumer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }
150
151 void operator() ()
152 {
153 Product* pProduct = NULL;
154 while (!pMediator->Stop())
155 {
156 if (pMediator->Get(&pProduct))
157 {
158 scoped_lock lock(io_mutex);
159 std::cout << "Consumer[" << id << "] is consuming Product["
160 << *pProduct << "]" << std::endl;
161 delete pProduct;
162 }
163
164 Sleep(100);
165 }
166 }
167 };
168
169 unsigned int Consumer::num = 1;
170
171 int main()
172 {
173 Mediator mediator(2); // we have only 2 slot to put products
174
175 // we have 2 producers
176 Producer producer1(&mediator);
177 boost::thread thrd1(producer1);
178 Producer producer2(&mediator);
179 boost::thread thrd2(producer2);
180 // and we have 3 consumers
181 Consumer consumer1(&mediator);
182 boost::thread thrd3(consumer1);
183 Consumer consumer2(&mediator);
184 boost::thread thrd4(consumer2);
185 Consumer consumer3(&mediator);
186 boost::thread thrd5(consumer3);
187
188 // wait 1 second
189 Sleep(1000);
190 // and then try to stop all threads
191 mediator.Stop(true);
192 mediator.NotifyAll();
193
194 // wait for all threads to exit
195 thrd1.join();
196 thrd2.join();
197 thrd3.join();
198 thrd4.join();
199 thrd5.join();
200
201 return 0;
202 }
barrier
barrier类的接口定义如下:
1 class barrier : private boost::noncopyable // Exposition only
2 {
3 public:
4 // construct/copy/destruct
5 barrier(size_t n);
6 ~barrier();
7
8 // waiting
9 bool wait();
10 };
barrier类为我们提供了这样一种控制线程同步的机制:
前n - 1次调用wait函数将被阻塞,直到第n次调用wait函数,而此后第n + 1次到第2n - 1次调用wait也会被阻塞,直到第2n次调用,依次类推。
barrier::wait的实现十分简单:
1 barrier::barrier(unsigned int count)
2 : m_threshold(count), m_count(count), m_generation(0)
3 {
4 if (count == 0)
5 throw std::invalid_argument("count cannot be zero.");
6 }
7
8 bool barrier::wait()
9 {
10 boost::mutex::scoped_lock lock(m_mutex); // m_mutex is the base of barrier and is initilized by it's default constructor.
11 unsigned int gen = m_generation; // m_generation will be 0 for call 1~n-1, and 1 for n~2n - 1, and so on
12
13 if (--m_count == 0)
14 {
15 m_generation++; // cause m_generation to be changed in call n/2n/
16 m_count = m_threshold; // reset count
17 m_cond.notify_all(); // wake up all thread waiting here
18 return true;
19 }
20
21 while (gen == m_generation) // if m_generation is not changed, lock current thread.
22 m_cond.wait(lock);
23 return false;
24 }
因此,说白了也不过是mutex的一个简单应用。
以下是一个使用barrier的例子:
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/barrier.hpp>
3
4 int i = 0;
5 boost::barrier barr(3); // call barr.wait 3 * n times will release all threads in waiting
6
7 void thread()
8 {
9 ++i;
10 barr.wait();
11 }
12
13 int main()
14 {
15 boost::thread thrd1(&thread);
16 boost::thread thrd2(&thread);
17 boost::thread thrd3(&thread);
18
19 thrd1.join();
20 thrd2.join();
21 thrd3.join();
22
23 return 0;
24 }
如果去掉其中thrd3相关的代码,将使得线程1、2一直处于wait状态,进而使得主线程无法退出。
xtime
xtime是boost::thread中用来表示时间的一个辅助类,它是一个仅包含两个成员变量的结构体:
1 struct xtime
2 {
3 //
4 xtime_sec_t sec;
5 xtime_nsec_t nsec;
6 };
condition::timed_wait、thread::sleep等涉及超时的函数需要用到xtime。
需要注意的是,xtime表示的不是一个时间间隔,而是一个时间点,因此使用起来很不方便。为了方便使用xtime,boost提供了一些辅助的xtime操作函数,如xtime_get、xtime_cmp等。
以下是一个使用xtime来执行sleep的例子(跟简单的一句Sleep比起来,实在是太复杂了),其中用到了xtime初始化函数xtime_get:
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/xtime.hpp>
3 #include <iostream>
4
5 int main()
6 {
7 boost::xtime xt;
8 boost::xtime_get(&xt, boost::TIME_UTC); // initialize xt with current time
9 xt.sec += 1; // change xt to next second
10 boost::thread::sleep(xt); // do sleep
11
12 std::cout << "1 second sleep over." << std::endl;
13
14 return 0;
15 }
多线程编程中还有一个重要的概念:Thread Local Store(TLS,线程局部存储),在boost中,TLS也被称作TSS,Thread Specific Storage。
boost::thread库为我们提供了一个接口简单的TLS的面向对象的封装,以下是tss类的接口定义:
class tss
{
public:
tss(boost::function1<void, void*>* pcleanup);
void* get() const;
void set(void* value);
void cleanup(void* p);
};
分别用于获取、设置、清除线程局部存储变量,这些函数在内部封装了TlsAlloc、TlsGetValue、TlsSetValue等API操作,将它们封装成了OO的形式。
但boost将该类信息封装在detail名字空间内,即不推荐我们使用,当需要使用tss时,我们应该使用另一个使用更加方便的类:thread_specific_ptr,这是一个智能指针类,该类的接口如下:
1 class thread_specific_ptr : private boost::noncopyable // Exposition only
2 {
3 public:
4 // construct/copy/destruct
5 thread_specific_ptr();
6 thread_specific_ptr(void (*cleanup)(void*));
7 ~thread_specific_ptr();
8
9 // modifier functions
10 T* release();
11 void reset(T* = 0);
12
13 // observer functions
14 T* get() const;
15 T* operator->() const;
16 T& operator*()() const;
17 };
即可支持get、reset、release等操作。
thread_specific_ptr类的实现十分简单,仅仅为了将tss类“改装”成智能指针的样子,该类在其构造函数中会自动创建一个tss对象,而在其析构函数中会调用默认参数的reset函数,从而引起内部被封装的tss对象被析构,达到“自动”管理内存分配释放的目的。
以下是一个运用thread_specific_ptr实现TSS的例子:
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/mutex.hpp>
3 #include <boost/thread/tss.hpp>
4 #include <iostream>
5
6 boost::mutex io_mutex;
7 boost::thread_specific_ptr<int> ptr; // use this method to tell that this member will not shared by all threads
8
9 struct count
10 {
11 count(int id) : id(id) { }
12
13 void operator()()
14 {
15 if (ptr.get() == 0) // if ptr is not initialized, initialize it
16 ptr.reset(new int(0)); // Attention, we pass a pointer to reset (actually set ptr)
17
18 for (int i = 0; i < 10; ++i)
19 {
20 (*ptr)++;
21 boost::mutex::scoped_lock lock(io_mutex);
22 std::cout << id << ": " << *ptr << std::endl;
23 }
24 }
25
26 int id;
27 };
28
29 int main(int argc, char* argv[])
30 {
31 boost::thread thrd1(count(1));
32 boost::thread thrd2(count(2));
33 thrd1.join();
34 thrd2.join();
35
36 return 0;
37 }
此外,thread库还提供了一个很有趣的函数,call_once,在tss::init的实现中就用到了该函数。
该函数的声明如下:
void call_once(void (*func)(), once_flag& flag);
该函数的Windows实现通过创建一个Mutex使所有的线程在尝试执行该函数时处于等待状态,直到有一个线程执行完了func函数,该函数的第二个参数表示函数func是否已被执行,该参数往往被初始化成BOOST_ONCE_INIT(即0),如果你将该参数初始化成1,则函数func将不被调用,此时call_once相当于什么也没干,这在有时候可能是需要的,比如,根据程序处理的结果决定是否需要call_once某函数func。
call_once在执行完函数func后,会将flag修改为1,这样会导致以后执行call_once的线程(包括等待在Mutex处的线程和刚刚进入call_once的线程)都会跳过执行func的代码。
需要注意的是,该函数不是一个模板函数,而是一个普通函数,它的第一个参数1是一个函数指针,其类型为void (*)(),而不是跟boost库的很多其它地方一样用的是function模板,不过这样也没有关系,有了boost::bind这个超级武器,想怎么绑定参数就随你的便了,根据boost的文档,要求传入的函数不能抛出异常,但从实现代码中好像不是这样。
以下是一个典型的运用call_once实现一次初始化的例子:
1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/once.hpp>
3 #include <iostream>
4
5 int i = 0;
6 int j = 0;
7 boost::once_flag flag = BOOST_ONCE_INIT;
8
9 void init()
10 {
11 ++i;
12 }
13
14 void thread()
15 {
16 boost::call_once(&init, flag);
17 ++j;
18 }
19
20 int main(int argc, char* argv[])
21 {
22 boost::thread thrd1(&thread);
23 boost::thread thrd2(&thread);
24 thrd1.join();
25 thrd2.join();
26
27 std::cout << i << std::endl;
28 std::cout << j << std::endl;
29
30 return 0;
31 }
结果显示,全局变量i仅被执行了一次++操作,而变量j则在两个线程中均执行了++操作。

View File

@@ -0,0 +1,116 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-19T21:13:13+08:00
====== Boost 中的智能指针 ======
Created Sunday 19 February 2012
http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html
这篇文章主要介绍 boost中的智能指针的使用。
内存管理是一个比较繁琐的问题,C++中有两个实现方案: __垃圾回收机制和智能指针__。垃圾回收机制因为性能等原因不被C++的大佬们推崇, 而智能指针被认为是解决C++内存问题的最优方案。
===== 1. 定义 =====
一个智能指针就是__一个C++的对象, 这对象的行为像一个指针__,但是它却可以在其不需要的时候自动删除。注意这个“其不需要的时候”, 这可不是一个精确的定义。这个不需要的时候可以指好多方面:局部变量退出函数作用域、类的对象被析构……。所以boost定义了多个不同的智能指针来管理不同的场景。
shared_ptr<T> 内部维护一个引用计数器来判断此指针是不是需要被释放。是boost中最常用的智能指针了。
scoped_ptr<t> 当这个指针的__作用域消失__之后自动释放
intrusive_ptr<T> 也维护一个引用计数器,比shared_ptr有更好的性能。但是__要求T自己提供这个计数器__。
weak_ptr<T> 弱指针,要**和shared_ptr 结合**使用
shared_array<T> 和shared_ptr相似,但是访问的是数组
scoped_array<T> 和scoped_ptr相似,但是访问的是数组
===== 2. Boost::scoped_ptr<T> =====
scoped_ptr 是boost中最简单的智能指针。scoped_ptr的目的也是很简单, 当一个__指针离开其作用域时候,释放相关资源__。特别注意的一定就是scoped_ptr __不能共享指针的所有权也不能转移所有权__。也就是说这个内存地址就只能给的声明的变量用,不能给其他使用。
下面是scoped_ptr的几个特点:
* scoped_ptr的效率和空间的消耗内置的指针差不多。
* scoped_ptr__不能用在标准库的容器上__。(用shared_ptr代替, **因为放入标准容器的对象需要能被复制和赋值**)
* scoped_ptr 不能指向一块能够动态增长的内存区域(用scoped_array代替)
1 class test
2 {
3 public:
4 void print()
5 {
6 cout << "test print now" <<endl;
7 }
8 };
9 int _tmain(int argc, _TCHAR* argv[])
10 {
11 boost::scoped_ptr<__test__> x(new test);
12 x->print();
13 return 0;
14 }
===== 3.Boost::shared_ptr<T> =====
shared_ptr 具有如下几个特点:
* 在内部维护一个引用计数器, 当有一个指针指向这块内存区域是引用计数+1, 反之-1, 如果没有任何指针指向这块区域, **引用计数器为0,释放内存区域**。
* 可以__共享和转移所有权__。
* 可以被标准库的容器所使用
* 不能指向一块动态增长的内存(用share_array代替)
我们可以看下如下例子:
1 int _tmain(int argc, _TCHAR* argv[])
2 {
3 boost::shared_ptr<test> ptr_1(new test);
4 ptr_1->print();//引用计数为1
5 boost::shared_ptr<test> ptr_2 = ptr_1; //共享所有权
6 ptr_2->print();//引用计数为2
7 ptr_1->print();// 引用计数还是为2
8 return 0;
9 }
===== 4. Boost::intrusive_ptr<T> =====
intrusive_ptr 的主要和share_ptr一样, 对比share_ptr,其效率更高,但是需要自己维护一个引用计数器, 这里不做详细介绍。
===== 5. Boost::weak_ptr<T> =====
* weak_ptr 就是一个弱指针。__weak_ptr 被shared_ptr控制__, 它可以通过share_ptr的构造函数或者lock成员函数转化为share_ptr。
* weak_ptr的一个最大特点就是它共享一个share_ptr的内存,但是无论是构造还是析构一个weak_ptr 都__不会影响引用计数器__。
1 int _tmain(int argc, _TCHAR* argv[])
2 {
3 boost::shared_ptr<test> sharePtr(new test);;
4 boost::weak_ptr<test> weakPtr(sharePtr); //**不增加**sharPtr的计数
5 //weakPtr 就是用來保存指向這塊內存區域的指針的
6 //干了一大堆其他事情
7 boost::shared_ptr<test> sharePtr_2 = __weakPtr.lock()__;
8 if (sharePtr_2)
9 sharePtr_2->print();
10 return 0;
11 }
===== 6. Boost::shared_array<T> 和Boost::scoped_array<T> =====
前面提到过shared_ptr和scoped_ptr不能用于数组的内存(new [ ]),所以shared_array和scoped_array就是他们的代替品。我们可以看下shared_array的用法
1 int _tmain(int argc, _TCHAR* argv[])
2 {
3 const int size = 10;
4 boost::shared_array<test> a(new test[size]);
5 for (int i = 0; i < size; ++i)
6 a[i].print();
7 return 0;
8 }
===== 7. 使用智能指针的几个注意点 =====
下面是几个使用智能指针需要注意的地方:
* 声明一个智能指针的时候要__立即给它实例化, 而且一定不能手动释放它__。
* …_ptr<T> 不是T* 类型。所以:
a: 声明的时候要…_ptr<T> 而不是….._ptr<T*>
b:不能把T* 型的指针赋值给它
c: 不能写ptr=NULl, 而用__ptr.reset()__代替。
* 不能循环引用。
* 不要声明临时的share_ptr, 然后把这个指针传递给一个函数
8. 总结
智能指针使用上还是比较简单的, 而且能比较有效得解决C++内存泄露的问题,各位使用C++的童鞋赶快用起来吧。

View File

@@ -0,0 +1,6 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T19:30:46+08:00
====== Boost学习系列 ======
Created Tuesday 14 February 2012

View File

@@ -0,0 +1,313 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T14:27:48+08:00
====== Smart Pointers in Boost ======
Created Tuesday 14 February 2012
http://drdobbs.com/cpp/184401507
Welcome to Boost, the peerless community of C++ innovators. If you can't find what you need in the Standard C++ library, chances are Boost has it production-ready.
===== Introducing Boost =====
According to the Boost website, Boost is "a repository of free, portable, peer-reviewed C++ libraries. Boost acts as a proving ground for new libraries, particularly those that work well with the** ISO C++ Standard library**." But Boost is more than a collection of libraries. It is also the focus of a rapidly growing community of developers who create, use, and discuss the Boost libraries. Beyond making these excellent libraries available, Boost also offers a place to learn. The libraries are examples of __solid library design__ in a world that seldom seems to look further than the next release. Joining the discussions on the Boost mailing list (either actively or just by listening) is a good way to improve your understanding of library design problems and solutions. Boost also provides a rapidly growing Boost-Users mailing list that focuses on questions related to using the libraries.
The quality and the technological standard of the Boost libraries are __amazing__. And the Boost portability standard ensures that when you move your code to another platform, you'll know the libraries will __still work__. The current release, Boost 1.25.0, consists of libraries ranging from **smart pointers **to **regular expressions** to **a portable threading library**. Boost currently supports 35 libraries, all of which have been thoroughly reviewed by community members. These libraries can be freely used, and many of them are being used in commercial applications.
Boost is one of the strongest C++ communities. Among the 2,000 members are many of the __world's top C++ programmers__. Members stay involved because they like working with some of the best minds in programming. They also know their efforts are bound to have an impact on the C++ community, since much of what you see in Boost is a strong candidate for inclusion in the next C++ Standard.
The best way to get acquainted with Boost is **to tour the Boost libraries**. In this article, I'll introduce you to the **Boost smart pointer** library smart_ptr. smart_ptr is a good example of the innovation and sound design you'll find in Boost. I encourage you to visit the Boost website (www.boost.org) for more on the other 34 libraries in the Boost collection.
===== Smart Pointers =====
One of the smaller (size-wise) Boost libraries is smart_ptr. smart_ptr is one of the libraries that I think is going to end up in the C++ Standard. This article discusses the Boost smart_ptr library. But first, I'll begin with a brief introduction to smart pointers.
===== 30-Second Introduction to Smart Pointers =====
Smart pointers are __classes __that store pointers to dynamically allocated __(heap) objects__. They behave much like built-in C++ pointers except that they __automatically delete the object__ pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects. They can also be used to__ keep track__ of dynamically allocated objects shared by multiple owners.
Actually, smart pointers can do more, such as __handle thread safety__, provide copy-on-write, ensure protocols, and provide remote communication services. There are ways to create generic smart pointers for these ESPs (Extremely Smart Pointers), but those won't be covered here. (See [1] for in-depth coverage of this topic. By the way, Alexandrescu is currently considering submitting his C++ library Loki to Boost.)
Most uses of smart pointers are __for lifetime control__, period. They implement **operator-> and operator*** to yield** the raw pointer**, allowing the smart pointer to look like an ordinary pointer.
One such class comes with the standard library:** std::auto_ptr**. It is designed to handle ownership of a resource, but lacks support for **reference counting** and **arrays**. Also, std::auto_ptr transfers ownership when it is copied. In many cases, you need more and/or different functionality. Enter the smart_ptr classes.
===== smart_ptr Classes =====
The smart pointers in Boost are:
* **scoped_ptr**, which handles** sole ownership** of single objects; unlike std::auto_ptr, scoped_ptr __cannot be copied__
* scoped_array, which is similar to scoped_ptr, but handles arrays
* shared_ptr, which allows object ownership to __be shared__
* shared_array, which allows sharing of ownership for arrays
===== • scoped_ptr =====
The scoped_ptr smart pointer differs from std::auto_ptr in that it__ doesn't transfer ownership__. In fact, it explicitly **forbids** any attempt to do so! This is important for any scenario where you need to be sure that there is only ever one owner to a pointer. Without scoped_ptr, you would probably be inclined to use std::auto_ptr, but take a look at the following code:
auto_ptr<string> MyOwnString
(new string("This is mine to keep!"));
auto_ptr<string> NoItsMine(MyOwnString);
cout << *MyOwnString << endl; // Boom
This code will __obviously fail__, since ownership of the string has been transferred to NoItsMine. This is not a design flaw in std::auto_ptr — it is a feature. However, when you need MyOwnString to be just that, you'll want to use scoped_ptr:
scoped_ptr<string> MyOwnString
(new string("This is mine to keep for real!"));
// __Compiler error__ - there is no copy constructor.
scoped_ptr<string> TryingToTakeItAnyway
(MyOwnString);
The scoped_ptr accomplishes this behavior by **inheriting **from__ boost::noncopyable__ (found in the library **Boost.utility**). The non-copyable class declares the copy constructor and the assignment operator as__ private__.
===== • scoped_array =====
scoped_array is obviously the equivalent of scoped_ptr, but __for arrays.__ You'll get no help from the standard library here — unless of course you're using std::vector, which is the right way to go in most cases.
typedef __tuples::tuple<string, int>__ ArrayTuple;
scoped_array<ArrayTuple> MyArray(new ArrayTuple[10]);
tuples::get<0>(MyArray[5]) =
"The library Tuples is also part of Boost";
Tuples are collections of elements — for example **pairs, triples, and quadruples**. A typical use of tuples is__ returning multiple values from a function__. The __Boost Tuple Library __can be thought of as an extended generalization of the **standard library's pair**, and it currently works with up to 10 tuple elements. It supports tuple streaming, comparison, assignment, unpacking, and more. For more information about the Boost Tuple Library, see [3].
As the scoped_array goes out of scope, delete[] will be properly called. This eliminates a common mistake, namely invoking the wrong operator delete.
===== • shared_ptr =====
Here's an offering that you won't find in the standard library — __a reference-counted smart pointer.__ Most of you have rolled your own smart pointers, and there is plenty of literature regarding the issues involved in reference counting. One of the most important details is how the reference count is implemented — __intrusive__, which means that you add support to the classes being reference counted, or __non-intrusive__, which means you don't. __The Boost shared_ptr is non-intrusive__, and the implementation uses a reference counter allocated from the **heap**. There has been much discussion about providing parameterized strategies to best suit any given situation, but in the end it was decided against to focus on usability. Don't expect the discussions to go away though.
The usage of shared_ptr does what you'd expect: it takes responsibility of deleting the pointee when **there are no more users of the instance**, and it shares its pointee freely.
void PrintIfString(const __any__& Any) {
if (const shared_ptr<string>__* __s =
__any_cast__<shared_ptr<string> >(__&Any__)) {
cout << **s << endl;
}
}
int main(int argc, char* argv[])
{
std::vector<__boost::any__> Stuff;
shared_ptr<string> SharedString1
(new string("Share me. By the way,
Boost.any is another useful Boost
library"));
shared_ptr<string> SharedString2
(SharedString1);
shared_ptr<int> SharedInt1
(new int(42));
shared_ptr<int> SharedInt2
(SharedInt1);
Stuff.push_back(SharedString1);
Stuff.push_back(SharedString2);
Stuff.push_back(SharedInt1);
Stuff.push_back(SharedInt2);
// Print the strings
__for_each__(Stuff.begin(), Stuff.end(),
PrintIfString);
**Stuff.clear();**
// The pointees of the shared_ptr's
// will be released on leaving scope
return 0;
}
The __any library __offers a way to store just about anything [2] [4]. The requirements on contained types are that they are __CopyConstructible__, the destructor must not throw, and they should typically be __Assignable__. How do we store and pass "anything"? Indiscriminate types (read void*) can refer to just about anything, but that means throwing type safety (and knowledge) out the door. Using any, **type safety is preserved**. All types satisfying the requirements for any can be assigned, but** extraction demands knowing the exact type**. any_cast is the key for unlocking the value held by any. __any_cast works like dynamic_cast__ — casts to pointer types succeed or fail __by returning a null pointer__ whereas casts to reference types throw an exception (bad_any_cast) on failure.
* ===== shared_array =====
Again, shared_array is the equivalent of shared_ptr, but it is used with arrays.
shared_array<Base> MyStrings( new Base[20] );
===== Diving into the shared_ptr Implementation =====
Creating a simple smart pointer is fairly easy. Creating a smart pointer that will work with most compilers is more difficult. Creating a smart pointer with exception safety in mind is a lot more difficult.** Boost::shared_ptr** does it all, and here's how. (Note: includes, broken compiler fixes, and parts of the implementation are omitted, but you'll find them in Boost.smart_ptr).
First, the class definition: obviously, smart pointers are (almost always) templates.
template<typename T> class shared_ptr {
The public interface:
explicit shared_ptr(T* p =0) : px(p) {
// fix: prevent leak if new throws
try { pn = new long(1); }
catch (...) { checked_delete(p); throw; }
}
Now, in the constructor there are two things that are easily forgotten. The constructor is explicit, just like most constructors that can take a single argument. The other important thing to note is that the heap allocation of the reference counter is protected by a try-catch block. Without it, you would have a flawed smart pointer that wouldn't do its only job if the reference counter can't be allocated.
~shared_ptr() { dispose(); }
The destructor has another important task: if the reference count goes down to zero, it should safely delete the pointee. The destructor delegates this task to another method: dispose.
void dispose() { if (—*pn == 0)
{ checked_delete(px); delete pn; } }
As you can see, the reference count (pn) is decremented. If it comes down to zero, checked_delete is called on the pointee (px), and then the reference count (pn) is also deleted.
So, what does checked_delete do? This handy function (which you'll find in Boost.utility) makes sure that the pointer represents a complete type. Do you have that in your own smart pointer classes?
Now, here's the first assignment operator:
template<typename Y> shared_ptr& operator=
(const shared_ptr<Y>& r) {
share(r.px,r.pn);
return *this;
}
This is a member template, and if it had not been, there are two scenarios:
If there is no parameterized copy constructor, assignment of the type Base = Derived won't work.
If there is a parameterized copy constructor, assignments will work, but it involves creating an unnecessary temporary smart_ptr.
Again, this shows you a very good reason why you shouldn't roll your own smart pointers — these are not obvious issues.
The actual work of this assignment operator is done by share:
void share(T* rpx, long* rpn) {
if (pn != rpn) { // Q: why not px != rpx?
// A: fails when both == 0
++*rpn; // done before dispose() in case
// rpn transitively dependent on
// *this (bug reported by Ken Johnson)
dispose();
px = rpx;
pn = rpn;
}
}
Note that the comparison for self assignment (or rather self sharing) is done by comparing the reference counters, not the pointers. Why? They both can be zero, but not necessarily the same.
template<typename Y> shared_ptr
(const shared_ptr<Y>& r) : px(r.px) { // never throws
++*(pn = r.pn);
}
This version is a templated copy constructor. See above for a good reason why.
The assignment operator and copy constructor also have non-templated versions:
shared_ptr(const shared_ptr& r) :
// never throws
px(r.px) { ++*(pn = r.pn); }
shared_ptr& operator=
(const shared_ptr& r) {
share(r.px,r.pn);
return *this;
}
The reset function is true to its name by resetting the pointee. Very handy if you need to destroy a pointee before leaving scope, or simply to invalidate some cached values.
__void reset__(T* p=0) {
// fix: self-assignment safe
if ( px == p ) return;
if (—*pn == 0)
{ checked_delete(px); }
else { // allocate new reference
// counter
// fix: prevent leak if new throws
try { pn = new long; }
catch (...) {
// undo effect of —*pn above to
// meet effects guarantee
++*pn;
checked_delete(p);
throw;
} // catch
} // allocate new reference counter
*pn = 1;
px = p;
} // reset
Again, note the care that has been taken to avoid potential leaks and to maintain exception safety.
Then you have the operators that enable the "smart" of the smart pointer:
// never throws
__T& operator*() __const { return *px; }
// never throws
__T* operator->()__ const { return px; }
// never throws
__T* get() __const { return px; }
Just a note: there are smart pointers that implement a cast operator to T*. This is not a good idea, and more often than not, you'll burn your fingers. The inconvenience of having to write get disables the compiler from playing games with your sanity.
I think it was Andrei Alexandrescu who said "If your smart pointer behaves exactly like dumb pointers, they are dumb pointers." True enough.
In closing, here are a couple of convenient functions.
long use_count() const
{ return *pn; } // never throws
bool unique() const
{ return *pn == 1; } // never throws
The names are self-explanatory, right?
There is a lot more to mention about Boost.smart_ptr (such as specializations for std::swap and std::less, members for compatibility and convenience together with std::auto_ptr, etc.), but I'm running out of space. See smart_ptr.hpp in the Boost distribution (<www.boost.org>) for the gory details. Even without those extras, isn't this indeed a very smart pointer?
===== Conclusion =====
This has been a short introduction to the world of Boost. Anyone is welcome to join, and frankly, I think that most C++ programmers have very good reasons to do so. I'd like to thank Beman Dawes, David Abrahams, and Jens Maurer for their help in answering questions and sharing their opinions (see sidebar, "Answers from the Original Boosters") on the next page.
See you at Boost!
Notes and References
[1] Andrei Alexandrescu. Modern C++ Design (Addison-Wesley, 2001).
[2] Boost, www.boost.org.
[3] Jaako Jarvi. Tuple Types and Multiple Return Values, C/C++ Users Journal, August 2001.
[4] Jim Hyslop and Herb Sutter.I'd Hold Anything for You,C/C++ Users Journal C++ Experts Forum, December 2001.
[5] Boost mailing list, http://groups.yahoo.com/group/Boost.
[6] Boost-Users mailing list, http://groups.yahoo.com/group/Boost-Users
[7] C++ Standard, International Standard ISO/IEC 14882.
The three moderators of the Boost mailing list are Beman Dawes, Dave Abrahams, and Jens Maurer. (If you're not familiar with these names, see their biographies on www.boost.org). I interviewed Beman, Dave, and Jens on their views of Boost. Following is a summarized version of what they had to say:
Why do you think Boost is important?
Boost is one of the only communities working on the process and practice of library design. There are lots of libraries available elsewhere whose design pre-dates the ISO C++ Standard. These libraries often fail to exploit the potential of compile-time evaluation (i.e., templates) and thus miss type-checking and optimization opportunities. The license requirements allow the use of Boost libraries in commercial and non-commercial projects free of charge, thereby helping them to produce better programs.
In today's professional software development world, it can be hard to make the case for long-term investment in reusable components. Developers are (often rightly) expected to do the simplest thing that could possibly work, under the assumption that the generalization won't be needed. As the problems they need to solve become more complex, however, they need library components that can help them keep their solutions simple. The C++ Standard library goes some distance towards filling that role, but programmers will continue to need more than it provides. What should be the design, documentation, and coding practices for the libraries that programmers need? Boost's emergent collaborative process provides one answer.
What will happen to the libraries when (if) they become a part of Standard C++? Will they still be available in Boost? Will compiler vendors be able to use the implementation of Boost libraries in their packages?
It is likely that the libraries that become a part of Standard C++ will still be available in Boost for quite some time after the release of the respective standard. Experience with C99 and C++98 shows that compiler vendors require some time to catch up with the latest standard. Compiler vendors are certainly allowed to use the Boost-provided implementation of the specified library interfaces, by virtue of Boost's license requirements. However, some Boost libraries could benefit from compiler support or manual adaptation to the target platform.
Why should C++ users (rather than those that submit libraries) join Boost?
A new mailing list called boost-users@yahoogroups.com has been set up, specifically targeted at Boost users. However, Boost users are always welcome to join the main mailing list as well. Since Boost provides C++ libraries, Boost users are C++ programmers. I believe all C++ programmers can learn a lot from following discussions on the Boost mailing list. It's also a chance to see how world-class developers approach and solve problems.
If companies were to specialize in supporting the Boost libraries, would that be a problem?
Not at all, that would be a welcome step. It would be even better if such companies would contribute any bug fixes or changes they make back to the community.
David adds: I'd like to start such a company myself.
Does Boost aim to be widely used by developers before libraries have been included in the Standard, or should it be regarded as research work?
Boost is absolutely not to be regarded as research work, though many of the techniques are cutting-edge. During the formal review process, membership relentlessly pursues issues of practical importance to real-world software. After all, experience can only be gained by using it, not by waiting until the paperwork is done.

View File

@@ -0,0 +1,54 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T19:28:43+08:00
====== boost format 格式控制输出(学习笔记) ======
Created Tuesday 14 February 2012
http://blog.csdn.net/wzq9706/article/details/6168430
#include <boost/format.hpp>
#include <iostream>
using namespace boost;
using namespace std;
// 格式输出控制,(类似c++准准库中的sprintf)
//
int main()
{
// format库的几种用法
//------------------------------------------
// 第一种:
// 最接近printf()
cout<< "First: " << format("%s: %d + % d = %d/n")%"sum"%1%2%(1+2); // format 重载了%操作符类似<<操作符
// result: sum: 1 + 2 =3
// 第二种:
// 在format对象中用%X%指定X是第几个参数
format fmt1("(%1% + %2%)*%2% = %3%/n"); // %1% 接收第一个参数, %2% 接收第二个参数, %3%接收第三个参数
fmt1 %2 %5; // 第一,二个参数
fmt1 %((2+5)*5); // 第三个参数,参数不一定像第一种方法直接植入,可以分次完成但参数的总数量不能少
cout<< "Second: " << fmt1.str(); // str()是获取格式化后的字符串,不负责清空
// result: (2 + 5)*5 = 50;
// 第三种:
// 带格式化选项(和printf一样)
format fmt3("%05d %-8.3f %10s %05X/n");
cout<< "Third: " <<fmt3 %62 %3.14159f %123456789 %48;
// 第四种:
// 在对象构造函数字符串添加管道符,用于美观
format fmt4("%|05d| %|-8.3f| %|10s| %|05X|/n");
cout<< "Fourth: " << fmt4 %62 %3.14159f %123456789 %48;
// 高级应用:
format fmtOther("%1% %2% %3% %2% %1%");
cout<< "Other normal: " << fmtOther %1 %2 %3 <<endl; // 插入值 %1%对应第一个参数,%2%第二个, %3%第三个
fmtOther.bind_arg(2, 10); // 将第二个参数固定为10,即使使用clear也一样
cout<< "Other bind_arg: " << fmtOther %1 %3 <<endl; // 插入值时忽略%2%,直接跳到下一个%X%
// group, modify_item 略
//-----------------------------
// 与printf的比较:
//-----------------------------
// printf是函数,format是类
// printf不进行类形检查,format进行类型安全检查
// printf速度是format的2到5倍
// format个人还不太习惯
getchar();
return 0;
}

View File

@@ -0,0 +1,131 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T14:44:56+08:00
====== boost 学习计划 ======
Created Tuesday 14 February 2012
http://remonstrate.wordpress.com/2010/10/27/boost-%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92/
嗯,最近看到一本 Beyond C++ standard library: An Introduction to Boost,感觉还不错。正好拿来学习一下,这里会摘录一些笔记,跟原先的 blog 一起。那本书似乎是针对 1.33 版本的,而现在已经 release 到 1.44 版本了。
===== 字符串和文本处理 =====
* boost.regex 为 c++ 提供了正则表达式;
* boost.spirit 是 functional recursive-decent parser framework,支持使用 C++ 描述类似 EBNF 语法,可用于创建命令行 parser、预处理器。boost.wave 就是利用这个 framework 实现的 C++ 预处理器;
* boost.string_algo 提供了 std::string 以外的__字符串处理程序__,包括 trimming、case conversion、splitting、finding、replacing 等功能;
* boost.tokenizer 用于将字符串类型的容器__切分__成为一个一个 token;
* (1.44)boost.iostreams 用于自定义 stream;
* (1.44)boost.wave 是利用 spirit 写的 C++ 的预处理程序,可以利用它实现很多 c++ 0x 的特性;
* (1.44)boost.xpressive 也是一个 regex 的库,但是使用 context free 语法;
===== 数据结构、容器、迭代器(iterator)和算法 =====
* boost.any 的目标是__存储异构数据__。一般来说异构数据需要使用不同的指针,而一种解决方案是使用 **void* 指针**(但这种方式并不是 type safe 的);另外一种是使用**串行化**,将所有异构的数据转换成为某种格式(如 string),要求所有的数据提供 serialization 和 deserialization 的方法。而 any 实现的类型安全的(value-based variant type with an unbounded set of possible types);当我们在容器里面需要存储异构数据的时候,any 是一个非常好的选择;
* boost.array 是对 C++ __数组的封装__,由于 std::vector 等容器是动态的,性能上相对静态的数组是有一定的差距的,如果性能上需要使用数组但希望使用封装的更好的对象的时候,可以使用 array;
* boost.compressed_pair 是与 std::pair 相近的功能;在 std::map 这类关联容器中我们常常使用 std::pair 创建需要的一对 key/value;boost 的版本允许在 pair 中存在空对象时利用 empty base optimization **压缩这个 pair 的内存占有率**;
* boost.dynamic_bitset 是对 std::bitset 的扩展,后者是一个静态的数据结构,而 dynamic_bitset 允许在运行时确定 bitset 的大小;
* boost.graph 是对__图结构、相关算法的封装__,提供了 adjacency matrix、adjacency list、edge list 这些方式表示图,同时提供了最短路径、minimum spanning tree、topological sort 等算法的实现;
* boost.iterator 用于创建符合 stl 标准的 iterator,提供**自动的 typedef**,并且可以将已有的 iterator 的行为改变,创建 indirect iterator adaptor 可以将放在容器里面的智能指针 deference,这样可以像一般的容器(里面盛放的是正常的对象)一样使用;
* boost.multiarray 是对 C++ 中**多维数组**的封装;
* boost.multi-index 允许一个容器存在多个 index,这意味着可以__让同一个容器使用不同的序__,这样抽取其中元素的时候能够更加便捷;特别在 std::set、std::map 不能够提供需要的多重序关系访问时,multi-index 就会产生作用了;
* boost.range 提供的主要是一种概念性的东西:一般的 algorithm 输入都是两个 iterator 表示操作的范围;ranges 提供了更好的表达方式,这样使用 algorithm 会更加方便,另外也使得用户代码更加抽象;
* boost.tuple 是对 std::pair 的另一种扩展,也就是提供了所谓的__ n-tuple(n 元组)结构__;与直接使用 struct、class 创建 n-tuple 不同之处在于可以 direct declaration、作为函数返回值或者参数以及一致的方法访问 tuple 的任意成员;
* boost.variant 是对 __union 结构的封装__,主要为了解决一般 union 结构为了判断其类型而写的类型转换代码;variant 提供的是 type safe visitation;
* (1.44)boost.bimap 提供了**双向关联容器**;
* (1.44)boost.circular_buffer 提供了循环的 buffer;
* (1.44)boost.gil 提供的是图片处理的封装;
intrusive [in'tru:siv] adj.打搅的, 侵入的
* (1.44)boost.intrusive 提供了所谓的 intrusive container;intrusive container 是相对于 non-intrusive container 来说的,STL 的容器都是 non-intrusive,被存储的对象不需要有特别的代码上的修改,任何对象都可以存下来,代价是只能**复制一份**存放在容器里面;intrusive 容器需要对象本身提供互相连接的机制,因此需要修改原来的对象,好处是进入容器的**是原来的对象**,而不是其复制体;
* (1.44)boost.pointer_container 用于在堆上的对象需要的指针;
* (1.44)**boost.property_tree** 用于__存放配置__;
* (1.44)boost.unordered 是用于存放无序的数据;
* (1.44)boost.foreach 是一个 macro,可以方便的写遍历代码,使用 foreach 类型的语法;
===== functor 和 higher-order programming =====
* boost.bind 是非常有用的工具,是对 std::bind1st 和 std::bind2nd 的扩展;bind __允许用统一的接口__对函数、函数指针、函数对象、成员函数指针进行封装,产生 functor 以供后面的 algorithm 使用;定义的 functor 提供了 stl 中需要的 typedef;这使得我们没有必要使用 std::ptr_fun、std::mem_fun 这类产生 functor 的 helper function;
* boost.function 提供的是一种包装函数指针、函数对象以及成员函数的机制,由此能够产生 stateful callback function(估计就是将产生函数对象时的相关信息存放在函数对象中)等用例;
* boost.functional 主要是对 STL 中存在的一个问题进行了处理:当函数存在引用类型的参数的时候会产生“引用的引用”这种非法的结果导致的错误;使用 functional 将使得使用 std::ptr_fun 失去使用的意义;
* boost.lambda 是 Boost 的__ lambda 算子__,用于方便的创建 functor,并且与 STL 中的 algorithm 一起使用将非常方便;避免写非常多的 functors;
* boost.ref 是用来创建引用,这个引用和 C++ 自身的引用不同的是它**可以被复制**;与指针的用例不同之处在于,引用允许你一直用 . 而不是 -> 来访问成员、方法;
* boost.signals(2) 用于创建 __signal/slot 的通讯机制__,这在 design pattern 中对应着 observer 或者 publisher/subscriber 这种,GUI 开发中非常常见;
* (1.44)boost.mem_fun 产生成员函数的 functor;
* (1.44)boost.functional/factory 用于动态或者静态的生成对象;
* (1.44)boost.functional/forward 允许使用任意个参数的 generic programming;
* (1.44)boost.functional/hash 可扩展到用户定义的数据上的 hash;
* (1.44)result_of 用于确定函数的返回类型;
===== generic programming 与 template metaprogramming =====
关于这些诡异的概念见上一篇 blog。
* boost.call_traits 提供了自动推理函数传递参数时最合适的方法,如果函数不修改参数本身,可以传值也可以传引用,在写 generic function 的时候需要这种机制选择合适的类型,一个例子比如我希望写一个串行化的东西,要求传入对象都支持 operator<< 插入到 std::ostream,可是 int 和 std::string 很显然前者希望传值,后者希望传引用,call_traits 的作用就在于可以让编译器自动选择最合适的方式生成对应的 template;
* boost.concept_check 用于检查 concept,我们知道 generic programming 里面最重要的就是搞清楚什么样的东西能够被传递给 template 而不会出错(满足某种 concept),那么这个 concept_check 的用途就是在编译时能够检测出来;另外也提供了 30 多个 concept 用于检查;
* boost.enable_if 用于根据某些条件特例化或者排除某种特例化;
* boost.in_place_factory 实现的是 in-place 创建对象,这对于将一些不能进行 copy 的对象放在容器里面是非常有用的;
* boost.mpl 本身就可以写一本书了,这是用来做 template metaprogramming 的非常有用的库(而在此之前几乎没有);它实现了很多 C++ STL 的 algorithm,但是都是在编译时执行的;
* boost.property_map 提供了用于创建 mapping 机制的 concept,与 concept_check 一起可以用于有效的检查 generic programming 的正确性;
* boost.static_assert 嗯这个就是进行编译时检查所用的技术,和 assert 宏类似,后者是运行时检查;
* boost.type_traits 提供编译时判断 template 的类型,如是否指针/引用等等;
* (1.44)boost.function_type 可调用(callable)对象的 traits;
* (1.44)boost.typed_in_place_factory?
* (1.44)boost.fusion 用于与 tuple 一起使用的一些工具;
===== 数学与数值计算 =====
* boost.integer 提供了编译时关于整数的信息,对应于 C99 的 stdint.h;
* boost.interval 提供了对区间的封装;
* boost.math 提供了对复数的推广 quaternion 和 octonion 的封装以及 LCD 和 GCM 的计算;
* boost.minmax 是对 std::min 和 std::max 的补充;
* boost.numeric 给出了 numeric_cast 保证类型转换的时候不会出现溢出;
* boost.operator 提供了常用的比较 operator;
* boost.random 用于产生__随机数__;
* boost.rational 封装了有理数;
* boost.ublas 是对 BLAS 的 C++ 实现,不同之处是 BLAS 原来是 fortran 写的 columnwise 的,而 uBLAS 是 rowwise 的;
* (1.44)boost.accumulator 用于处理__增量式问题__,比如求和,求最大值等;
* (1.44)boost.math/special_function 包括了许多特殊函数;
===== 输入输出 =====
* boost.assign 重载了逗号和括号 operator,可以更方便的给容器赋值;
* boost.filesystem 提供了__对基本文件操作的封装__;
* boost.format 提供了__与 printf 类似的功能__,但是是 type safe 的;
* boost.io_state_savers 允许对 std::istream 和 std::ostream 进行保存,并在后面 undo ;
* boost.serialization 用于__对数据进行串行化__,并给出了对 STL 某些容器串行化的封装;
===== 并行处理 =====
* **boost.thread** 是一种 portable 的线程实现;
* (1.44)boost.asio 用于低级__网络通讯__;
* (1.44)boost.interprocess 用于__进程之间的通讯__;
* (1.44)boost.MPI 是一个 MPI 实现;
===== 其他 =====
* boost.conversion 提供了另外几种 cast 如 polymorphic_cast、__polymorphic_downcast__ 等;
* boost.crc 用于计算 CRC;
* boost.date_time 用于__时间方面的操作__;
* boost.optional 用于返回值可能存在需要表达“错误”或者“空”但是原来的类型可能不存在这种值的情况;
* boost.pool 提供了__内存池__;
* boost.preprocessor 提供了预处理程序,支持 recursion 和容器等;
* **boost.program_option **用于__ parse 输入的命令行参数__;
* boost.python 和 SWIG 做的类似,提供 C++ 的 python binding,当然 boost 这个仅仅面向 python 一个语言;
* **boost.smart_ptr** 提供了几种__智能指针__;
* boost.test 提供了__ unit test __的 framework;
* **boost.timer **用于__计算时间__,尽量提供最高精度的时间;
* boost.tribool 提供三值(bool 是两值);
* boost.utility 包括很多小工具,如 check_deleted、noncopyable 等;
* boost.value_initialized 用于处理各种不同方式的初始化,避免之间可能存在的不一致;
* (1.44)boost.uuid 用于**计算 UUID**;
* (1.44)boost.base_from_member 提供了从成员初始化基类的方法;
* (1.44)boost.exception 允许在 exception 中携带别的对象;
* (1.44)boost.flyweight 用于处理含有大量冗余的大规模数据的 design pattern;
* (1.44)boost.scope_exit 提供在__退出环境时执行的函数__;
* (1.44)boost.statechart 用于实现有限状态机;
* (1.44)**boost.swap** **交换对象**;
* (1.44)**boost.system **提供对__操作系统方面__的支持;
* (1.44)boost.TR1 是 C++ 标准的 technical report;
* (1.44)boost.typeof 是 typeof 的一种模拟;
* (1.44)boost.units 是用来做 dimensional analysis 和 unit/quantity manipulation 的;
许多 boost 的库已经存在 C++ technical report,这意味着它们已经进入 C++ 标准委员会审查进入 STL 的阶段,有望在 C++0x 版本中成为新的标准。

View File

@@ -0,0 +1,75 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T15:45:10+08:00
====== boost 的 system 和 filesystem ======
Created Tuesday 14 February 2012
http://remonstrate.wordpress.com/2011/06/29/boost-%E7%9A%84-system-%E5%92%8C-filesystem/
boost 的 system 为一些__常见的系统调用__提供了非全局变量(errno)的解决方案,主要是提供了一个 std::runtime_error 的子类 boost::system::system_error,这是一个跨平台的头文件 boost/system/system_error.hpp 里面包含的。
boost 的 filesystem 就是建立在 system 上面的一个工具,方便在 C++ 程序里面访问文件系统(跨平台)。下面是一个简单的例子:
01 #include <iostream>
02 #include <boost/filesystem.hpp>
03 using namespace boost::filesystem;
04
05 int
06 main (int argc, char* argv[]) {
07 if (argc < 2) {
08 std::cout << "Usage: tut1 path\n";
09 return 1;
10 }
11 std::cout << argv[1] << " " << file_size(argv[1]) << '\n';
12 return 0;
13 }
这里的 boost::filesystem::filesize 就是一个用来获得文件大小的函数。
事实上,有更多的所谓的 operational function:
* absolute 取绝对路径
* copy、copy_file、copy_directory 和 copy_symlink 进行复制
* create_directory/create_directories、create_hard_link 和 create_symlink 用于创建需要的目录和连接
* current_path 返回当前路径
* exists 判断 path 是否存在
* equivalent 判断两个文件的 file_status 是否相同
* hard_link_count 硬连接数
* is_directory 判断是否为目录,is_empty 是否空文件,is_other 别的类型,is_regular_file 是否为正常文件,is_symlink 是否符号链接
* read_symlink 读符号链接
* remove 删除和 remove_all 删除所有文件
* rename 重命名
* resize_file 改变文件大小
* space 返回占用空间信息,status 返回 file_status
* temp_directory_path 获得临时文件路径
* unique_path 返回一个 UUID
另外有一些支持的类,比如:
* path 用来创建一个路径,比起一般的字符串,将有更多的功能,如判断是否绝对路径 is_absolute/is_relative 等函数,转换(make_absolute)或者替换扩展名(replace_extension)等;
* filesystem_error __将对应的 errno 转换成为了 exception 机制__,前面的例子只需要换一下参数访问一个不存在的文件就会抛出异常;
* 用于访问目录的 directory_entry 和__遍历目录__的 directory_iterator、directory_recursive_iterator。
* file_status 用于获得文件的信息。
* fstream 和 std::fstream 的区别在于使用 path 作为 constructor 的输入
下面是一个稍微复杂一点的例子:
01 #include <iostream>
02 #include <boost/filesystem.hpp>
03
04 namespace bfs = boost::filesystem ;
05
06 int
07 main (int, char**) {
08 for (bfs::recursive_directory_iterator it =
09 bfs::recursive_directory_iterator (bfs::current_path()) ;
10 it != bfs::recursive_directory_iterator() ; ++ it) {
11 const bfs::path& cp = it -> path() ;
12 std::cout << cp << '\t'
13 << (bfs::is_regular_file (cp) ?
14 static_cast<int> (bfs::file_size (cp)) : -1) << std::endl ;
15 }
16 return 0;
17 }
这个还是很不错的,写代码都很简单了…

View File

@@ -0,0 +1,276 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-02-14T15:56:06+08:00
====== 了解 Boost Filesystem Library ======
Created Tuesday 14 February 2012
http://www.ibm.com/developerworks/cn/aix/library/au-boostfs/
简介: 缺乏定义良好的、用于处理文件系统操作的库,这一直是 C++ 语言存在的一个问题。过去,程序员必须使用__本机 API__ 来解决此问题。通过本文您将了解一个提供安全、可移植且易用的 C++ 接口来促进文件系统操作的库:Boost Filesystem Library。
发布日期: 2008 年 6 月 17 日
级别: 中级
其他语言版本: 英文
访问情况 : 4575 次浏览
评论: 0 (查看 | 添加评论 - 登录)
平均分 4 星 共 5 个评分 平均分 (5个评分)
为本文评分
C++ 语言(实际上是 C++ 标准)的最常见问题之一是,缺乏定义良好的库来帮助处理__文件系统查询和操作__。由于这个原因,程序员不得不使用本机操作系统提供的应用程序编程接口(Application Program Interfaces,API),而这使得代码不能在平台之间移植。以下面的简单情况为例:您需要确定某个文件是否是 Directory 类型。在 Microsoft® Windows® 平台中,可以通过调用 GetAttributes 库函数(在 windows.h 头文件中定义)进行此操作:
DWORD GetFileAttributes (LPCTSTR lpFileName);
对于目录,所得到的结果应该为 FILE_ATTRIBUTE_DIRECTORY,而您的代码必须检查是否为此结果。在 UNIX® 和 Linux® 平台上,可以通过使用 **stat 或 fstat **函数及 sys/stat.h 中定义的 **S_ISDIR 宏**来实现相同的功能。您还必须理解 stat 结构。下面是对应的代码:
#include <sys/stat.h>
#include <stdio.h>
int main()
{
struct stat s1;
int status = stat(<const char* denoting pathname>, &s1);
printf(“Path is a directory : %d\n”, S_ISDIR(s1.st_mode));
return 0;
}
对于 I/O 操作较多的程序,这样的不一致就意味着需要进行大量的工程工作才能__在平台间移植代码__。正是因为这个原因,我们才引入了 Boost Filesystem Library。这个广泛使用的库提供了安全、可移植且易用的 C++ 接口,用于执行文件系统操作。可以从 Boost 站点免费下载此库。
===== 使用 boost::filesystem 的第一个程序 =====
在深入研究 Boost Filesystem Library 的更多细节之前,请看一下清单 1 中所示的代码;此代码使用 Boost API 确定某个文件的类型是否为 Directory。
**清单 1. 用于确定某个文件的类型是否为 Directory 的代码**
#include <stdio.h>
#include “boost/filesystem.hpp”
int main()
{
**boost::filesystem::path** path("/usr/local/include"); // random pathname
bool result = boost::filesystem::is_directory(path);
printf(“Path is a directory : %d\n”, result);
return 0;
}
此代码非常明了易懂,您并__不需要了解任何系统特定的例程__。此代码经过验证,能在不用修改的情况下在 gcc-3.4.4 和 cl-13.10.3077 上成功编译。
===== 了解 Boost path 对象 =====
了解 Boost Filesystem Library 的关键是 __path 对象__,因为 Filesystem Library 中定义的多个例程都要对相应的 path 对象操作。**文件系统路径通常依赖于操作系统**。例如,众所周知,UNIX 和 Linux 系统使用正斜杠 ( /) 字符作为目录分隔符,而 Windows 将反斜杠 (\) 字符用于类似的用途。boost::filesystem::path 旨在__准确地抽象此特性__。path 对象可以通过多种方式进行初始化,最常见的方式是使用 char* 或 std::string 进行初始化,如清单 2 中所示。
**清单 2. 创建 Boost path 对象的方法**
path(); // empty path
path(const char* pathname);
path(const std::string& pathname);
path(const char* pathname, boost::filesystem::path::name_check checker);
path(const char* pathname, boost::filesystem::path::name_check checker);
在初始化 path 对象时,可以采用本机格式或可移植操作系统接口(Portable Operating System Interface,POSIX)委员会定义的可移植格式提供 PATHNAME 变量。这两种方法在实际中各有优缺点。考虑以下情况:您希望操作软件所创建的目录,此目录在 UNIX 和 Linux 系统上位于 /tmp/mywork,而在 Windows 上位于 C:\tmp\mywork。可以采用多种方法处理问题。清单 3 显示了面向本机格式的方法。
清单 3. 使用本机格式初始化 path
**#ifdef UNIX**
boost::filesystem::path path("/tmp/mywork");
#else
boost::filesystem::path path("C:\\tmp\\mywork ");
#endif
需要单个 #ifdef 来按操作系统初始化 path 对象。不过,如果您喜欢使用可移植格式,请参见清单 4。
清单 4. 使用可移植格式初始化 path
boost::filesystem::path path("/tmp/mywork");
请注意,path::name_check 指的是一个**名称检查函数原型**。如果其参数输入 PATHNAME __对于特定的操作系统或文件系统有效__,名称检查函数将返回“True”。Boost Filesystem Library __提供了多个名称检查函数__,而且也欢迎您提供自己的变体。常用的名称检查函数是 Boost 提供的 **portable_posix_name 和 windows_name**。
===== path 成员函数概述 =====
path 对象提供了多个成员方法。这些成员例程并不会修改文件系统,但会根据 path 名称提供有用的信息。此部分提供了其中几个例程的概述:
* const std::string& string( ):此例程会返回用于初始化 path 的字符串的副本,其格式符合 path 语法规则。
* std::string root_directory( ):在提供了路径的情况下,此 API 将返回__根目录__,否则将返回空字符串。例如,如果路径包含 /tmp/var1,则此例程将返回 /,即 UNIX 文件系统的根。不过,如果路径是相对路径,如 ../mywork/bin,此例程将返回空字符串。
* std::string root_name( ):在给定从文件系统根目录开始的路径的情况下,此例程将返回包含 PATHNAME 的第一个字符的字符串。
* std::string leaf( ):在给定绝对路径名称(例如,/home/user1/file2)的情况下,此例程将提供__与文件名称对应的字符串__(即 file2)。
* std::string branch_path( ):这是__与 leaf 互补的例程__。在给定路径的情况下,将会返回其构造所用的所有元素(除了最后一个元素)。例如,对于使用 /a/b/c 初始化的 path,path.branch_path( ) 将返回 /a/b。对于包含单个元素的路径,如 c,此例程将返回空字符串。
* bool empty( ):如果 path 对象包含空字符串(例如 path path1("")),则此例程将返回 True。
* boost::filesystem::path::iterator:此例程__用于遍历 path 的各个元素__。请看清单 5 所示的代码。
清单 5. 使用 path::iterator(begin 和 end 接口)
#include <iostream>
#include “boost/filesystem.hpp”
int main()
{
boost::filesystem::path path1("/usr/local/include"); // random pathname
boost::filesystem::path::iterator pathI = path1.begin();
while (pathI != path1.end())
{
std::cout << *pathI << std::endl;
++pathI;
}
return 0;
}
// result: 1
上述程序的输出依次是 /、usr、local、include,代表了该目录的层次结构。
path operator / (char* lhs, const path& rhs):此例程是 path 的非成员函数。它将返回使用 lhs 和 rhs 形成的路径的串联值。它将自动插入 / 作为路径分隔符,如清单 6 中所示。
清单 6. 路径字符串的串联
#include <iostream>
#include “boost/filesystem.hpp”
int main()
{
boost::filesystem::path path1("/usr/local/include"); // random pathname
boost::filesystem::path::iterator pathI = path1.begin();
while (pathI != path1.end())
{
std::cout << *pathI << std::endl;
++pathI;
}
return 0;
}
// result: 1
===== 错误处理 =====
文件系统操作经常遇到意外的问题,Boost Filesystem Library 将__使用 C++ 异常报告运行时错误__。boost::filesystem_error 类派生自 std::runtime_error 类。库中的函数使用 filesystem_error 异常报告操作错误。与不同的可能错误类型对应,Boost 头文件定义了相应的错误代码。用户代码通常驻留在 try...catch 块内,使用 filesystem_error 异常来报告相关错误消息。清单 7 提供了重命名文件的小示例,在 from 路径中的文件不存在时引发异常。
清单 7. Boost 中的错误处理
#include <iostream>
#include “boost/filesystem.hpp”
int main()
{
try {
boost::filesystem::path path("C:\\src\\hdbase\\j1");
boost::filesystem::path path2("C:\\src\\hdbase\\j2");
boost::filesystem::rename(path, path2);
}
catch(boost::filesystem::filesystem_error e) {
// do the needful
}
return 0;
}
===== Boost Filesystem Library 中的函数类别 =====
boost::filesystem 提供了不同类别的函数:有些函数(如 is_directory)用于查询文件系统,而其他函数(如 create_directory)则主动对文件系统进行修改。根据各自功能的不同,这些函数可以大略归入以下类别:
* __属性函数__:**提供杂项信息**,如文件大小、磁盘使用量等。
* 文件系统__操作函数__:用于创建常规文件、目录和符号链接;复制和重命名文件;提供删除功能。
* **实用工具**:测试文件的扩展名等。
* 杂项常规函数:以编程方式更改文件扩展名等。
==== 属性函数 ====
Boost Filesystem Library 包括以下属性函数:
* uintmax_t file_size(const path&):返回**常规文件的大小**(以字节为单位)
* boost::filesystem::space_info space(const path&):接受路径作为输入,并返回定义如下的 space_info 结构:
struct space_info {
uintmax_t capacity;
uintmax_t free;
uintmax_t available;
};
根据文件系统所属的磁盘分区,此流程将对该分区的所有目录返回相同的__磁盘使用量统计数据__(以字节为单位)。例如,对于 C:\src\dir1 和 C:\src\dir2,都会返回相同的磁盘使用数据。
* std::time_t last_write_time(const path&):返回文件的最后修改时间。
* void last_write_time(const path&, std::time_t new_time):__修改__文件的最后修改时间。
* const path& current_path( ):返回程序的__当前工作目录的完整路径__(注意,此路径与最初运行程序的路径可能不同,因为可能采用编程方式更改目录)。
===== 文件系统操作函数 =====
这组函数负责进行新文件和目录创建、文件删除等操作:
* bool create_directory(const path&):此函数使用给定的路径名称__创建目录__。(请注意,如果 PATHNAME 本身包含无效字符,则结果经常是由平台定义的。例如,在 UNIX 和 Windows 系统中,星号 (*)、问号 (?) 及其他此类字符视为无效,不能出现在目录名称中。)
* bool create_directories(const path&):与创建单个目录相对,您可以使用此 API__ 创建目录树__。例如,以目录树 /a/b/c 为例,必须在 /tmp 文件夹内创建此目录树。可调用此 API 完成任务,但使用相同的参数调用 create_directory 时将引发异常。
* bool create_hard_link (const path& frompath, const path& topath):此函数在 frompath 和 topath 间__创建硬链接__。
* bool create_symlink(const path& frompath, const path& topath):此函数在 frompath 和 topath 间__创建符号(软)链接__。
* void copy_file(const path& frompath, const path& topath):将 frompath 引用的文件的内容和属性复制到 topath 引用的文件中。例程expects a destination file to be absent;如果存在目标文件,则会引发异常。因此,此函数与 UNIX 中系统指定的 cp 命令并不等效。另外,此函数还预期 frompath 变量将引用正确的常规文件。请看以下示例:frompath 引用符号链接 /tmp/file1,而后者反过来引用文件 /tmp/file2;而 topath 可以为 /tmp/file3。在这种情况下,copy_file 将失败。这是此 API 与 cp 命令相比的另一个差别。
* void rename(const path& frompath, const path& topath):此函数是用于__重命名文件__的 API。可以通过在 topath 参数中指定完整路径名来同时重命名和更改文件的位置,如清单 8 中所示。
清单 8. Boost 中的重命名功能
#include <stdio.h>
#include “boost/filesystem.hpp”
int main()
{
boost::filesystem::path path("/home/user1/abc");
boost::filesystem::rename(path, "/tmp/def");
return 0;
}
// abc is renamed def and moved to /tmp folder
* bool remove(const path& p):此例程将尝试__删除__路径 p 所引用的文件或目录。对于目录的情况,如果目录的内容不为空,则此例程将引发异常。警告:此例程并不考虑所删除的内容,即使其他程序在访问同一文件也如此!
* unsigned long remove_all(const path& p):此 API 尝试删除路径 p 所引用的__文件或目录__。与 remove 不同,此函数并不会特殊考虑不为空的目录。此函数是 UNIX rm –rf 命令的 Boost 对等项。
===== 实用工具 =====
Boost Filesystem Library 包含以下实用工具:
* bool exists(const path&):此函数检查文件的__扩展名__。文件可以为任何类型:常规文件、目录、符号链接等等。
* bool is_directory(const path&):此函数检查路径是否与目录对应。
* bool is_regular(const path&):此函数检查普通文件(即此文件不是目录、符号链接、套接字或设备文件)。
* bool is_other(const path&):通常,此函数检查设备文件(如 /dev/tty0)或套接字文件。
* bool is_empty(const path&):如果路径与文件夹对应,此函数将检查文件夹__是否为空__,并据此返回“True”或“False”。如果路径与文件对应,此函数将检查文件的大小是否等于 0。对于文件的硬链接或符号链接的情况,此 API 将检查原始文件是否为空。
* bool equivalent(const path1& p1, const path2& p2):此 API 非常实用,可用于比较相对路径和绝对路径名。请看清单 9:
清单 9. 测试两个路径是否等效
#include <stdio.h>
#include “boost/filesystem.hpp”
int main()
{
boost::filesystem::path path1("/usr/local/include"); // random pathname
boost::filesystem::path path2("/tmp/../usr/local/include");
bool result = boost::filesystem::is_equivalent(path1, path2);
printf(“Paths are equivalent : %d\n”, result);
return 0;
}
// result: 1
* path system_complete(const path&):此函数是与 bool equivalent(const path1& p1, const path2& p2) 同一系列的另一个 API。在给定当前工作目录中任意文件路径的情况下,此 API 将__返回该文件的绝对路径__。例如,如果用户位于目录 /home/user1 并查询文件 ../user2/file2,此函数将返回 /home/user2/file2,即文件 file2 的完整路径名。
===== 杂项函数 =====
Boost Filesystem Library 包括以下杂项函数:
* std::string extension(const path&):此函数以前面带句点 (.) 的形式返__回给定文件名的扩展名__。例如,对于文件名为 test.cpp 的文件,extension 将返回 .cpp。对于文件没有扩展名的情况,此函数将返回空字符串。对于隐藏文件(即 UNIX 系统中文件名以 . 开始的文件),此函数将相应地计算扩展名类型或返回空字符串(因此,对于 .test.profile,此例程将返回 .profile)。
* std::string basename(const path&):这是与 extension 互补的例程。它将__返回文件名中 . 之前的字符串__。请注意,即使提供了绝对文件名,此 API 仍然仅会返回属于文件名的直接部分,如清单 10 中所示。
清单 10. 使用 boost::basename
#include <stdio.h>
#include <cstring>
#include “boost/filesystem.hpp”
use namespace std;
int main()
{
boost::filesystem::path path1("/tmp/dir1/test1.c ");
boost::filesystem::path path2("/tmp/dir1/.test1.profile");
string result1 = boost::filesystem::basename (path1);
string result2 = boost::filesystem::basename (path2);
printf(“Basename 1: %s Basename2 : %s\n”, result1.c_str(), result2.c_str());
return 0;
}
// result: Basename1: test1 Basename2: .test1
* std::string change_extension(const path& oldpath, const std::string new_extension):此 API 将返回反__映更改后的名称的新字符串__。请注意,与 oldpath 对应的文件保持不变。这只是一个常规函数。另请注意,您必须显式地在扩展名中指定点。例如,change_extension("test.c", "so") 会得到 testso,而不是 test.so。
===== 结束语 =====
本文提供了 Boost Filesystem Library 的简单概述。不应将本文视为 Boost 中的整个文件系统接口的综合文档。并未讨论此 API 集的内部情况,也没有讨论这些 API 在非 UNIX 或 Windows 平台(如 VMS)中的细节。有关文件系统的更多信息,请参见参考资料。