C++ Boost.Signals2信號/槽概念
一、關(guān)于Boost.Signals2
Boost.Signals2 實現(xiàn)了信號/槽的概念。一個或多個函數(shù)(稱為槽)與可以發(fā)出信號的對象相關(guān)聯(lián)。每次發(fā)出信號時,都會調(diào)用鏈接的函數(shù)。
信號/槽概念在開發(fā)具有圖形用戶界面的應用程序時非常有用??梢詫Π粹o進行建模,以便在用戶單擊它們時發(fā)出信號。它們可以支持指向許多函數(shù)的鏈接以處理用戶輸入。這樣就可以靈活地處理事件。
std::function 也可用于事件處理。 std::function 和 Boost.Signals2 之間的一個重要區(qū)別是 Boost.Signals2 可以將多個事件處理程序與單個事件相關(guān)聯(lián)。因此,Boost.Signals2更適合支持事件驅(qū)動開發(fā),應該是任何需要處理事件的首選。
Boost.Signals2 繼承了庫 Boost.Signals,后者已被棄用且本書未討論。
Table of Contents
二、關(guān)于Signals庫
Boost.Signals2 提供類 boost::signals2::signal,可用于創(chuàng)建信號。此類在 boost/signals2/signal.hpp 中定義。或者,您可以使用頭文件 boost/signals2.hpp,這是一個主頭文件,定義了 Boost.Signals2 中可用的所有類和函數(shù)。
Boost.Signals2 定義了 boost::signals2::signal 和其他類,以及命名空間 boost::signals2 中的所有函數(shù)。
示例 67.1。 “你好世界!”使用 boost::signals2::signal
#include <boost/signals2.hpp> #include <iostream> using namespace boost::signals2; int main() { signal<void()> s; s.connect([]{ std::cout << "Hello, world!\n"; }); s(); }
boost::signals2::signal 是一個類模板,它期望將用作事件處理程序的函數(shù)的簽名作為模板參數(shù)。在示例 67.1 中,只有簽名為 void() 的函數(shù)才能與信號相關(guān)聯(lián)。
lambda 函數(shù)通過 connect() 與信號 s 相關(guān)聯(lián)。因為 lambda 函數(shù)符合所需的簽名 void(),所以關(guān)聯(lián)已成功建立。每當觸發(fā)信號 s 時,都會調(diào)用 lambda 函數(shù)。
信號是通過像調(diào)用常規(guī)函數(shù)一樣調(diào)用 s 來觸發(fā)的。此函數(shù)的簽名與作為模板參數(shù)傳遞的簽名匹配。括號是空的,因為 void() 不需要任何參數(shù)。調(diào)用 s 會產(chǎn)生一個觸發(fā)器,而該觸發(fā)器又會執(zhí)行之前與 connect() 相關(guān)聯(lián)的 lambda 函數(shù)。
示例 67.1 也可以使用 std::function 實現(xiàn),如示例 67.2 所示。
示例 67.2。 “你好世界!”使用 std::function
#include <functional> #include <iostream> int main() { std::function<void()> f; f = []{ std::cout << "Hello, world!\n"; }; f(); }
在示例 67.2 中,調(diào)用 f 時也會執(zhí)行 lambda 函數(shù)。雖然 std::function 只能用于類似示例 67.2 的場景,但 Boost.Signals2 提供了更多種類。例如,它可以將多個函數(shù)與特定信號相關(guān)聯(lián)(參見示例 67.3)。
示例 67.3。帶有 boost::signals2::signal 的多個事件處理程序
#include <boost/signals2.hpp> #include <iostream> using namespace boost::signals2; int main() { signal<void()> s; s.connect([]{ std::cout << "Hello"; }); s.connect([]{ std::cout << ", world!\n"; }); s(); }
boost::signals2::signal 允許您通過重復調(diào)用 connect() 將多個函數(shù)分配給特定信號。每當觸發(fā)信號時,函數(shù)都會按照它們與 connect() 關(guān)聯(lián)的順序執(zhí)行。
順序也可以在 connect() 的重載版本的幫助下明確定義,它需要一個 int 類型的值作為附加參數(shù)(示例 67.4)。
示例 67.4。具有明確順序的事件處理程序
#include <boost/signals2.hpp> #include <iostream> using namespace boost::signals2; int main() { signal<void()> s; s.connect(1, []{ std::cout << ", world!\n"; }); s.connect(0, []{ std::cout << "Hello"; }); s(); }
與前面的示例一樣,示例 67.4 顯示 Hello, world!。
要從信號中釋放關(guān)聯(lián)函數(shù),請調(diào)用 disconnect()。
示例 67.5。斷開事件處理程序與 boost::signals2::signal 的連接
#include <boost/signals2.hpp> #include <iostream> using namespace boost::signals2; void hello() { std::cout << "Hello"; } void world() { std::cout << ", world!\n"; } int main() { signal<void()> s; s.connect(hello); s.connect(world); s.disconnect(world); s(); }
示例 67.5 僅打印 Hello,因為與 world() 的關(guān)聯(lián)在信號被觸發(fā)之前已釋放。
除了 connect() 和 disconnect() 之外,boost::signals2::signal 還提供了幾個成員函數(shù)(參見示例 67.6)。
示例 67.6。 boost::signals2::signal 的附加成員函數(shù)
#include <boost/signals2.hpp> #include <iostream> using namespace boost::signals2; int main() { signal<void()> s; s.connect([]{ std::cout << "Hello"; }); s.connect([]{ std::cout << ", world!"; }); std::cout << s.num_slots() << '\n'; if (!s.empty()) s(); s.disconnect_all_slots(); }
num_slots() 返回關(guān)聯(lián)函數(shù)的數(shù)量。如果沒有函數(shù)關(guān)聯(lián),num_slots() 返回 0。empty() 告訴您事件處理程序是否已連接。而 disconnect_all_slots() 的作用正如它的名字所說:它釋放所有現(xiàn)有的關(guān)聯(lián)。
示例 67.7。處理事件處理程序的返回值
#include <boost/signals2.hpp> #include <iostream> using namespace boost::signals2; int main() { signal<int()> s; s.connect([]{ return 1; }); s.connect([]{ return 2; }); std::cout << *s() << '\n'; }
在示例 67.7 中,兩個 lambda 函數(shù)與信號關(guān)聯(lián)。第一個 lambda 函數(shù)返回 1,第二個返回 2。
示例 67.7 將 2 寫入標準輸出。 s 正確接受了兩個返回值,但除了最后一個之外的所有返回值都被忽略了。默認情況下,只返回所有關(guān)聯(lián)函數(shù)的最后一個返回值。
請注意,s() 不會直接返回上次調(diào)用的函數(shù)的結(jié)果。返回類型為 boost::optional 的對象,取消引用時返回數(shù)字 2。觸發(fā)與任何函數(shù)無關(guān)的信號不會產(chǎn)生任何返回值。因此,在這種情況下,boost::optional 允許 Boost.Signals2 返回一個空對象。 boost::optional 在第 21 章中介紹。
可以自定義信號,以便相應地處理各個返回值。為此,必須將組合器作為第二個模板參數(shù)傳遞給 boost::signals2::signal。
示例 67.8。使用用戶定義的組合器查找最小返回值
#include <boost/signals2.hpp> #include <vector> #include <algorithm> #include <iostream> using namespace boost::signals2; template <typename T> struct min_element { typedef T result_type; template <typename InputIterator> T operator()(InputIterator first, InputIterator last) const { std::vector<T> v(first, last); return *std::min_element(v.begin(), v.end()); } }; int main() { signal<int(), min_element<int>> s; s.connect([]{ return 1; }); s.connect([]{ return 2; }); std::cout << s() << '\n'; }
組合器是一個具有重載的 operator() 的類。該運算符使用兩個迭代器自動調(diào)用,這兩個迭代器用于訪問與特定信號關(guān)聯(lián)的函數(shù)。當?shù)鞅蝗∠脮r,函數(shù)被調(diào)用并且它們的返回值在組合器中變得可用。然后可以使用標準庫中的常用算法,例如 std::min_element() 來計算并返回最小值(參見示例 67.8)。
boost::signals2::signal 使用 boost::signals2::optional_last_value 作為默認組合器。此組合器返回 boost::optional 類型的對象。用戶可以定義一個具有任何類型返回值的組合器。例如,示例 67.8 中的組合器 min_element 返回作為模板參數(shù)傳遞給 min_element 的類型。
無法將諸如 std::min_element() 之類的算法作為模板參數(shù)直接傳遞給 boost::signals2::signal。 boost::signals2::signal 期望組合器定義一個名為 result_type 的類型,它表示 operator() 返回值的類型。由于這個類型沒有被標準算法定義,所以編譯器會報錯。
請注意,不可能將迭代器 first 和 last 直接傳遞給 std::min_element() ,因為該算法需要前向迭代器,而組合器使用輸入迭代器。這就是為什么在使用 std::min_element() 確定最小值之前使用向量存儲所有返回值的原因。
示例 67.9 修改組合器以將所有返回值存儲在容器中,而不是評估它們。它將所有返回值存儲在一個向量中,然后由 s() 返回該向量。
示例 67.9。使用用戶定義的組合器接收所有返回值
#include <boost/signals2.hpp> #include <vector> #include <algorithm> #include <iostream> using namespace boost::signals2; template <typename T> struct return_all { typedef T result_type; template <typename InputIterator> T operator()(InputIterator first, InputIterator last) const { return T(first, last); } }; int main() { signal<int(), return_all<std::vector<int>>> s; s.connect([]{ return 1; }); s.connect([]{ return 2; }); std::vector<int> v = s(); std::cout << *std::min_element(v.begin(), v.end()) << '\n'; }
練習
創(chuàng)建一個帶有課程按鈕的程序。該類應代表圖形用戶界面中的按鈕。添加成員函數(shù) add_handler() 和 remove_handler() ,它們都希望傳遞一個函數(shù)。如果調(diào)用另一個名為 click() 的成員函數(shù),則應依次調(diào)用已注冊的處理程序。通過注冊一個將消息寫入標準輸出的處理程序來實例化按鈕并測試該類。調(diào)用 click() 來模擬鼠標點擊按鈕。
到此這篇關(guān)于C++ Boost.Signals2信號/槽概念的文章就介紹到這了,更多相關(guān)C++ Boost Signals2內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言實現(xiàn)學生信息管理系統(tǒng)(單鏈表)
這篇文章主要為大家詳細介紹了C語言實現(xiàn)學生信息管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01

C++?OpenCV技術(shù)實戰(zhàn)之身份證離線識別

使用pybind11封裝C++結(jié)構(gòu)體作為參數(shù)的函數(shù)實現(xiàn)步驟