簡體   English   中英

將 C++ object 指針傳遞給 C++ class 通過 C++/CLI

[英]Passing a C++ object pointer to a C++ class through C++/CLI

我有一個特殊的問題,我似乎無法在最小的工作示例中重現。 我必須處理一個大型遺留代碼框架,並從我的 scope 中修改所有這些。 為了處理它,我必須應用一些特定的模式。

代碼庫概述

我有一個托管的 C# 應用程序(.NET 5.0)。 在這個應用程序中,我需要運行一些 C++ 代碼。 為此,(遺留代碼的一部分)有一個 CLI 包裝器項目。 基於配置,這個遺留框架使用包裝器來實例化 C++ 類,並對其調用方法並處理結果,然后銷毀所有 C++ 類 這一切都在這個遺留框架中給出,並且改變它超出了我當前的 scope 這個 CLI 包裝器只允許我將字符串作為參數傳遞給它創建的 C++ 類

問題的根源

每隔幾秒就會重復調用 C++ 代碼。 它應該快速響應。 我的 C++ 代碼需要從磁盤加載一些大文件(大約 400 MB)並處理它,這需要相當長的時間。 由於每次都會重新創建 C++ 類,因此每次加載文件都會消耗大量時間,這是不可接受的。 由於該數據基本上是恆定的,因此我嘗試在程序初始化期間加載一次。 然后我將一個指針傳遞給我的 C++ class 然后可以使用 object。 當 C++ class 被破壞時,object 將保留在 memory 中,以便以后再次使用。

更復雜的是,我需要一個相當大的庫來讀取和處理我的文件。 如果我讓 CLI 包裝器依賴於此,它將無法編譯。 我可以想象這是因為 CLI 的東西。 因此,我使用了一個void指針,因此包裝器不必知道指針背后的實際類型。 我的 C++ 所在的項目有這個大型庫作為依賴項。 這一切都編譯得很好。

我的解決方案

我對 CLI 包裝器做了一個小擴展,以創建 object,它從磁盤讀取我的文件並將信息保存在 memory 中。 這個 object 是使用CreateInformationObject()方法創建的。 ptr_native是一個智能指針,用於在托管代碼中使用本機對象。 它的類型是: CAutoNativePtr<std::shared_ptr<void>> ptr_native 創建我的 object 看起來像:

// Create a shared_ptr on dynamic memory (i.e. heap).
std::shared_ptr<void>* objectPointer = new std::shared_ptr<void>();

// Load the module and store a shared pointer pointing to it in the dynamic memory.
*objectPointer = CppConsumerStuff::CppConsumer::CreateInformationObject(value);

// Load the module and store a shared pointer pointing to it in the dynamic memory.
ptr_native.Attach(objectPointer);

然后,由於遺留框架,我嘗試了這個長鏡頭:將指針地址轉換為string ,通過框架將其傳遞給我的 C++ class 並將其轉換回指向 ZA8CFDE6331BD59EB26666F8911CB4 的實際類型的指針。

這就像(在我的 CLI 包裝器擴展中):

//Cast void pointer to string.
String^ CliStorage::GetPointerString()
{
    std::stringstream ss;
    ss << (*ptr_native).get();  // Pointer to hex string.
    std::string ptr_string = ss.str();
    return StringToManaged(ptr_string);
}

最后,(在我的 C++ 類中),我將此指針字符串轉換回指向實際 object 的指針:

void DoWorkOnLargeObject(std::string ptr_string)
{
    // Cast pointer to usable type
    uint64_t raw_ptr = 0; // Define int size depending on system architecture.
    std::stringstream ss;
    ss << std::hex << ptr_string;
    ss >> raw_ptr; //Hex string to int.
    cppObjectPtr = reinterpret_cast<void*>(raw_ptr);
    LargeLibrary::ActualObjectType* cppObjectPtrCasted = static_cast<LargeLibrary::ActualObjectType*>(cppObjectPtr);
    
    // Use the object.
    cppObjectPtrCasted->GetDataStuff();
    // Rest of code doing work...
}

我的結果

我在 Visual Studio 2019 中構建了所有這些。當我創建調試版本時,一切正常:)。 但是,當我創建發布版本時,它不起作用並引發以下異常:``

最小的工作示例

我試圖創建一個最小的工作示例。 有和沒有大型外部庫。 但是,在我的最小工作示例中,無論構建類型(調試/發布)如何,它總是有效。

我的問題

所以我的問題是:我的最小工作示例是否偶然起作用,我是否依賴於未定義的行為? 或者這個概念(不管它有多丑陋)是否真的有效? 如果是未定義的行為,請解釋一下,我想學習。 如果它應該工作,問題在於遺留框架,我將對此進行調查。

我知道這些是非常難看的模式,但我試圖用我的 scope 中的方法來獲得一些東西。

謝謝

我不是這方面的專家,所以請謹慎對待我的建議。 在我看來,由於 memory 保護(禁止程序僅訪問未分配給它們的 memory),在進程之間直接共享 memory 地址通常會失敗。

您可以使用共享 memory 這是在進程之間共享的 memory。 通常人們會使用它在並發進程之間共享 memory 但這絕不是必要的(並且沒有競爭訪問實際上是有益的)。 Wikipedia 列出了 boost 和 Qt 作為對共享 memory 實現跨平台支持的庫的示例。

查看用於共享 memory的 boost 文檔,它說“由於共享 memory 具有 kernel 或文件系統持久性,用戶必須明確地銷毀它,因為它應該在調用之間持久化。 請注意,您應該以其他方式刪除共享的 memory,因為它會持續存在。

改編文檔中的示例,它可能看起來像這樣:

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>
#include <cstdlib>
#include <string>

constexpr auto shm_name = "SharedMemoryCLI";
using namespace boost::interprocess;

auto create_shared_memory() {
    // Compute your data and calculate the size needed:
    shared_memory_object shm {create_only, shm_name, read_write};

    // Either use an upper bound for the size needed or compute your data before.
    shm.truncate(data_size);

    //Map the whole shared memory in this process
    mapped_region region{shm, read_write};

    // Either write your data directly to region.get_address():
    compute_data_to(region.get_address());
    // Or have the data already computed and memcopy it:
    std::memcpy(region.get_address(), data_ptr, data_size);
    return region;
}

auto obtain_memory_region() {
   try {
      shared_memory_object shm{open_only, shm_name, read_only};
      return mapped_region {shm, read_only};
   } catch(const std::exception &er) {
      // One should probably check if this is the "right" exception but boost does not say what type it uses here...
      return create_shared_memory();
   }
}

int main(int argc, char *argv[])
{
   region = obtain_memory_region();
   static_cast<char*>(region.get_address()); // can be used as a to your data.
   return 0;
}

請注意,您可能必須以其他方式(或者可能只是該區域的前 8 個字節)保留共享 memory 的確切大小。 然后,您可能必須以某種方式將char*恢復為您想要的類型,但我認為reinterpret_cast應該在這里工作。

上面的代碼未經測試,我不做任何保證,但我非常有信心它應該以這種方式大致工作,並且與共享指針一樣快(如果這樣的話)。 在以任何方式應用之前,您真的應該閱讀https://www.boost.org/doc/libs/1_48_0/doc/html/interprocess/sharedmemorybetweenprocesses.html的全部內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM