簡體   English   中英

我如何確保在編譯時我的序列化函數創建具有預期大小的緩沖區?

[英]How can I ensure at compile time that my serialization functions create buffers with the expected size?

目前我們使用帶位域的打包結構來創建我們的數據

struct Data1
{
   uint8_t type;
   uint8_t value;
   bool aBool:1;
   uint8_t threeBits:3;
   uint8_t spare:4;
} __attribute__((packed))
const uint8_t Data1SerializedSize{3};
static_assert(sizeof(Data1) == Data1SerializedSize, "wrong Data1 size");

...

const uint8_t MaxBufferSize{32};

union AllDatas
{
   Data1 data1;
   ...
   uint8_t buffer[MaxBufferSize];
}__attribute__((packed))
static_assert(sizeof(AllDatas) <= MaxBufferSize);

它確實有效,因為我們在任何地方都使用相同的 cpu 架構,而 gcc 足以讓我們使用不是我們上次編寫的 AllDatas 聯合的成員。

void send(Serial& serial, const Data1& data)
{
    AllDatas u;
    u.data1 = data;
    serial.write(u.buffer, Data1SerializedSize);
}

但:

  • 如果我們遵循標准規范,這是未定義的行為
  • 它嚴重依賴於平台。
  • 它相當混亂和低效

但它提供的優勢是允許我們在編譯時檢查我們的結構是否具有我們期望的大小,因此,如果不是這種情況則不會編譯(例如在 32 位平台上工作,但不能在 64 位平台上工作)

我如何創建序列化函數,以確保至少我的 output 緩沖區具有預期的大小,並且我沒有忘記添加一些成員,或者相反,例如將一個成員放兩次?

std::array<uint8_t, Data1SerializedSize> serialize(const Data1& data)
{
   std::array<uint8_t, Data1SerializedSize> buffer;
   uint8_t head{0};
   buffer[head] = data.type;
   ++head;
   buffer[head] = data.size;
   ++head;
   buffer[head] = (data.aBool & 0x1) 
                | ((data.threeBits << 1) & 0b1110) 
                | ((data.spare << 4) & 0b11110000);
   ++head;
   //static_assert(head == Data1SerializedSize);//won't work and above code looks more error prone
   return buffer;
}

序列化格式已明確定義,因此我無法切換到序列化庫,因為它們(據我所知)不能如此緊密地打包,因此從一開始就不會遵循我們的格式。

作為附帶問題,有沒有辦法避免數組的雙精度類型聲明(在返回類型和 function 主體中)? 我發現沒有辦法在 function 本身中創建 function 的返回類型的變量?

我終於設法得到一些東西,不僅可以在編譯時檢查大小是否正確,而且我的序列化和反序列化函數也能按預期工作

序列化器.h

struct Data1Serializer
{
   static const uint8_t size{3};
   constexpr std::array<uint8_t, size> serialize(const Data1& data);
   constexpr Data1 deserialize(const std::array<uint8_t, size>& buffer);
}

序列化器.cpp

constexpr auto
Data1Serializer::serialize(const Data1& data)
-> std::array<uint8_t, size>
{
   std::array<uint8_t, size> buffer;
   uint8_t head{0};
   buffer[head] = data.type;
   ++head;
   buffer[head] = data.value;
   ++head;
   buffer[head] = (data.aBool & 1)
            | ((data.threeBits >> 1) & 0b111)
            | ((data.spare >> 4) & 0b1111));
   ++head;
   assert(head == size);
   return buffer;
}

constexpr auto
Dat1Serializer::deserialize(const std::array<uint8_t, size>& buffer)
-> Data1
{
   Data1 data;
   data.type = buffer[0];
   data.value = buffer[1];
   data.aBool = buffer[2] & 1;
   data.threeBits = (buffer[2] >> 1) & 0b111;
   data.spare = (buffer[2] >> 4) & 0b1111;
   return data;
}

consteval bool checkData1()
{
   Data1 data{3,2,true,5,0};
   Data1Serializer serializer;
   const auto& result = serializer.deserialize(serializer.serialize(data));
   
   return (data.type == result.type)
       && (data.value == result.value)
       && (data.aBool == result.aBool)
       && (data.threeBits == result.threeBits)
       && (data.spare == result.spare);
}

static_assert(checkData1() == true, "Data1 serialization or deserialization failure");

通過在編譯時對數組進行越界檢查、確保我們填充了整個緩沖區的斷言以及在 checkData1() 中進行的比較,我可以確信我的序列化和反序列化函數可以正常工作。

我想我會把所有這些都移到單元測試中,以避免編譯時間開銷和 consteval 檢查中涉及的每個 function 所需的所有 constexpr,但很高興知道這是可能的。

暫無
暫無

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

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