1. 首先定义一个PCH文件,把所有的公共的include都加到这个文件中来
  2. 定义一个cpp文件,只include该PCH文件
  3. bjam的Jamfiles里增加cpp-pch pch : <pch头文件> ;以及在lib或者exe中将pch包括到编译列表中
  4. 所有的其它的cpp文件都需要在第一行include这个PCH文件
  5. 详细可以参见:http://www.boost.org/boost-build2/doc/html/bbv2/reference/precompiled_headers.html

1. 首先在Windows下设置PATH对应到qt的mingw\bin目录
2. 将boost解压缩,然后cd到boost\tools\build\v2\engine下,执行build mingw来编译bjam
3. 编译好后,在当前的目录下应该有一个bin.ntx86目录,进入到该目录下,将bjam.exe拷贝到boost源代码的根目录下
4. 回到boost源代码的根目录下,执行bjam
5. 将boost\boost目录复制到qt的mingw的include目录
6. 将stage\libs里所有的库文件复制到qt的mingw的lib目录
7. 在QT中修改qmake文件来链接boost库文件,若要使用线程,要注意定义BOOST_THREAD_USE_LIB这个预定义,例如:

DEFINES += BOOST_THREAD_USE_LIB
 
debug {
    LIBS += libboost_system-mgw47-mt-d-1_53 libboost_thread-mgw47-mt-d-1_53
}
 
release {
    LIBS += libboost_system-mgw47-mt-1_53 libboost_thread-mgw47-mt-1_53
}

代入的第一个字符串变量是不会被regex_match函数拷贝的,所以一定要保证在使用smatch取匹配项的时候该字符串内存仍然是有效的,也就是,杜绝这样的调用

regex_match(std::string("hello world"), ...);

1. 概述:QT是跨平台的C++界面库,并且大部分可以通过拖拉控件的方式来方便开发,并且,在未使用特定平台特性的情况下,可以很容易跨平台编译,这意味着使用QT来开发的GUI程序,可以在任何平台下编译运行;并且,QT使用了比较先进的C++技术,比起MFC这个老又重的类库来说,着实先进了不少,开发起程序来应该会更加简单,程序结构也会更加清晰明了
2. 编译器:最初,使用QT来开发,打算使用MINGW编译器,也就是GCC在WINDOWS下的实现,但是发现MINGW配置比较难用,特别在编译BOOST库,以及链接BOOST库的时候,会碰到很多麻烦的事情,于是寻求使用VC编译器,其实VC编译器是有免费版本的,分别可以下载Microsoft Visual Studio Express版本或者在最新的Platform SDK中也包含有。在本文中,推荐使用最新的Platform SDK,因为在Platform SDK中不止是包含有VC2010的编译器,还包含有CDB调试工具
3. IDE:QT可以以插件的方式安装在Visual Studio IDE中,但是可安装QT插件的Visual Studio不是免费的,另外Visual Studio构建于.NET之上,想要安装Visual Studio还需要一大堆.NET和其它不需要的东西,比如MSSQL等。QT Creator是QT这个工具本身提供的开发IDE,支持C++语法高亮以及基本的代码提示功能,并且有我比较喜欢的VIM模拟,其实也算是一个比较成熟的IDE了,另外,若使用QT Creator来管理代码,那么在别的平台下也一样可以使用QT Creator(包括MAC OS以及各大LINUX发行版)来编译在WINDOWS下建立的工程,所以,在本文中选择使用QT Creator作为IDE开发平台
4. QT的结构:QT其实分为两大块:a. QT Creator – 一个纯C++写的IDE平台,本身该IDE应该构建于QT基础上; b. 根据不同的平台而编译好的QT Lib,在Windows平台下可能为QT library for MingW或者QT library for VC2008/VC2010;在这里,使用QT Creator for windows + QT library for VC2010
5. 基本的情况介绍完成,现在在网上下载各个需要的部件:

6. 下载好各个安装包后,首先安装.NET Framework 4.0,这个很简单,双击下载下来的安装文件,安装完成后,会被要求重启系统
7. 然后安装SDK包,使用虚拟光盘软件打开该SDK镜像(或者你可以直接使用Web installer,但是这样会比较慢一些,取决于你的网络状况)
8. 安装QT Creator,这个也很容易,但是注意在安装的时候去掉MingW选项,因为不需要
9. 双击打开QT Library源代码,该包包含有QT所有的类库、工具以及DEMO的源代码,将该源代码解压到c:\qt\4.7.2目录下(若你下载的源代码版本不为4.7.2,则请自行修改目录名称,以下不再复述)
9. 这时启动“所有程序”中的“Windows SDK 7.1 Command Prompt”,来到命令行模式。使用以下命令编译QT Library源代码:

c:
cd c:\qt\4.7.2
configure # 打入configure命令后,会被寻问使用哪种license,这时选择社区免费版本“o”,然后会被寻问是否接受许可协议,这时选择“y”,稍等一下,编译文件将会被自动配置
nmake # 编译文件配置完成后,打入该命令进行编译,这个时间非常长,所以建议编译完成后,将c:\qt\4.7.2目录压缩保存

10. 编译Boost库,将Boost源代码解压缩至c:\boost\srcs目录下,然后进入该目录输入以下命令:

c:
cd c:\boost\srcs\boost_1_45_1
bootstrap
bjam
bjam install

11. QT Creator使用配置环境变量来指明当前的编译器,包含头文件以及库文件位置,所以最后一步需要配置环境变量。注,若安装的文件都为C盘默认目录并且版本号都一致的话,可以直接使用下方的环境变量的值

INCLUDE=C:\Program Files\Microsoft SDKs\Windows\v7.1\Include;C:\Program Files\Microsoft Visual Studio 10.0\VC\include;C:\boost\include\boost-1_46_1
LIB=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib;C:\Program Files\Microsoft Visual Studio 10.0\VC\lib;c:\boost\lib
PATH=c:\qt\4.7.2\bin;C:\Program Files\Microsoft Visual Studio 10.0\VC\bin;C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE;C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin

12. 这时,打开QT Creator,一切应该都配置完成了,使用QT Creator建立一个简单的程序来编译和调试吧,若在使用QT Creator的过程中碰到问题,请仔细检查以上各项的参数是否都设置正确,特别若是下载的软件包版本号不一至的情况下,特别需要小心路径问题。

经过一段时间的Asio使用,理清楚了一些基本的概念,这里和shared_ptr指针一起总结一下
1、Asio中,不管写(Write)还是读(Read)都需要等待相应的事件完成后再发起下一次写或者读。读操作比较好办,在handle_read事件中直接进行下一次async_read操作就可以,但是写的话得自己管理一个deque队例,在写入操作完成后则自动把最顶的数据包弹出,然后开始写下一个(如果在缓冲队列中还有剩余的数据包);
2、Asio中,不管任何的函数调用,若有未涉及error_code和涉及error_code的相同功能函数存在,则使用涉及error_code的函数调用,并且处理错误信息,否则io_service会因为异常而退出消息循环;
3、若session或其它的类是使用shared_ptr来包装的,则需要将该类继承于enable_shared_from_this,否则会有可能在该对象已经被删除的情况下,该对象内的异步回调函数被调用,这样会导致程序崩溃退出;
4、要注意,当类继承了enable_shared_from_this后,在构造函数中千万不要调用shared_from_this()函数,否则程序会抛掷异常;
5、如果类中有方法暴露在外,而有可能是非线程安全调用的,则使用io_service::post函数来调用asio中的函数,以保证asio的回调是线程安全的;

示例代码如下:(注:因为只是代码片断,随便手写,而且只是为了说明问题,所以并未检查过编译是否通过)

#include <deque>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
 
using namespace boost::asio;
using namespace boost::asio::ip;
 
typedef boost::shared_ptr<tcp::socket> socket_ptr;
typedef std::pair<void*, std::size_t> buffer_type;
typedef std::deque<buffer_type> buffer_deque;
 
class session : public boost::enable_shared_ptr_from_this<session>
{
public:
  session(io_service& ios, socket_ptr sp)
    : ios_(ios)
    , sp_(sp)
  {
  }
  void start_read()
  {
    async_read_until(*sp_, sb_, '\n', boost::bind(&session::handle_read, shared_from_this(), placeholders::error);
  }
  void send(void const* p, std::size_t size)
  {
    bool need_write = buffers_.empty();
    buffers_.push_back(std::make_pair(p, size));
    if (need_write) ios_.post(boost::bind(&session::do_send, shared_from_this()));
  }
  void close()
  {
    boost::system::error_code ec;
    sp_->shutdown(tcp::socket::shutdown_both, ec);
    if (ec) std::cout << ec.message().c_str() << std::endl;
    sp_->close(ec);
    if (ec) std::cout << ec.message().c_str() << std::endl;
  }
private: // do functions
  void do_send()
  {
    async_write(*sp, buffer(buffers_.begin()->first, buffers_.begin()->second), boost::bind(&sessions::handle_write, shared_from_this(), placeholders::error));
  }
private: // handlers
  void handle_read(boost::system::error_code const& ec)
  {
    if (!ec)
    {
      std::istream is(&sb_);
      std::string cmd;
      std::getline(is, cmd);
      // todo: handle command
      start_read(); // start next round
    }
    else
    {
      std::cout << ec.message().c_str() << std::endl;
      close();
    }
  }
  void handle_write(boost::system::error_code const& ec)
  {
    if (!ec)
    {
      buffers_.pop_front();
      if (!buffers_.empty()) do_send();
    }
    else
    {
      std::cout << ec.messages().c_str() << std::endl;
      close();
    }
  }
private:
  io_service& ios_;
  socket_ptr sp_;
  streambuf sb_;
  buffer_deque buffers_;
};

在asio库中的很多异步函数是需要调用者保持数据内存有效性一直到该异步方法被handle,这样如果按照正常的做法应该把该数据作为类的成员变量(这样在整个对象的生存期间该数据都有效了)。但是这样做却很不优雅,今天看到有这样的写法

class cmd_writer
{
public:
  template <typename C>
  void write_command(boost::asio::ip::tcp::socket& socket, int const iden, C const& cmd)
  {
    cmd_container_ptr p(new cmd_container(iden, cmd)); // 内存在这里被分配
    boost::asio::async_write(socket, boost::asio::buffer(p->get(), p->size()),
      boost::bind(&cmd_writer::handle_write, this,
        boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred,
        p // 在这里将该智能指针对象传递下去,直至handle函数启用后才删除
      )
    );
  }
 
private:
  void handle_write(boost::system::error_code const& err, std::size_t const bytes_transferred,
    cmd_container_ptr p)
  {
    if (!err)
    {
      p.reset(); // 最后到了这里才释放内存
    }
  }
};

这种写法使用boost:bind函数将需要保持的对象智能指针对象从异步调用函数内传递给该异步调用函数的handle函数,完美解决了异步调用中保持数据有效性的问题而却又不增加多余的成员函数。

io_service::run函数在没有任何任务的时候将会自动返回,这对于WTL的项目来说并不方便,之前我有一篇文章讲到使用一个循环来运行该run函数,那是十分不优雅的。今天在网上再次查找办法,终于找到io_service::work类可以使io_service::run函数在没有任务的时候仍然不返回,直至work对象被销毁。

boost::asio::io_service ios;
boost::asio::io_service::work work(ios); // 使用work对象
ios.run(); // 就算是当前没有任务,ios.run()也不会马上返回

或者,下面从我的程序中直接拷贝出来的例子,该程序使用WTL框架

boost::asio::io_service m_ios;
std::shared_ptr<boost::thread> m_ios_thread;
std::shared_ptr<tm_client> m_client_ptr;
std::shared_ptr<boost::asio::io_service::work> m_work;
 
m_work.reset(new boost::asio::io_service::work(m_ios));
m_ios_thread.reset(new boost::thread(boost::bind(&boost::asio::io_service::run, &m_ios)));
 
// resolve the hostname
boost::asio::ip::tcp::resolver resolver(m_ios);
boost::asio::ip::tcp::resolver::query q(hostname, boost::lexical_cast<std::string>(x::tl::PORT_TMSRV));
boost::asio::ip::tcp::resolver::iterator res_begin = resolver.resolve(q), res_end;
 
if (res_begin != res_end)
{
  // constructor client connection object
  m_client_ptr.reset(new tm_client(m_ios, tm_window_notifier(*this)));
  m_client_ptr->connect(*res_begin);
}

另外可以参考文章:Stopping the io_service from running out of work