走进开源代码(三)

       由于工作的原因,虽然是一名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库的具体用法可以参考官网的tutorialboost::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程序库完全开发指南)读书笔记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值