走进开源代码(三)
由于工作的原因,虽然是一名C++程序员,平时工作中还是使用的C++99,而比特币v0.20.1的源码是C++11写的,虽然之前对C++11也有些了解,毕竟语言这东西不用就会忘,更何况只是了解,所以在看的时候遇到C++11的代码会花些时间重新学习,其实虽然v0.20.1版本比较新,但很多代码与两年前的是一样的,尤其是我看的bitcoind的初始化,参数设置相关的代码,其实在CSDN已经有很多大神写过
由于工作的原因,虽然是一名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
}
...
这样在有界面和无界面两种情况下,能够较好的实现逻辑相同但内容不同的功能,而且还具有很好的扩展性——通过将信号关联不同的槽,达到统一的逻辑处理,个性化的实现。
扩展阅读:
![Logo](https://devpress.csdnimg.cn/79de2bf0b7994defa4242ef90d5513fa.jpg)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)