C++對象間通信組件,讓C++對象“無障礙交流”

来源:https://www.cnblogs.com/xdblog/archive/2022/06/19/easymsgcpp.html
-Advertisement-
Play Games

介紹 這是很久之前的一個項目了,最近剛好有些時間,就來總結一下吧! 推薦初步熟悉項目後閱讀本文: https://gitee.com/smalldyy/easy-msg-cpp 從何而來 這要從我從事Qt開發的那些日子說起了,項目說大不大,說小也不小,人倒是一茬又一茬,需求也換了又換,後來的事情大家 ...


介紹

這是很久之前的一個項目了,最近剛好有些時間,就來總結一下吧!
推薦初步熟悉項目後閱讀本文: https://gitee.com/smalldyy/easy-msg-cpp

從何而來

這要從我從事Qt開發的那些日子說起了,項目說大不大,說小也不小,人倒是一茬又一茬,需求也換了又換,後來的事情大家都懂了,項目變成了一坨濃Shit,且不說其中的設計、構架、以及需求問題,單說說我對這個項目的直觀感受,在我看來,整個程式仿佛一顆大樹,從某點作為根然後一直向上延伸,在沒有足夠時間重構的情況下,它的層級越來越深,這時候問題來了,如果想讓樹木的兩個不同分支的葉子節點發生關係,事情就馬上會變得十分痛苦!

這兩個想要聯繫的對象根本不再一個地方,我可能要將其中一個對象的指針在這顆大樹的節點上倒退3層然後再前進2層才能讓他們見面,然後暗戳戳的寫下一個connect。

這時候我就想,如果有一個專門的通信組件負責傳遞各種消息,讓兩個對象中間產生一個媒介作為他們通信的橋梁,獲取這件事情就會變得更加輕鬆了,我不用再費盡心思的將兩個對象引用到同一個作用域,甚至還要考慮哪個作用域更加合理。

誠然,如果在前期就對項目的各個組件進行全盤規劃,我想這種困境可能不會或者很少出現,但是並非所有事情都會按照美好的方向前進,就如曾經堆在我面前的那坨濃Shit,儘管我也為它的存在出過不少力…………

設計目標

  • 提供C++對象進程內通信功能 可進行消息傳遞;
  • 將已經存在的結構體定義為消息時,不能破壞已經存在的結構體本身的結構;
  • 處理消息的類無需繼承任何基類;
  • 足夠簡單的訂閱方法;
  • RAII形式的取消訂閱,但也支持手動取消訂閱。

你可能註意到了,我特意強調了不破壞原有結構。目的很簡單,就是為了保證項目不會因為引入這個組件而發生太大的變化。眾所周知,大部分程式員都是懶癌晚期,如果引入一個組件會導致工作量激增,程式員就會開始衡量shit的臭味和工作量之間的關係了。

總之,核心特征只有兩個:易用,改動小。

原理分析

我首先將這個組件設計為一個基於訂閱分發方式的通信組件,它有三個主要角色,訂閱者,發佈者,和消息。

首先考慮最簡單的發佈者,發佈者的功能非常直觀——發送消息,也就是說用戶只要在需要的位置調用一個sendMsg之類的函數即可,這個函數的功能就是將用戶給定的消息發送出去。

然後便是訂閱者,我們要求訂閱的宿主類型不可以繼承任何基類,這個要求決定了我們訂閱的方式,我們需要提供一個函數,它接受一個對象的指針(我稱之為宿主)和它的成員函數,將兩者包裝成一個std::function,將這個包裝好的回調函數與一個定義好的消息關聯並記錄下來,這就形成了訂閱關係。

當發佈者發送消息時,我們的組件需要查詢訂閱關係,找到消息對應的回調函數,將消息作為參數調用它!此時,對象間就完成了一次通信。我們的組件就是信使,這樣就無需發信人四處奔波了。

我們還要求不破壞原本的結構體的結構,這也就意味著我們不能改動已經存在的結構體,比如果讓它繼承一個消息基類然後就能作為消息傳遞之類的操作——雖然很好,但是我們得對這個設計說拜拜了。但是,上樹訂閱分發的流程必然要求消息擁有一個統一的基類類型,否則我們無法統一回調函數的函數簽名,存儲訂閱關係也就無從談起了!因為參數類型不同的函數,是很難存儲到一個容器中以供查詢的!

為瞭解決這個鬧人的問題,我們不妨反向思考一下,既然我們不能讓一個已經存在的消息結構繼承我們的基類,那麼就創建一個新的類型同時繼承兩者吧!

 class NewExistMsg : public ExistMsg, public em::EasyMsg

用戶可以使用 NewExistMsg 來創建消息體,就像使用 ExistMsg一樣,回調函數可以使用EasyMsg*作為參數,來達到類型的統一,並可以安全的進行多態設計。

至此,消息的問題也解決了。

你可能會感興趣的技術細節

以下是EasyMsg的頭文件:

class EASYMSG_API EasyMsg {
public:
  EasyMsg();
  virtual ~EasyMsg() = default;
  virtual std::string id() const = 0;

  template <typename T> struct is_easymsg {
    template <typename U> static char test(typename U::MsgType *x);
    template <typename U> static long test(U *x);
    static const bool value = sizeof(test<T>(0)) == 1;
  };

  // c++17 support constexpr if
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
  template <typename EASY_MSG_ID> bool match() {
    is_easymsg<EASY_MSG_ID> test_easymsg;
    if constexpr (test_easymsg.value) { // c++17
      return id() == EASY_MSG_ID::value;
    } else {
      std::cerr << "匹配消息ID時發生錯誤,檢查是否使用了未定義的消息? 檢查:"
                << typeid(EASY_MSG_ID).name() << std::endl;
      return false;
    }
  }
#else
  template <class MSGID>
  typename std::enable_if<!is_easymsg<MSGID>::value, bool>::type match() {
    std::cerr
        << "匹配消息ID時發生錯誤,檢查是否使用了未定義的消息? typeinfo : "
        << typeid(MSGID).name() << std::endl;
    return false;
  }

  template <class MSGID>
  typename std::enable_if<is_easymsg<MSGID>::value, bool>::type match() {
    return id() == MSGID::value;
  }

#endif
};

這裡邊有一些有意思的東西可以學習一下,首先映入眼帘的就像是經典的虛析構函數,這是作為多態基類的必要手續。接下來就是SFINAE的經典用法,我是用這個技巧實現了match函數,這個函數的主要作用就是判斷給定的EASY_MSG_ID是否和傳入的消息指針是同一種消息類型。

match根據c++標準分成了兩個實現,C++17版本藉助了 constexpr if特性。以前的版本則用了經典的std::enable_if。

SFINAE不甚瞭解的人應該很難理解這些代碼,SFINAE中文含義為“匹配失敗不是錯誤”,這對模板變成來說非常重要,不過這已經超出了本文範圍,我僅僅是拋磚引玉,之後我可能會更新文章對此段代碼進行詳解,從而讓大家瞭解這些慣用法。

其他的便沒有什麼技術細節了,都是些常規的東西,無非是用map記錄下訂閱關係,然後send時執行回調之類的東西,不值細說。

結論

本文向大家介紹了一個侵入性較低的C++對象間通信組件,或許可以幫助你解決一些頭疼的通信問題,並展示了一些你可能感興趣的技術細節,如果能引發更多的思考那就更好不過了!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 認識WEB **「網頁」**主要是由文字、圖像和超鏈接等元素構成,當然除了這些元素,網頁中還可以包括音頻、視頻以及Flash等。 **「瀏覽器」**是網頁顯示、運行的平臺。 「瀏覽器內核」(排版引擎、解釋引擎、渲染引擎) 常見的瀏覽器及其內核 瀏覽器 內核 備註 IE Trident IE、獵豹安全 ...
  • HTML各知識點總結: 基本標簽 標題標簽、段落標簽、換行標簽、水平線標簽、字體樣式標簽、註釋和特殊符號 網頁插入 圖像、超鏈接,視頻、音頻、列表、表格、表單、內聯框架等 超鏈接 錨鏈接、功能性鏈接 列表 有序列表、無序列表、自定義列表 表格 行、列、跨行、跨列 表單 提交格式、文本框、密碼框、單選 ...
  • 昨天太晚就沒來得及更新,今天是spu管理界面,這個界面一共有三個界面需要切換,完成了兩個界面,而且今天的難度在於最後兩個章節,富有一定的邏輯性,當然中間也有很多需要註意的,比如ElementUI的照片牆需要添加list屬性而且值為你的數據並且必須是一個數組必須有name、url屬性 一.spu管理 ...
  • 本章是系列文章的第七章,終於來到了鼎鼎大名的SSA,SSA是編譯器領域最偉大的發明之一,也是影響最廣的發明。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 7.1 控制流圖回顧 對下麵的c代碼保存成7.1.cc: 1 int max(int ...
  • 樣才能拿到大廠的offer,沒有掌握絕對的技術,那麼就要不斷的學習 他是如何拿下阿裡等大廠的offer的呢,今天分享他的秘密武器,美團資深架構師整理的Java核心知識點,面試時面試官必問的知識點,篇章包括了很多知識點,其中包括了有基礎知識、Java集合、JVM、多線程併發、spring原理、微服務、 ...
  • 引言:今天看MicrosoftDoc關於CFileDialog的doModal函數返回值的部分,提到了實際上MFC提供了錯誤信息顯示。 個人技術博客(文章整理+源碼): https://zobolblog.github.io/LearnWinAPI/ 1.用法: CFileDialog::DoMod ...
  • 一、MybatisSpring的使用 1.創建 Maven 工程。 2.添加依賴,代碼如下 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7-ybe</version ...
  • C++ 標準庫提供了原子操作。(我已經懶得寫序言了) 先來說原子操作的概念: 原子操作是多線程當中對資源進行保護的一種手段,主要作用是和互斥量(Mutex)一樣,避免對資源的併發訪問、修改。 互斥量的粒度衡量是作用域(哪怕作用域內只有一個變數),而原子的粒度衡量則是以一個變數或對象為單位。因此,原子 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...