上一次,我们代码看到了
gArgs.ParseParameters(argc, argv);
分析输入的参数,今天我们继续看下面的代码。
// Process help and version before taking care about datadir
if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
{
std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";
if (gArgs.IsArgSet("-version"))
{
strUsage += FormatParagraph(LicenseInfo());
}
else
{
strUsage += "\n" + _("Usage:") + "\n" +
" bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
strUsage += "\n" + HelpMessage(HMM_BITCOIND);
}
fprintf(stdout, "%s", strUsage.c_str());
return true;
}
从注释,我们可以看出,这是在读取数据目录以前,先处理版本信息和帮助文档
如果if语句成立,则执行if中的语句,执行完成后,返回true,程序结束,如果不执行if语句,则执行if后面的语句。
其中if语句中的函数IsArgSet是昨天讲的类ArgsManager中的函数,定义在util.h,实现在util.cpp.我们看看他的实现函数
bool ArgsManager::IsArgSet(const std::string& strArg)
const
{
LOCK(cs_args);//所存这个变量,保证该线程调用时,其他线程不会调用
return mapArgs.count(strArg);
}
这个函数中我们先看看变量mapArgs,定义为
std::map mapArgs;
在标准库std中,map是一类关联式容器。map的key-value用于1对1的映射情况
count函数,查找map中,是否有这个value,如果有则返回true,如果没有则返回false.
因此
IsArgSet(const std::string& strArg)
就是检查mapArgs中是否有strArg,如果有,则返回true,如果没有,则返回false.
昨天看到的
mapArgs.clear();
clear是清空mapArgs中所有的元素
再返回去,看if语句
if (gArgs.IsArgSet("-?") ||gArgs.IsArgSet("-h") ||gArgs.IsArgSet("-help")|| gArgs.IsArgSet("-version"))
即查看gArgs中是否有“-?”,"-h","-help","-version",前3个是帮助标识,后一个是版本标识,如果检测到其中一个,则进入if语句,开始执行,执行完成后,返回true。否则不进入if'语句,执行下面的语句。
1) 版本信息
第一句是
std::string strUsage =strprintf(_("%s Daemon"), (PACKAGE_NAME)) + " " +("version") + " " + FormatFullVersion() + "\n";
读取版本信息,在菜菜子的文章中,对这句代码有详细说明。
/********************以下是菜菜子的分析****************************/
strprintf函数为字符串格式化命令,主要功能是把格式化的数据写入某个字符串中。此处是将PACKAGE_NAME写入_("%s Daemon")中,生成PACKAGE_NAME
Daemon形式的字符串内容。PACKAGE_NAME的定义位于src/config/bitcoin-config.h中,其定义为:
该文件在我们下载的源码中一开始是不存在的,需经过对源码进行./configure命令后才能生成。源码的./configure过程可参见我的《聊聊比特币(Bitcoin)客户端源码编译那些事》一文。
FormatFullVersion函数的功能是输出比特币核心的完整版本信息。该函数的实现位于src/clientversion.cpp中,其实现代码如下:
std::string FormatFullVersion()
{
return CLIENT_BUILD;
}
函数中直接调用了CLIENT_BUILD函数,该函数的定义也在当前文件
const std::stringCLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
再来看BUILD_DESC,其定义就在当前文件:
#ifndef BUILD_DESC
#ifdef BUILD_SUFFIX
#defineBUILD_DESCBUILD_DESC_WITH_SUFFIX(CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD,BUILD_SUFFIX)
#elif defined(GIT_COMMIT_ID)
#defineBUILD_DESCBUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD,GIT_COMMIT_ID)
#define BUILD_DESCBUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,
CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD)
#endif
#endif
通过分析#ifndef BUILD_DESC,我们可以判断BUILD_DESC将在BUILD_DESC_FROM_UNKNOWN函数中执行,该函数采用的是预编译实现方式,这样的好处是对于小型、通用性函数采用预编译方式可以提高程序的执行效率。
#defineBUILD_DESC_FROM_UNKNOWN(maj, min,rev, build) \
"v" DO_STRINGIZE(maj)"." DO_STRINGIZE(min)"." DO_STRINGIZE(rev) "."DO_STRINGIZE(build)"-unk"
在函数实现中调用了DO_STRINGIZE函数,该函数的实现位于src/clientversion.h中
/**
Converts theparameter X to astring after macro replacement on X has been performed.
Don't mergethese into onemacro!
*/
#define STRINGIZE(X)DO_STRINGIZE(X)
#define DO_STRINGIZE(X) #X
通过其注释,我们可以知道该函数的作用是将宏定义的参数X转变为字符串。那问题来了,BUILD_DESC_FROM_UNKNOWN函数中调用的4次DO_STRINGIZE函数包含的变量是宏定义变量吗?答案肯定是的,不然程序就出错了。那包含的maj, min, rev, build在哪定义呢?那我们就需要看BUILD_DESC_FROM_UNKNOWN调用位置传入的四个变量:
CLIENT_VERSION_MAJOR
CLIENT_VERSION_MINOR
CLIENT_VERSION_REVISION
CLIENT_VERSION_BUILD
他们的定义位于src/clientversion.h中,通过定义我们可知其为宏定义,因此传入DO_STRINGIZE函数中是没问题的。
#define CLIENT_VERSION_MAJOR 0
#define CLIENT_VERSION_MINOR 14
#define CLIENT_VERSION_REVISION 2
#define CLIENT_VERSION_BUILD 0
再来看BUILD_DESC_FROM_UNKNOWN的实现,其功能是将版本信息的主要、次要、修正以及建立4个值进行拼接,从而输出完整的版本号信息。
/*****************************************以上为菜菜子分析*********************/
2)版本许可
下面开始判断,如果检测到的是版本信息
if (gArgs.IsArgSet("-version"))
{
strUsage +=FormatParagraph(LicenseInfo());
}
代码的意思是将LicenseInfo()返回的字符串拼接到strUsage上。
其中:FormatParagraph()函数
/**
Format aparagraph of text to a fixed width, adding spaces for
indentation toany added line.
*/
std::string FormatParagraph(const std::string& in,size_t width = 79, size_t indent = 0);
从注释可以看出,该函数主要是通过添加空格的方式把一段文本变成固定宽度。
函数处理的内容是LicenseInfo()而,这个函数包含着版权许可信息。
因此,if语句的功能就是输出版本许可信息,下面我们用ubuntu进行测试。在终端中输入命令"bitcoin -version",
3)帮助信息
else
{
strUsage += "\n" +_("Usage:") + "\n" +
"bitcoind [options]" +strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
strUsage += "\n" +HelpMessage(HMM_BITCOIND);
}
其中HelpMessage函数的注释如下
/**
Format a stringto be used as group of options in help messages
@param messageGroup name (e.g. "RPC server options:")
@return theformatted string
*/
std::string HelpMessageGroup(const std::string&message);
用于显示帮助信息。
HMM_BITCOIND定义如下
/** The help message mode determines what help message toshow */
enum HelpMessageMode {
HMM_BITCOIND,//比特币后台进程帮助信息
HMM_BITCOIN_QT//比特币前台进程帮助信息
};
此处我们研读的代码为后台进程bitcoind程序,所以参数为HMM_BITCOIND。在HelpMessage函数中,将会根据具体的类型输出相应的帮助信息内容,其帮助内容主要为后台进程涉及参数的使用方法说明。所以,大家后续在使用后台进程时,如果遇到不会的命令,可以通过“bitcoind -?”、"bitcoind -h"或" bitcoind -help"得到帮助信息。
下面在ubuntu中测试一下,输入命令“bitcoind -?”输出的信息,与HelpMessage函数中的一致
最后程序通过fprintf(stdout, "%s",strUsage.c_str());实现版本或帮助信息的输出
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
区块链研习社比特币源码研读班 electroman
以下是广告:
我们区块链研习社已创建“区块链研习社币圈交流”小密圈”,在小密圈中,我们将带领大家一起学习区块链的原理与投资,还将提供区块链基本原理解答、交易所注册与交易操作、ICO交易与操作、投资分析、风险分析等内容。