簡體   English   中英

在結構/類的編譯期間使用用戶定義的大小“重新創建” sizeof 運算符的正確方法是什么

[英]What would be the correct way to "recreate" sizeof operator with user defined sizes during compile time for structs/classes

早晨,

為結構和類“重新創建” sizeof運算符並為其提供我自己的大小的正確方法是什么。

我最初的想法是創建一個看起來像這樣的助手 class:

template<size_t SizeInBytes = 0>
struct SizeHelper
{
    inline static const size_t size = SizeInBytes;
};


class Test : public SizeHelper<133>
{
public:
};
class Test2 : public SizeHelper<13>
{
public:
};

什么時候做:

template<class  T>
T ReadMemory(uintptr_t base, std::ptrdiff_t offset)
{
    constexpr auto size = T::size;
    return *(T*)mmio(base + size, size);
    .....
}

每次使用ReadMemory<T>(0,0)為一個T constexpr auto size = T::size; 會在編譯時計算嗎? 到目前為止,代碼已經優化,內部mmio調用的size參數將被替換? 這樣就不需要先查找全局 var size了嗎? 基本上然后看一個反匯編程序,調用會看起來:

// Call of ReadMemory<Test>
mmio(..., 133);

// Call of ReadMemory<Test2>
mmio(...,13); 

或者我怎么能做到這一點?

編輯:我正在從外部 memory 源訪問 memory。 因此依靠mmio將其從外部 memory 復制到我程序的 memory 中。 然后解釋 memory 我曾經重新創建存儲在 memory 中的結構並正確填充它們以便我可以輕松訪問這些字段。

例如:

struct ExampleClass 
{
    float data;
    int more_data;
    uint_t pad_1[32];
    Class* class;
}

這些類可以有許多成員,並且取決於提供外部 memory 源的軟件版本,並且各個字段具有不同的偏移量。

因此,我不是多次重新創建每個 class,而是

struct ExampleClass 
{
    float GetData() 
    {
        return *(float*)this + version::data;
    }
    int GetMore_Data() 
    {
       return *(int*)this + version::more_data;
    }
};

現在我可以簡單地切換#define version offsets::version12宏並為正確的版本編譯軟件。

這種設計打破了sizeof 因為我不想為每個字段讀取單個讀取調用,所以我正在讀取結構大小的整個 memory,然后使用修改后的 class 訪問成員。 所以我仍然依賴基礎數據的大小,因此采用size=largest offset + sizeof field_type來獲得必須從外部源讀取的正確大小。 通常我只會為mmio提供正確的大小,但相同的結構可以在多個指針鏈等中使用。當大小發生變化時,我仍然需要更新所有調用,因此,最好將其推入此SizeHelper它是從 class 本身獲得的。

問候阿圖爾

每次使用 ReadMemory(0,0) 為一個 T constexpr auto size = T::size; 會在編譯時計算嗎?

它可能是。 使用constexpr而不是inline const

到目前為止,代碼已經優化,內部 mmio 調用的 size 參數將被替換? 這樣就不需要先查找全局變量大小了嗎?

這取決於編譯器和選項。 您可以關閉優化。

不要相信我。 編譯您的代碼並檢查匯編代碼。 還有神螺栓 https://godbolt.org/z/Kh7s1e4sv

利用:

template<size_t SizeInBytes = 0>
struct SizeHelper {
    static constexpr size_t size = SizeInBytes;
};

從我的角度來看,大小信息是毫無用處的,因為數據接收者必須知道完整的 object 詳細信息才能完全解釋這些數據。 大小絕對沒有幫助,因為根本沒有任何東西可以幫助我們解釋這個二進制數據。 如果我們有完整的類型描述,我們會自動知道大小,並且解決方案與 OP 問題相去甚遠。

回到需求:一方創建數據轉儲,另一方正在讀取它,我們需要一個簡單的東西:Serializer。 如果通信結果為二進制流或 XML 或 JSON 或客戶喜歡的任何內容,則它是完全獨立的。 而且我們也不需要知道我們正在使用什么樣的數據交換基礎設施。 如果它是 HTML 上的 get() 或套接字或某些具有共享文件訪問權限的 VPN。

但是我們需要一個定義良好的接口,它不僅可以分發純數據,還可以分發其版本信息。 這一切都由或多或少所有可用的序列化程序完成。

我希望每個分布式數據對象都使用某種版本信息或簡單地使用序列化的std::variant來處理自己,其中存儲的聯合元素的 id 足以很好地描述數據。 大多數序列化程序都能夠編寫數據容器,其中可能包含任何順序的變體容器。

作為我們當前使用的示例:(從單元測試中截取的代碼)

   { // writing

        ofstream os("variant_nativ.dat");
        PLAIN_TEXT_WRITER file(os);

        class SerialWriter:
        public ...SC< list of serializer components>{}
        ser( {{file}} );


        std::variant< int, double, std::string > var2;

        var2 = 1.2;
        ser & var2;

        var2 = "Hallo";
        ser & var2;

        var2 = 42; 
        ser & var2;
    }

並閱讀:

    {
       class SerialReader: public CS< list of components >{}
       ser( {{file},{std::cerr}});


        std::variant< int, double, std::string > var2;

        ser & var2;
        if(auto pval = std::get_if<double>(&var2))
        {
            EXPECT_EQ( 1.2, *pval );
        }
        else
        {
            FAIL();
        }

        ser & var2;
        if(auto pval = std::get_if<std::string>(&var2))
        {
            EXPECT_EQ( "Hallo", *pval );
        }
        else
        {
            FAIL();
        }

        ser & var2;
        if(auto pval = std::get_if<int>(&var2))
        {
            EXPECT_EQ( 42, *pval );
        }
        else
        {
            FAIL();
        }
    }

如上所述,您想要傳輸什么數據以及您擁有哪種格式並不重要。 只需序列化。 如果序列化doublestd::vector< std::variant<std::string, std::variant<int,double, A,B,C>>>也沒關系。

重要的是您可以使用std::variant作為具有不同版本的相關類型列表的容器,例如: std::variant< CLASS_A_V1, CLASS_B_V2, ...> 所有這些都是開箱即用的。

序列化器還關注來自不同主機系統的不同二進制數據輸出和/或不同編譯器的使用。 作為后備,您總是可以通過簡單地更改一行代碼來決定使用人類可讀的格式,例如 XML。

但只需從給定服務器獲取二進制 stream 寫為 memory 轉儲永遠不適合,因為如果它們產生不同的 ZCD69B4957F06CD818D7ZBF3D61 和數據結構的所有布局信息,您還必須注意填充、字節順序、編譯器版本很快。

並且通過簡單地cast數據轉換為對象,您還有另一個問題:根據定義,這是未定義的行為,如果對象包含 vtables 或其他不受開發人員控制的東西,您的程序將失敗。 很清楚,我們都知道,即使按照語言規則它是 UB,將罐子寫入流並將它們投射回具有相同操作系統和相同編譯器版本的同一個 cpu 上也可以工作。 但這不是我們在潛在異構服務器群中具有不同服務器架構的商業環境中使用的東西。

暫無
暫無

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

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