<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 10.2 生成輸出頭文件 **NOTE**:*此示例代碼可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-10/recipe-02 中找到,其中有一個C++示例。該示例在CMake 3.6版(或更高版本)中是有效的,并且已經在GNU/Linux、macOS和Windows上進行過測試。* 設想一下,當我們的小型庫非常受歡迎時,許多人都在使用它。然而,一些客戶希望在安裝時使用靜態庫,而另一些客戶也注意到所有符號在動態庫中都是可見的。最佳方式是規定動態庫只公開最小的符號,從而限制代碼中定義的對象和函數對外的可見性。我們希望在默認情況下,動態庫定義的所有符號都對外隱藏。這將使得項目的貢獻者,能夠清楚地劃分庫和外部代碼之間的接口,因為他們必須顯式地標記所有要在項目外部使用的符號。因此,我們需要完成以下工作: * 使用同一組源文件構建動態庫和靜態庫 * 確保正確分隔動態庫中符號的可見性 第1章第3節中,已經展示了CMake提供了與平臺無關的方式實現的功能。但是,沒有處理符號可見性的問題。我們將用當前的配方重新討論這兩點。 ## 準備工作 我們仍將使用與前一個示例中基本相同的代碼,但是我們需要修改`src/CMakeLists.txt`和`Message.hpp`頭文件。后者將包括新的、自動生成的頭文件`messageExport.h`: ```c++ #pragma once #include #include #include "messageExport.h" class message_EXPORT Message { public: Message(const std::string &m) : message_(m) {} friend std::ostream &operator<<(std::ostream &os, Message &obj) { return obj.printObject(os); } private: std::string message_; std::ostream &printObject(std::ostream &os); }; std::string getUUID(); ``` `Message`類的聲明中引入了`message_EXPORT`預處理器指令,這個指令將讓編譯器生成對庫的用戶可見的符號。 ## 具體實施 除了項目的名稱外,主`CMakeLists.txt`文件沒有改變。首先,看看`src`子目錄中的`CMakeLists.txt`文件,所有工作實際上都在這里進行。我們將重點展示對之前示例的修改之處: 1. 為消息傳遞庫聲明`SHARED`庫目標及其源。注意,編譯定義和鏈接庫沒有改變: ```cmake add_library(message-shared SHARED "") target_sources(message-shared PRIVATE ${CMAKE_CURRENT_LIST_DIR}/Message.cpp ) target_compile_definitions(message-shared PUBLIC $<$<BOOL:${UUID_FOUND}>:HAVE_UUID> ) target_link_libraries(message-shared PUBLIC $<$<BOOL:${UUID_FOUND}>:PkgConfig::UUID> ) ``` 2. 設置目標屬性。將`${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/messageExport.h`頭文件添加到公共頭列表中,作為`PUBLIC_HEADER`目標屬性的參數。`CXX_VISIBILITY_PRESET`置和`VISIBILITY_INLINES_HIDDEN`屬性將在下一節中討論: ```cmake set_target_properties(message-shared PROPERTIES POSITION_INDEPENDENT_CODE 1 CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1 SOVERSION ${PROJECT_VERSION_MAJOR} OUTPUT_NAME "message" DEBUG_POSTFIX "_d" PUBLIC_HEADER "Message.hpp;${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/messageExport.h" MACOSX_RPATH ON ) ``` 3. 包含` GenerateExportHeader.cmake`模塊并調用`generate_export_header`函數,這將在構建目錄的子目錄中生成`messageExport.h`頭文件。我們將稍后會詳細討論這個函數和生成的頭文件: ```cmake include(GenerateExportHeader) generate_export_header(message-shared BASE_NAME "message" EXPORT_MACRO_NAME "message_EXPORT" EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/messageExport.h" DEPRECATED_MACRO_NAME "message_DEPRECATED" NO_EXPORT_MACRO_NAME "message_NO_EXPORT" STATIC_DEFINE "message_STATIC_DEFINE" NO_DEPRECATED_MACRO_NAME "message_NO_DEPRECATED" DEFINE_NO_DEPRECATED ) ``` 4. 當要更改符號的可見性(從其默認值-隱藏值)時,都應該包含導出頭文件。我們已經在`Message.hpp`頭文件例這樣做了,因為想在庫中公開一些符號。現在將`${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}`目錄作為` message-shared`目標的`PUBLIC`包含目錄列出: ```cmake target_include_directories(message-shared PUBLIC ${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR} ) ``` 現在,可以將注意力轉向靜態庫的生成: 1. 添加一個庫目標來生成靜態庫。將編譯與靜態庫相同的源文件,以獲得此動態庫目標: ```cmake add_library(message-static STATIC "") target_sources(message-static PRIVATE ${CMAKE_CURRENT_LIST_DIR}/Message.cpp ) ``` 2. 設置編譯器定義,包含目錄和鏈接庫,就像我們為動態庫目標所做的一樣。但請注意,我們添加了`message_STATIC_DEFINE`編譯時宏定義,為了確保我們的符號可以適當地暴露: ```cmake target_compile_definitions(message-static PUBLIC message_STATIC_DEFINE $<$<BOOL:${UUID_FOUND}>:HAVE_UUID> ) target_include_directories(message-static PUBLIC ${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR} ) target_link_libraries(message-static PUBLIC $<$<BOOL:${UUID_FOUND}>:PkgConfig::UUID> ) ``` 3. 還設置了` message-static `目標的屬性: ```cmake set_target_properties(message-static PROPERTIES POSITION_INDEPENDENT_CODE 1 ARCHIVE_OUTPUT_NAME "message" DEBUG_POSTFIX "_sd" RELEASE_POSTFIX "_s" PUBLIC_HEADER "Message.hpp;${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/messageExport.h" ) ``` 4. 除了鏈接到消息動態庫目標的`hello-world_wDSO`可執行目標之外,還定義了另一個可執行目標`hello-world_wAR`,這個鏈接指向靜態庫: ```cmake add_executable(hello-world_wAR hello-world.cpp) target_link_libraries(hello-world_wAR PUBLIC message-static ) ``` 5. 安裝指令現在多了`message-static`和`hello-world_wAR`目標,其他沒有改變: ```cmake install( TARGETS message-shared message-static hello-world_wDSO hello-world_wAR ARCHIVE DESTINATION ${INSTALL_LIBDIR} COMPONENT lib RUNTIME DESTINATION ${INSTALL_BINDIR} COMPONENT bin LIBRARY DESTINATION ${INSTALL_LIBDIR} COMPONENT lib PUBLIC_HEADER DESTINATION ${INSTALL_INCLUDEDIR}/message COMPONENT dev ) ``` ## 工作原理 此示例演示了,如何設置動態庫的符號可見性。最好的方式是在默認情況下隱藏所有符號,顯式地只公開那些需要使用的符號。這需要分為兩步實現。首先,需要指示編譯器隱藏符號。當然,不同的編譯器將有不同的可用選項,并且直接在`CMakeLists.txt`中設置這些選項并不是是跨平臺的。CMake通過在動態庫目標上設置兩個屬性,提供了一種健壯的跨平臺方法來設置符號的可見性: * `CXX_VISIBILITY_PRESET hidden`:這將隱藏所有符號,除非顯式地標記了其他符號。當使用GNU編譯器時,這將為目標添加`-fvisibility=hidden`標志。 * `VISIBILITY_INLINES_HIDDEN 1`:這將隱藏內聯函數的符號。如果使用GNU編譯器,這對應于` -fvisibility-inlines-hidden ` Windows上,這都是默認行為。實際上,我們需要在前面的示例中通過設置`WINDOWS_EXPORT_ALL_SYMBOLS`屬性為`ON`來覆蓋它。 如何標記可見的符號?這由預處理器決定,因此需要提供相應的預處理宏,這些宏可以擴展到所選平臺上,以便編譯器能夠理解可見性屬性。CMake中有現成的`GenerateExportHeader.cmake `模塊。這個模塊定義了`generate_export_header`函數,我們調用它的過程如下: ```cmake include(GenerateExportHeader) generate_export_header(message-shared BASE_NAME "message" EXPORT_MACRO_NAME "message_EXPORT" EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/messageExport.h" DEPRECATED_MACRO_NAME "message_DEPRECATED" NO_EXPORT_MACRO_NAME "message_NO_EXPORT" STATIC_DEFINE "message_STATIC_DEFINE" NO_DEPRECATED_MACRO_NAME "message_NO_DEPRECATED" DEFINE_NO_DEPRECATED ) ``` 該函數生成`messageExport.h`頭文件,其中包含預處理器所需的宏。根據`EXPORT_FILE_NAME`選項的請求,在目錄`${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}`中生成該文件。如果該選項為空,則頭文件將在當前二進制目錄中生成。這個函數的第一個參數是現有的目標(示例中是`message- shared`),函數的基本調用只需要傳遞現有目標的名稱即可。可選參數,用于細粒度的控制所有生成宏,也可以傳遞: * BASE_NAME:設置生成的頭文件和宏的名稱。 * EXPORT_MACRO_NAME:設置導出宏的名稱。 * EXPORT_FILE_NAME:設置導出頭文件的名稱。 * DEPRECATED_MACRO_NAME:設置棄用宏的名稱。這是用來標記將要廢棄的代碼,如果客戶使用該宏定義,編譯器將發出一個將要廢棄的警告。 * NO_EXPORT_MACRO_NAME:設置不導出宏的名字。 * STATIC_DEFINE:用于定義宏的名稱,以便使用相同源編譯靜態庫時使用。 * NO_DEPRECATED_MACRO_NAME:設置宏的名稱,在編譯時將“將要廢棄”的代碼排除在外。 * DEFINE_NO_DEPRECATED:指示CMake生成預處理器代碼,以從編譯中排除“將要廢棄”的代碼。 GNU/Linux上,使用GNU編譯器,CMake將生成以下`messageExport.h`頭文件: ```cmake #ifndef message_EXPORT_H #define message_EXPORT_H #ifdef message_STATIC_DEFINE # define message_EXPORT # define message_NO_EXPORT #else # ifndef message_EXPORT # ifdef message_shared_EXPORTS /* We are building this library */ # define message_EXPORT __attribute__((visibility("default"))) # else /* We are using this library */ # define message_EXPORT __attribute__((visibility("default"))) # endif # endif # ifndef message_NO_EXPORT # define message_NO_EXPORT __attribute__((visibility("hidden"))) # endif #endif #ifndef message_DEPRECATED # define message_DEPRECATED __attribute__ ((__deprecated__)) #endif #ifndef message_DEPRECATED_EXPORT # define message_DEPRECATED_EXPORT message_EXPORT message_DEPRECATED #endif #ifndef message_DEPRECATED_NO_EXPORT # define message_DEPRECATED_NO_EXPORT message_NO_EXPORT message_DEPRECATED #endif #if 1 /* DEFINE_NO_DEPRECATED */ # ifndef message_NO_DEPRECATED # define message_NO_DEPRECATED # endif #endif #endif ``` 我們可以使用`message_EXPORT`宏,預先處理用戶公開類和函數。棄用可以通過在前面加上`message_DEPRECATED`宏來實現。 從`messageExport.h`頭文件的內容可以看出,所有符號都應該在靜態庫中可見,這就是`message_STATIC_DEFINE`宏起了作用。當聲明了目標,我們就將其設置為編譯時定義。靜態庫的其他目標屬性如下: * `ARCHIVE_OUTPUT_NAME "message"`:這將確保庫文件的名稱是`message`,而不是`message-static`。 * `DEBUG_POSTFIX "_sd" `:這將把給定的后綴附加到庫名稱中。當目標構建類型為Release時,為靜態庫添加"_sd"后綴。 * `RELEASE_POSTFIX "_s" `:這與前面的屬性類似,當目標構建類型為Release時,為靜態庫添加后綴“_s”。 ## 更多信息 構建動態庫時,隱藏內部符號是一個很好的方式。這意味著庫會縮小,因為向用戶公開的內容要小于庫中的內容。這定義了應用程序二進制接口(ABI),通常情況下應該與應用程序編程接口(API)一致。這分兩個階段進行: 1. 使用適當的編譯器標志。 2. 使用預處理器變量(示例中是`message_EXPORT`)標記要導出的符號。編譯時,將解除這些符號(類和函數)的隱藏。 靜態庫只是目標文件的歸檔。因此,可以將源代碼編譯成目標文件,然后歸檔器將它們捆綁到歸檔文件中。這時沒有ABI的概念:所有符號在默認情況下都是可見的,編譯器的可見標志不影響靜態歸檔。但是,如果要從相同的源文件構建動態和靜態庫,則需要一種方法來賦予`message_EXPORT`預處理變量意義,這兩種情況都會出現在代碼中。這里使用` GenerateExportHeader.cmake`模塊,它定義一個包含所有邏輯的頭文件,用于給出這個預處理變量的正確定義。對于動態庫,它將給定的平臺與編譯器相組合。注意,根據構建或使用動態庫,宏定義也會發生變化。幸運的是,CMake為我們解決了這個問題。對于靜態庫,它將擴展為一個空字符串,執行我們期望的操作——什么也不做。 細心的讀者會注意到,構建此處所示的靜態和共享庫實際上需要編譯源代碼兩次。對于我們的簡單示例來說,這不是一個很大的開銷,但會顯得相當麻煩,即使對于只比示例稍大一點的項目來說,也是如此。為什么我們選擇這種方法,而不是使用第1章第3節的方式呢?`OBJECT`庫負責編譯庫的第一步:從源文件到對象文件。該步驟中,預處理器將介入并計算`message_EXPORT`。由于對象庫的編譯只發生一次,`message_EXPORT`被計算為構建動態庫庫或靜態庫兼容的值。因此,為了避免歧義,我們選擇了更健壯的方法,即編譯兩次,為的就是讓預處理器正確地評估變量的可見性。 **NOTE**:*有關動態共享對象、靜態存檔和符號可見性的更多細節,建議閱讀:http://people.redhat.com/drepper/dsohowto.pdf*
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看