走进开源代码(三)

2023-05-16

       由于工作的原因,虽然是一名C++程序员,平时工作中还是使用的C++99,而比特币v0.20.1的源码是C++11写的,虽然之前对C++11也有些了解,毕竟语言这东西不用就会忘,更何况只是了解,所以在看的时候遇到C++11的代码会花些时间重新学习,其实虽然v0.20.1版本比较新,但很多代码与两年前的是一样的,尤其是我看的bitcoind的初始化,参数设置相关的代码,其实在CSDN已经有很多大神写过比特币源码的学习笔记了,比如aabbc59的区块链专栏,朝歌1122的bitcoin专栏,下面言归正传开始源码之旅。

       首先找到BitCoind.cpp的main函数:

int main(int argc, char* argv[])
{
#ifdef WIN32
    util::WinCmdLineArgs winArgs;
    std::tie(argc, argv) = winArgs.get();
#endif
    SetupEnvironment();

    // Connect bitcoind signal handlers
    noui_connect();

    return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}

     在SetupEnvironment函数里用到了boost::filesystem库,这里主要是设置文件路径的字符集(我的理解,如果有错误请指正)。

// The path locale is lazy initialized and to avoid deinitialization errors
    // in multithreading environments, it is set explicitly by the main thread.
    // A dummy locale is used to extract the internal default locale, used by
    // fs::path, which is then used to explicitly imbue the path.
    std::locale loc = fs::path::imbue(std::locale::classic());
#ifndef WIN32
    fs::path::imbue(loc);
#else
    fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
#endif

       接着是noui_connect函数,这里用到了boost的signals2库,该库实现了一个管理信号和槽的系统(The Boost.Signals2 library is an implementation of a managed signals and slots system.),是原始Boost.Signals库的线程安全版本。信号代表多个目标的回调,也被称为发布者(publisher)或者事件(event),信号连接到一组槽,这些槽是回调接收器(也称为事件目标或订阅者),在“发射”信号时调用它们。( Signals represent callbacks with multiple targets, and are also called publishers or events in similar systems. Signals are connected to some set of slots, which are callback receivers (also called event targets or subscribers), which are called when the signal is "emitted.")

       上面一段摘自boost库的在线官方文档,了解Qt的人小伙伴肯定对信号&槽(signal/slot)机制不会陌生,只要定义了一个对象信号与(一个或多个)对象的(一个或多个)槽的关联,当该对象发送信号,关联对象的槽就会执行。我想这里的signals2的效果应该是一样。

       代码中先是定义了全局的3个信号与槽的连接connection,再再noui_connect中给三个连接赋值,这里为了避免重复代码,在.h和.cpp中分别使用宏(ADD_SIGNALS_DECL_WRAPPER和ADD_SIGNALS_IMPL_WRAPPER)定义了关联信号和槽的函数,执行uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox) 就相当于 return g_ui_signals.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);

       这里的signal模板的第二个参数是合并器(combiner),用来处理槽的返回值,把多个插槽的返回值合并为一个结果返回给用户。这里的ThreadSafeMessageBox和ThreadSafeQuestion信号都只返回最后一个槽执行的返回值。关于boost的signals2库的具体用法可以参考官网的tutorial和boost::signals2::last_value的源码。

//noui.cpp
/** Store connections so we can disconnect them when suppressing output */
boost::signals2::connection noui_ThreadSafeMessageBoxConn;
boost::signals2::connection noui_ThreadSafeQuestionConn;
boost::signals2::connection noui_InitMessageConn;

...

void noui_connect()
{
    noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
    noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
    noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage);
}

...

//ui_interface.h
class CClientUIInterface
{
public:

...

#define ADD_SIGNALS_DECL_WRAPPER(signal_name, rtype, ...)                                  \
    rtype signal_name(__VA_ARGS__);                                                        \
    using signal_name##Sig = rtype(__VA_ARGS__);                                           \
    boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn);

/** Show message box. */
    ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const std::string& message, const std::string& caption, unsigned int style);

...
};

//ui_interface.cpp
CClientUIInterface uiInterface;

struct UISignals {
    boost::signals2::signal<CClientUIInterface::ThreadSafeMessageBoxSig, boost::signals2::last_value<bool>> ThreadSafeMessageBox;
    boost::signals2::signal<CClientUIInterface::ThreadSafeQuestionSig, boost::signals2::last_value<bool>> ThreadSafeQuestion;
    boost::signals2::signal<CClientUIInterface::InitMessageSig> InitMessage;
    boost::signals2::signal<CClientUIInterface::NotifyNumConnectionsChangedSig> NotifyNumConnectionsChanged;
    boost::signals2::signal<CClientUIInterface::NotifyNetworkActiveChangedSig> NotifyNetworkActiveChanged;
    boost::signals2::signal<CClientUIInterface::NotifyAlertChangedSig> NotifyAlertChanged;
    boost::signals2::signal<CClientUIInterface::ShowProgressSig> ShowProgress;
    boost::signals2::signal<CClientUIInterface::NotifyBlockTipSig> NotifyBlockTip;
    boost::signals2::signal<CClientUIInterface::NotifyHeaderTipSig> NotifyHeaderTip;
    boost::signals2::signal<CClientUIInterface::BannedListChangedSig> BannedListChanged;
};
static UISignals g_ui_signals;

#define ADD_SIGNALS_IMPL_WRAPPER(signal_name)                                                                 \
    boost::signals2::connection CClientUIInterface::signal_name##_connect(std::function<signal_name##Sig> fn) \
    {                                                                                                         \
        return g_ui_signals.signal_name.connect(fn);                                                          \
    }

ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeMessageBox);
ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeQuestion);

          Ui_interface.h中的宏定义相当于下面三句:

bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style);

using ThreadSafeMessageBoxSig = bool(const std::string& message, const std::string& caption, unsigned int style);

boost::signals2::connection ThreadSafeMessageBox_connect(std::funtion<ThreadSafeMessageBoxSig> fn);

         CClientUIInterface类所有10个的"signal_name"函数所做的事情就是发送对应的信号。

//ui_interface.cpp
bool CClientUIInterface::ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style); }

...

         在有界面的情况下建立信号与槽的关联是在interfaces/node.cpp中实现的。

//node.h
...

//! Register handler for init messages.
    using InitMessageFn = std::function<void(const std::string& message)>;
    virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0;

    //! Register handler for message box messages.
    using MessageBoxFn =
        std::function<bool(const std::string& message, const std::string& caption, unsigned int style)>;
    virtual std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) = 0;

...

//node.cpp
namespace interfaces {

namespace {

class NodeImpl : public Node
{
public:

...

    std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
    {
        return MakeHandler(::uiInterface.InitMessage_connect(fn));
    }
    std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override
    {
        return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn));
    }
    std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override
    {
        return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn));
    }
    std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
    {
        return MakeHandler(::uiInterface.ShowProgress_connect(fn));
    }

...

};

} // namespace

std::unique_ptr<Node> MakeNode() { return MakeUnique<NodeImpl>(); }

} // namespace interfaces

        而真正的调用是在qt/Bitcoingui.cpp,qt/clientmodel.cpp和qt/splashscreen.cpp

//bitcoingui.cpp
...

void BitcoinGUI::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_message_box = m_node.handleMessageBox(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    m_handler_question = m_node.handleQuestion(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_3, std::placeholders::_4));
}

...

//clientmodel.cpp
...

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
    m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1));
    ...
}

...

//splashscreen.cpp
...

void SplashScreen::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
    m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
#ifdef ENABLE_WALLET
    m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
#endif
}

...

        这样在有界面和无界面两种情况下,能够较好的实现逻辑相同但内容不同的功能,而且还具有很好的扩展性——通过将信号关联不同的槽,达到统一的逻辑处理,个性化的实现。

扩展阅读:

boost------signals2的使用1(Boost程序库完全开发指南)读书笔记

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

走进开源代码(三) 的相关文章

  • 虚拟机中的Linux系统无法识别U盘

    问题描述 xff1a 将U盘插入到电脑USB接口 xff0c 然后在虚拟机的右下角选择让U盘从Windows上断开 xff0c 链接到虚拟机上来 链接上虚拟机后 xff0c 在Linux系统中输入命令fdisk l命令 xff0c 却只有
  • C语言丨关键字enum用法详解,看这篇就够了

    一 关键字enum的定义 enum是C语言中的一个关键字 xff0c enum叫枚举数据类型 xff0c 枚举数据类型描述的是一组整型值的集合 xff08 这句话其实不太妥当 xff09 xff0c 枚举型是预处理指令 define的替代
  • CCF CSP 201512-3 画图

    字符串基础题 问题描述 用 ASCII 字符来画图是一件有趣的事情 xff0c 并形成了一门被称为 ASCII Art 的艺术 例如 xff0c 下图是用 ASCII 字符画出来的 CSPRO 字样 lt 本题要求编程实现一个用 ASCII
  • fails sanity check错误的解决方法

    fails sanity check的解决方法 问题原因 xff1a 编译器缺乏必要的package xff1a 解决办法 xff1a 运行yum install glibc headers gcc c 43 43 即可解决
  • Rust tokio::select学习杂记

    Rust tokio select学习杂记 前言 Linux系统有select poll epoll等 xff0c 主要用于监控各种fd上发生的各种event 从而识别派发处理 golang语言中也有一个select xff0c 作用相似
  • IntelliJ IDEA2020【插件推荐】

    1 推荐动画效果插件 xff1a activate power mode 注 xff1a 需要消耗一定的系统资源 第一步 xff1a 在插件中心在线安装activate power mode插件 xff0c 安装好之后 xff0c 重启ID
  • k8s: 使node不参与调度

    可以通过命令 xff1a kubectl patch node k8s span class token operator span master span class token operator span p 39 span class
  • Linux7查看默认jdk安装目录(默认只有jre环境,无jdk开发环境。附:安装jdk并配置JAVA_HOME)

    1 检测默认jre运行环境 xff08 1 xff09 查看linux7默认运行环境jre span class token comment cd etc alternatives span span class token comment
  • no module的几种解决办法

    1 将其文件夹右击找到设置为marked as sourse root 2 采用sys append 3 在from xxx前面加入yyy xxx 4 python同目录下模块的导入失败问题 日常敲代码间歇旅行的程序媛的博客 CSDN博客
  • 如何获取C币

    C币获取 完善信息获得C币 现在去完善 行为获得数量说明完善个人资料 5完善个人资料 xff08 姓名 职业背景等 xff09 获得5个C币 手机验证获得C币 现在去绑定 行为获得数量说明绑定手机 5首次绑定手机可获得5个C币 撰写博文获得
  • shell:输出数组中大于5的数

    输出数组中大于5的数 xff0c 需要注意if a ge b 中的空格 bin bash array 61 1 2 3 5 64 7 8 9 21 length 61 array 64 for a 61 0 a lt length a 43
  • shell 特殊符号大全

    注释符号 Hashmark Comments 1 在shell文件的行首 xff0c 作为shebang标记 xff0c bin bash 2 其他地方作为注释使用 xff0c 在一行中 xff0c 后面的内容并不会被执行 xff0c 除非
  • shell:查看一个文件是否存在文件夹中

    bin bash check a file is exist or no echo 34 please enter fileName 34 read fileName if test e 34 fileName 34 then echo 3
  • 在WSL中启动Ubuntu 20.04时出现错误[出现错误 2147942402 (0x80070002) (启动“ubuntu2004.exe”时)]

    1 之前好好的WSL xff0c 今天在我装了git xff0c go xff0c 并配置了环境变量后 xff0c 通过终端运行时突然报错 xff0c 错误为 xff1a 出现错误 2147942402 0x80070002 启动 ubun
  • MySQL(七)—— 分组查询

    MySQL数据库系列内容的学习目录 rightarrow 老杜带你学MySQL学习系列内容汇总 7 分组查询7 1 group by7 2 having7 3 select 语句总结 7 分组查询 分组查询主要涉及到两个子句 xff0c 分
  • Python之pytesseract模块-实现OCR

    欢迎关注原创视频教程 Python微信订餐小程序课程视频 https edu csdn net course detail 36074 Python实战量化交易理财系统 https edu csdn net course detail 35
  • QT创建项目 编译提示 无法运行“rc.exe”

    作者 xff1a 虚坏叔叔 博客 xff1a https xuhss com 沉淀 分享 成长 xff0c 让自己和他人都能有所收获 xff01 x1f604 一 QT创建项目 编译提示 无法运行 rc exe 网上搜索了挺多 xff0c

随机推荐