博客專欄

        EEPW首頁 > 博客 > 征程 6|工具鏈 VP 示例中日志打印解讀

        征程 6|工具鏈 VP 示例中日志打印解讀

        發(fā)布人:地平線開發(fā)者 時(shí)間:2025-04-05 來源:工程師 發(fā)布文章

        1.引言

        在上一篇文章【征程 6】VP 簡介與單算子實(shí)操中,介紹了 VP 是什么,并以單算子 rotate 為例,介紹了 VP API 使用方法,其中有一些日志打印的代碼顯得特別高大上

            LOGE_AND_RETURN_IF(src_mat.empty(), HB_UCP_INVALID_ARGUMENT,
                           "Read image {} failed", src_img.c_str());

        作為對 C++不那么熟悉的伙伴,可能會好奇:LOGE_AND_RETURN_IF 到底是怎么寫的呢?有沒有什么門道可以介紹一下?

        由于本人是屬于對 C++不那么熟悉的同學(xué),下面會從我的視角來介紹這個(gè)問題,如果其中有錯(cuò)誤或表述不當(dāng)?shù)牡胤?,歡迎評論指正。

        其實(shí)本文是為了服務(wù)于另外一篇文章:【征程 6】工具鏈 VP 示例為什么能運(yùn)行

        2.log_util.h 代碼解讀

        LOGE_AND_RETURN_IF 是在 log_util.h 定義的一個(gè)宏,下面來具體看下 log_util.h 中的代碼:

        #ifndef VP_CODE_07_ROTATE_INCLUDE_UTIL_LOG_UTIL_H_
        #define VP_CODE_07_ROTATE_INCLUDE_UTIL_LOG_UTIL_H_

        #include <sstream>    //C++中標(biāo)準(zhǔn)頭文件,不做解釋
        #include <utility>    //C++中標(biāo)準(zhǔn)頭文件,不做解釋

        #include "hlog/logging.h"    // 地平線提供的頭文件,并不在當(dāng)前目錄下,為什么能包含?

        #define LOGI(err_msg, ...) HFLOGM_I("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)
        #define LOGD(err_msg, ...) HFLOGM_D("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)
        #define LOGE(err_msg, ...) HFLOGM_E("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)
        #define LOGW(err_msg, ...) HFLOGM_W("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)

        #define LOGE_AND_RETURN_IF(condition, code, err_msg, ...) \
         do {                                                    \
           if (condition) {                                      \
             LOGE(err_msg, ##__VA_ARGS__);                       \
             return code;                                        \
           }                                                     \
         } while (0)

        #define LOGE_AND_RETURN_IF_NULL(ptr, code) \
         LOGE_AND_RETURN_IF(ptr == nullptr, code, NULLPTR_ERR(ptr))

        #endif  // VP_CODE_07_ROTATE_INCLUDE_UTIL_LOG_UTIL_H_

        2.1 LOGx

        來看一下日志宏定義

        #define LOGI(err_msg, ...) HFLOGM_I("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)
        #define LOGD(err_msg, ...) HFLOGM_D("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)
        #define LOGE(err_msg, ...) HFLOGM_E("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)
        #define LOGW(err_msg, ...) HFLOGM_W("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)

        這些宏封裝了 日志打印函數(shù):

        LOGI(Info):信息日志

        LOGD(Debug):調(diào)試日志

        LOGE(Error):錯(cuò)誤日志

        LOGW(Warning):警告日志

        具體解釋下其中一行:

        #define LOGI(err_msg, ...) HFLOGM_I("VP_TRANSFORMATION", err_msg, ##__VA_ARGS__)

        LOGI(err_msg, …) 定義了一個(gè)宏:

        err_msg 是第一個(gè)參數(shù)

        … 表示可以接受變長參數(shù)。但… 僅是一個(gè) 占位符,它不能直接用于宏的展開。(在下面介紹個(gè)展開的例子)

        HFLOGM_I(“VP_TRANSFORMATION”, err_msg, ##VA_ARGS):

        HFLOGM_I 是 hlog/logging.h 中提供的 具體日志實(shí)現(xiàn)

        “VP_TRANSFORMATION” 是 日志模塊名稱,用于分類日志來源。

        err_msg 就是前面的 LOGI 中的 err_msg

        VA_ARGS 是一個(gè) 占位符的替代符,用于 填充 … 傳遞的實(shí)際參數(shù)。

        ##VA_ARGS 的作用是 在 VA_ARGS 為空時(shí)去掉前面的逗號,防止編譯錯(cuò)誤。

        當(dāng)調(diào)用:LOGI(“Error code: %d”, 404);

        它會展開為:HFLOGM_I(“VP_TRANSFORMATION”, “Error code: %d”, 404);

        如果調(diào)用:LOGI(“Simple message”);

        它會展開為:HFLOGM_I(“VP_TRANSFORMATION”, “Simple message”);

        其中 ##VA_ARGS 確保了 VA_ARGS 為空時(shí)不會出現(xiàn)額外的 ,。(在下面介紹個(gè)展開的例子)

        2.2 LOGE_AND_RETURN_IF 宏

        下面來解讀 LOGE_AND_RETURN_IF 宏的寫法:

        #define LOGE_AND_RETURN_IF(condition, code, err_msg, ...) \
         do {                                                    \
           if (condition) {                                      \
             LOGE(err_msg, ##__VA_ARGS__);                       \
             return code;                                        \
           }                                                     \
         } while (0)

        這個(gè)宏 LOGE_AND_RETURN_IF 是一個(gè)常見的 防御式編程(Defensive Programming) 宏,如果 condition 為真,則:

          記錄錯(cuò)誤日志 LOGE(err_msg, ##VA_ARGS)

          返回 code

        定義一個(gè) LOGE_AND_RETURN_IF 宏時(shí),do { … } while (0)起到什么作用?

        do { … } while (0) 的作用是讓整個(gè)宏塊結(jié)構(gòu)化,使它在語法上表現(xiàn)得像一個(gè)普通的語句,這樣:確保 if 語句和 return 被視為一個(gè)整體,不會因?yàn)?if-else 結(jié)構(gòu)導(dǎo)致錯(cuò)誤。

        條件滿足,觸發(fā)錯(cuò)誤日志并返回;當(dāng) … 為空時(shí),去掉前面的 ,,防止編譯錯(cuò)誤,示例(無可變參數(shù))

        LOGE_AND_RETURN_IF(error, -1, "An error occurred");

        展開后

        do {
           if (error) {
               LOGE("An error occurred");
               return -1;
           }
        } while (0);

        ##VA_ARGS 確保了 沒有多余的 , 影響 LOGE 語法。

        (帶可變參數(shù))

        LOGE_AND_RETURN_IF(value < 0, -1, "Invalid value: %d", value);

        展開后

        do {
           if (value < 0) {
               LOGE("Invalid value: %d", value);
               return -1;
           }
        } while (0);

        2.3 do { … } while (0)作用

        在前面有說 do { … } while (0) 可以:確保 if 語句和 return 被視為一個(gè)整體,不會因?yàn)?if-else 結(jié)構(gòu)導(dǎo)致錯(cuò)誤。

        為什么 do { … } while (0) 能避免 if-else 結(jié)構(gòu)導(dǎo)致的錯(cuò)誤?if-else 結(jié)構(gòu)有什么問題?下面來看一下

        do { … } while (0) 的核心作用是讓整個(gè)宏塊在語法上表現(xiàn)得像一個(gè)單一語句,從而避免 if-else 結(jié)構(gòu)中的 懸空 else(dangling else) 和 單行 if 語句的 bug。

        2.3.1 if-else 結(jié)構(gòu)的問題

        如果 沒有 do { … } while (0),在 if-else 語句中 直接用 if 語句包裹宏時(shí),可能導(dǎo)致 else 結(jié)構(gòu)錯(cuò)誤。錯(cuò)誤示例如下:

        未使用 do { … } while (0) 定義宏

        #define LOGE_AND_RETURN_IF(condition, code, err_msg, ...) \
           if (condition) {                                      \
               LOGE(err_msg, ##__VA_ARGS__);                     \
               return code;                                      \
           }

        代碼中調(diào)用宏

        if (x < 0)
           LOGE_AND_RETURN_IF(x < -10, -1, "x is too small");
        else
           printf("x is positive\n");

        錯(cuò)誤展開:

        if (x < 0)
           if (x < -10) {  
               LOGE("x is too small");  
               return -1;  
           }  // 這里的 `if` 結(jié)束
        else  // 這個(gè) else 可能會匹配到錯(cuò)誤的 if!
           printf("x is positive\n");

        此時(shí)就會引入懸空 else 的問題。

        2.3.2 懸空 else 問題

        錯(cuò)誤現(xiàn)象:

        x = -5:
        x is positive   // 這個(gè)輸出是不應(yīng)該出現(xiàn)的!

        C/C++ 規(guī)定:else 總是匹配最近的 if。

        上一節(jié)中 else 錯(cuò)誤地匹配了 if (x < -10),而不是 if (x < 0),導(dǎo)致:

        x = -5 時(shí),if (x < 0) 為真,但 if (x < -10) 為假,所以 LOGE_AND_RETURN_IF 不執(zhí)行。

        然而 else 仍然執(zhí)行,最終 printf(“x is positive\n”); 被錯(cuò)誤地執(zhí)行!

        解決方案:使用 do { … } while (0)結(jié)構(gòu)。

        2.4 NULLPTR_ERR(ptr) 宏

        #define NULLPTR_ERR(ptr) #ptr " is null pointer"

        #ptr 是 字符串化(stringizing)運(yùn)算符,它會把 ptr 變成字符串。

        示例:

        printf("%s\n", NULLPTR_ERR(my_ptr));

        展開后:

        printf("%s\n", "my_ptr is null pointer");

        輸出:my_ptr is null pointer

        2.5 LOGE_AND_RETURN_IF_NULL(ptr, code) 宏

        #define LOGE_AND_RETURN_IF_NULL(ptr, code) \
         LOGE_AND_RETURN_IF(ptr == nullptr, code, NULLPTR_ERR(ptr))

        這個(gè)宏會:

        檢查 ptr 是否為 nullptr。

        如果 ptr == nullptr:

        記錄日志,日志信息由 NULLPTR_ERR(ptr) 生成,例如 “ptr is null pointer”。

        返回 code 作為錯(cuò)誤碼。

        3.總結(jié)

        本文主要介紹在 VP 單算子示例中用到的日志打印的頭文件應(yīng)該怎么寫,主要適用于和我一樣 C++基礎(chǔ)學(xué)習(xí)用戶~


        *博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請聯(lián)系工作人員刪除。




        相關(guān)推薦

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 上饶市| 桂东县| 汽车| 额济纳旗| 台湾省| 泗洪县| 西盟| 普兰店市| 彩票| 成安县| 宝丰县| 镇远县| 黄龙县| 阜新| 岳普湖县| 红桥区| 通榆县| 磴口县| 克什克腾旗| 石渠县| 紫金县| 儋州市| 砀山县| 云霄县| 黔西| 比如县| 福安市| 宁强县| 安陆市| 白山市| 通城县| 宁夏| 扬州市| 松潘县| 交城县| 旌德县| 卫辉市| 怀集县| 吉木乃县| 外汇| 宁晋县|