从零到壹学习比特币源码解析第一讲:准备知识-Boost
黎跃春
孔壹学院、ChainDesk创始人兼CEO
从零到壹学习比特币源码解析为一个系列,一共11讲,包括准备知识、源码解析等。今天我们将为大家介绍从零到壹学习比特币源码解析第一讲:准备知识-Boost。话不多说,马上开启我们的比特币源码解析学习之旅。
Boost是一个开源、跨平台、功能强大的c++库,并且是除了stl外最常用的库,实现了很多基本操作,能让开发变得更加简单、快捷。下面我们就介绍bitcoin源码中主要用到的一些类,官方文档见:
https://www.boost.org/doc/libs/1_67_0/
Signals2
Signals2是基于Boost的另一个库Signals,实现了线程安全的观察者模式。而观察者模式又是指:定义对象间的一种一对多的依赖关系,当一个对象发生改变时,所有依赖于它的对象都将得到通知并自动更新。 而在Signals2库中,观察者模式又被称为信号/插槽(Signals and slots),官方文档对其解释为
TheBoost.Signals2libraryisanimplementationofamanagedsignalsandslotssystem.Signalsrepresentcallbackswithmultipletargets,andarealsocalledpublishersoreventsinsimilarsystems.Signalsareconnectedtosomesetofslots,whicharecallbackreceivers(alsocalledeventtargetsorsubscribers),whicharecalledwhenthesignalis“emitted.”Boost.Signals2库是一个改善的信号/插槽系统的实现。信号在类似的系统中也被称为发布者或者事件,表示多个回调目标。信号会和多个插槽相连,插槽也就是回调的接收者(也被称为事件目标或者订阅者),当信号发射的时候,所有关联的插槽都会被调用。Signalsandslotsaremanaged,inthatsignalsandslots(or,moreproperly,objectsthatoccuraspartoftheslots)cantrackconnectionsandarecapableofautomaticallydisconnectingsignal/slotconnectionswheneitherisdestroyed.Thisenablestheusertomakesignal/slotconnectionswithoutexpendingagreatefforttomanagethelifetimesofthoseconnectionswithregardtothelifetimesofallobjectsinvolved.信号/插槽被设计成能够跟踪连接状态以及在双方任何一个被销毁时自动断开连接。这就使得用户能够方便的使用信号/插槽连接关系,而不用再去管理这种连接关系涉及的所有对象的生命周期。
通俗的来讲,信号就是一个触发器,插槽就是一些列的回调函数,当信号发射时,也就是触发器被触发的时候,所有的回调函数都会被调用。信号/插槽机制功能就是把这些功能相关的函数汇集到一起,在某一时刻,按顺序依次调用。
明白它要实现的功能之后,我们再来看一个简单的实例。
//example1.cpp#include#include"boost/signals2.hpp"usingnamespacestd;voidslot1(){cout<sig;//定义信号sig.connect(&slot1);//信号关联插槽sig.connect(&slot2);sig();//出发信号return0;}
运行:
$g++example1.cpp$./a.out#运行结果solt1callsolt2call
一定要安装 boostUbuntu 16.04 LTS,使用命令sudo apt-get install libboost-all-dev安装完boostmac 使用命令 brew install boost 安装 boost
Bind
bind并不是一个单独的类或函数,而是非常庞大的家族,依据绑定的参数个数和要绑定的调用对象类型,总共有数十个不同的形式,但它们的名字都叫做bind,编译器会根据具体的绑定代码自动确定要使用的正确形式。
bind接受的第一个参数必须是一个科调用对象f,包括函数指针、函数引用、成员函数指针和函数对象,之后bind接受最多九个参数,参数的数量必须与f的参数数量相等,这些参数将被传递给f作为形参。
绑定完成后,bind会返回一个函数对象,它内部保存了f的拷贝,具有operator(),返回值类型被自动推导为f的返回值类型。在发生调用时,这个函数对象将把之前存储的参数转发给f完成调用。
简单来说,Bind的功能就是对一个函数绑定某些参数,其中参数有一个很重要的概念叫做占位符,被定义为从1到9,下面来看一个简单的实例。
//example2.cpp#include"boost/bind.hpp"#include#include usingnamespacestd;intf(inta,intb){returna+b;}intg(inta,intb,intc){returna+b+c;}structP{intx,y;P(inta=9,intb=8):x(a),y(b){}voidprint(){cout<v(10);for_each(v.begin(),v.end(),boost::bind(&P::print,_1));//print:P.x,P.y,当引用成员函数时,占位符第一个总表示对象实例return0;}
运行:
$g++example2.cpp$./a.out#运行结果364x:9y:8x:9y:8x:9y:8x:9y:8x:9y:8x:9y:8x:9y:8x:9y:8x:9y:8x:9y:8
Thread
线程,是各种项目中经常会用到的一个技术,而一般提到线程都会涉及到多线程,多线程当中最经典的问题就是同步访问共享资源,和其他几乎所有语言一样boost也是通过提供互斥锁来解决的,但不同的是boost提供了多个互斥类,使得项目可以更灵活的处理共享资源。
//example3.cpp#include"boost/thread.hpp"#includeusingnamespacestd;boost::mutexm_mutex;voidwait(intsec){boost::this_thread::sleep(boost::posix_time::seconds(sec));}voidwork(){for(inti=0;i<5;i++){wait(1);cout<lock(mutex);lock_guard在内部构造和析构函数分别自动调用lock()和unlock(),所以能自动将当前域设为互斥访问区域。更多资料请参考:http://zh.highscore.de/cpp/boost/multithreading.html********************/}}intmain(){boost::threadth1(&work);//最简单的调用,不带任何参数boost::threadth2(boost::bind(&work1,2));boost::threadth3(boost::bind(&work1,3));//bind的一个重要应用,绑定参数!这里两个线程不加任何互斥,打印出来的是乱序boost::threadth4(boost::bind(&work2,4));boost::threadth5(boost::bind(&work2,5));//4,5线程加简单的互斥锁,结果按次序打印th1.join();//阻塞当前进程,等待调用线程完成,防止主线程先结束th2.join();th3.join();th4.join();th5.join();return0;}
运行:
$g++example3.cpp-lboost_thread-mt-lboost_system$./a.out#运行结果id:40id:41id:50id:42id:51id:43id:52id:44id:53id:54id:id:id:231000id:id:21id:1311id:22id:id:1322id:id:213id:333id:id:id:123444
Chrono
Chrono是Boost库中用于时间处理的库,主要包含三个概念时间段(duration),时间点(time_point)和时钟(clock)。
//example4.cpp#include#include"boost/chrono.hpp"usingnamespacestd;//durations表示一段时间间隔typedefboost::chrono::hourshours;typedefboost::chrono::minutesminutes;typedefboost::chrono::secondsseconds;typedefboost::chrono::millisecondsmilliseconds;typedefboost::chrono::microsecondsmicroseconds;typedefboost::chrono::nanosecondsnanoseconds;//clock表示当前时间,是在不断的变化typedefboost::chrono::system_clocksystem_clock;typedefboost::chrono::steady_clocksteady_clock;typedefboost::chrono::high_resolution_clockhigh_resolution_clock;//timepoint表示某一个具体的时间点typedefsystem_clock::time_pointsys_tp;intmain(){hoursh1(1);minutesm1(1);minutesm2=h1+m1;//只能转化为更小的单位cout< (h1+m1);//强制转换cout< 运行:
$g++example4.cpp-lboost_chrono-mt$./a.out#运行结果61minutes1hour1524449285833521000nanosecondssinceJan1,1970
Program options
在编写命令行程序时,经常会碰到的一个问题就是参数解析问题,就是说我们在运行程序时给程序附上不同的参数值使得程序能够完成不同的功能。而boost中的program options就是用来处理命令行传入的参数的模块,使得程序更简洁高效。
//example6.cpp#include#include #include #include usingnamespacestd;intmain(intargc,char*argv[]){usingnamespaceboost::program_options;options_descriptiondesc("Commandlineoptions");//定义描述选项:(选项全称,缩写),(参数类型->默认值),(描述)desc.add_options()("help,h","printhelpmessage")("person,p",value ()->default_value("world"),"personname")("file,f",value >(),"inputfilename");variables_mapvm;//存储传入的参数store(parse_command_line(argc,argv,desc),vm);//根据描述选项解析参数notify(vm);if(vm.count("help")){cout< ()< files(vm["file"].as >());cout< 运行:
$g++example6.cpp-lboost_program_options$./a.out#运行结果Helloworld$./a.out-h#运行结果Commandlineoptions:-h[--help]printhelpmessage-p[--person]arg(=world)personname-f[--file]arginputfilename$./a.out-pSplay#运行结果HelloSplay