猿问

将 C++ 字节结构转换/解析为 Go

我在 Go 中读取一些数据包,其中的字段是 C++ 数据类型。我尝试解析数据,但我正在读取垃圾值。


这是一个小例子 - 特定数据类型的数据规格表在 C++ 中如下所示,


struct CarTelemetryData

{

    uint16    m_speed;                      

    uint8     m_throttle;                   

    int8      m_steer;                      

    uint8     m_brake;                     

    uint8     m_clutch;                     

    int8      m_gear;                       

    uint16    m_engineRPM;                  

    uint8     m_drs;                        

    uint8     m_revLightsPercent;           

    uint16    m_brakesTemperature[4];       

    uint16    m_tyresSurfaceTemperature[4]; 

    uint16    m_tyresInnerTemperature[4];   

    uint16    m_engineTemperature;          

    float     m_tyresPressure[4];           

};

以下是我在 Go 中定义的内容


type CarTelemetryData struct {

    Speed                   uint16

    Throttle                uint8

    Steer                   int8

    Brake                   uint8

    Clutch                  uint8

    Gear                    int8

    EngineRPM               uint16

    DRS                     uint8

    RevLightsPercent        uint8

    BrakesTemperature       [4]uint16

    TyresSurfaceTemperature [4]uint16

    TyresInnerTemperature   [4]uint16

    EngineTemperature       uint16

    TyresPressure           [4]float32

}

对于实际的解组,我正在这样做 -


func decodePayload(dataStruct interface{}, payload []byte) {

    dataReader := bytes.NewReader(payload[:])

    binary.Read(dataReader, binary.LittleEndian, dataStruct)

}


payload := make([]byte, 2048)

s.conn.ReadFromUDP(payload[:])

telemetryData := &data.CarTelemetryData{}

s.PacketsRcvd += 1

decodePayload(telemetryData, payload)

我怀疑这是因为数据类型不等效,并且在将字节读入 Go 数据类型时存在一些转换问题,而它们最初是作为 C++ 打包的。我该如何处理?


注意:我无法控制发送的数据,这是由第三方服务发送的。


森栏
浏览 104回答 1
1回答

动漫人物

您面临的问题与结构成员的对齐方式有关。您可以在此处阅读有关它的更多信息,但简而言之,C++ 编译器有时会添加填充字节以保持架构所期望的自然对齐。如果不使用该对齐方式,可能会导致性能下降甚至访问冲突。例如,对于 x86/x64,大多数类型的对齐方式通常(但不一定保证)与大小相同。我们可以看到#include <cstdint>#include <type_traits>std::size_t offsets[] = {&nbsp; &nbsp; std::alignment_of_v<std::uint8_t>,&nbsp; &nbsp; std::alignment_of_v<std::uint16_t>,&nbsp; &nbsp; std::alignment_of_v<std::uint32_t>,&nbsp; &nbsp; std::alignment_of_v<std::uint64_t>,&nbsp; &nbsp; std::alignment_of_v<__uint128_t>,&nbsp; &nbsp; std::alignment_of_v<std::int8_t>,&nbsp; &nbsp; std::alignment_of_v<std::int16_t>,&nbsp; &nbsp; std::alignment_of_v<std::int32_t>,&nbsp; &nbsp; std::alignment_of_v<std::int64_t>,&nbsp; &nbsp; std::alignment_of_v<__int128_t>,&nbsp; &nbsp; std::alignment_of_v<float>,&nbsp; &nbsp; std::alignment_of_v<double>,&nbsp; &nbsp; std::alignment_of_v<long double>,&nbsp; &nbsp; std::alignment_of_v<void*>,};编译为offsets:&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;1&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;2&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;4&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;8&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;16&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;1&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;2&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;4&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;8&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;16&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;4&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;8&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;16&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;8由于这些(和其他)实现细节,建议不要依赖内部表示。但是,在某些情况下,其他方法可能不够快(例如逐字段序列化),或者您可能无法更改 C++ 代码,例如 OP。binary.Read需要打包数据,但 C++ 将使用填充。我们需要使用依赖于编译器的指令,例如#pragma pack(1)或添加填充 Go 结构。第一个不是 OP 的选项,所以我们将使用第二个。我们可以使用offsetof宏来确定结构成员相对于结构本身的偏移量。我们可以做类似的事情#include <array>#include <cstddef>#include <cstdint>using int8 = std::int8_t;using uint8 = std::uint8_t;using uint16 = std::uint16_t;struct CarTelemetryData {&nbsp; &nbsp; uint16 m_speed;&nbsp; &nbsp; uint8 m_throttle;&nbsp; &nbsp; int8 m_steer;&nbsp; &nbsp; uint8 m_brake;&nbsp; &nbsp; uint8 m_clutch;&nbsp; &nbsp; int8 m_gear;&nbsp; &nbsp; uint16 m_engineRPM;&nbsp; &nbsp; uint8 m_drs;&nbsp; &nbsp; uint8 m_revLightsPercent;&nbsp; &nbsp; uint16 m_brakesTemperature[4];&nbsp; &nbsp; uint16 m_tyresSurfaceTemperature[4];&nbsp; &nbsp; uint16 m_tyresInnerTemperature[4];&nbsp; &nbsp; uint16 m_engineTemperature;&nbsp; &nbsp; float m_tyresPressure[4];};// C++ has no reflection (yet) so we need to list every memberconstexpr auto offsets = std::array{&nbsp; &nbsp; offsetof(CarTelemetryData, m_speed),&nbsp; &nbsp; offsetof(CarTelemetryData, m_throttle),&nbsp; &nbsp; offsetof(CarTelemetryData, m_steer),&nbsp; &nbsp; offsetof(CarTelemetryData, m_brake),&nbsp; &nbsp; offsetof(CarTelemetryData, m_clutch),&nbsp; &nbsp; offsetof(CarTelemetryData, m_gear),&nbsp; &nbsp; offsetof(CarTelemetryData, m_engineRPM),&nbsp; &nbsp; offsetof(CarTelemetryData, m_drs),&nbsp; &nbsp; offsetof(CarTelemetryData, m_revLightsPercent),&nbsp; &nbsp; offsetof(CarTelemetryData, m_brakesTemperature),&nbsp; &nbsp; offsetof(CarTelemetryData, m_tyresSurfaceTemperature),&nbsp; &nbsp; offsetof(CarTelemetryData, m_tyresInnerTemperature),&nbsp; &nbsp; offsetof(CarTelemetryData, m_engineTemperature),&nbsp; &nbsp; offsetof(CarTelemetryData, m_tyresPressure),};constexpr auto sizes = std::array{&nbsp; &nbsp; sizeof(CarTelemetryData::m_speed),&nbsp; &nbsp; sizeof(CarTelemetryData::m_throttle),&nbsp; &nbsp; sizeof(CarTelemetryData::m_steer),&nbsp; &nbsp; sizeof(CarTelemetryData::m_brake),&nbsp; &nbsp; sizeof(CarTelemetryData::m_clutch),&nbsp; &nbsp; sizeof(CarTelemetryData::m_gear),&nbsp; &nbsp; sizeof(CarTelemetryData::m_engineRPM),&nbsp; &nbsp; sizeof(CarTelemetryData::m_drs),&nbsp; &nbsp; sizeof(CarTelemetryData::m_revLightsPercent),&nbsp; &nbsp; sizeof(CarTelemetryData::m_brakesTemperature),&nbsp; &nbsp; sizeof(CarTelemetryData::m_tyresSurfaceTemperature),&nbsp; &nbsp; sizeof(CarTelemetryData::m_tyresInnerTemperature),&nbsp; &nbsp; sizeof(CarTelemetryData::m_engineTemperature),&nbsp; &nbsp; sizeof(CarTelemetryData::m_tyresPressure),};constexpr auto computePadding() {&nbsp; &nbsp; std::array<std::size_t, offsets.size()> result;&nbsp; &nbsp; std::size_t expectedOffset = 0;&nbsp; &nbsp; for (std::size_t i = 0; i < offsets.size(); i++) {&nbsp; &nbsp; &nbsp; &nbsp; result.at(i) = offsets.at(i) - expectedOffset;&nbsp; &nbsp; &nbsp; &nbsp; expectedOffset = offsets.at(i) + sizes.at(i);&nbsp; &nbsp; }&nbsp; &nbsp; return result;}auto padding = computePadding();编译为(constexprFTW)padding:&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;1&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; .quad&nbsp; &nbsp;2所以,在 x86 上,我们需要一个字节之前EngineRPM和两个字节之前TyresPressure。所以,让我们检查一下它是否有效。C++:#include <cstddef>#include <cstdint>#include <iomanip>#include <iostream>#include <span>using int8 = std::int8_t;using uint8 = std::uint8_t;using uint16 = std::uint16_t;struct CarTelemetryData {&nbsp; &nbsp; uint16 m_speed;&nbsp; &nbsp; uint8 m_throttle;&nbsp; &nbsp; int8 m_steer;&nbsp; &nbsp; uint8 m_brake;&nbsp; &nbsp; uint8 m_clutch;&nbsp; &nbsp; int8 m_gear;&nbsp; &nbsp; uint16 m_engineRPM;&nbsp; &nbsp; uint8 m_drs;&nbsp; &nbsp; uint8 m_revLightsPercent;&nbsp; &nbsp; uint16 m_brakesTemperature[4];&nbsp; &nbsp; uint16 m_tyresSurfaceTemperature[4];&nbsp; &nbsp; uint16 m_tyresInnerTemperature[4];&nbsp; &nbsp; uint16 m_engineTemperature;&nbsp; &nbsp; float m_tyresPressure[4];};int main() {&nbsp; &nbsp; CarTelemetryData data = {&nbsp; &nbsp; &nbsp; &nbsp; .m_speed = 1,&nbsp; &nbsp; &nbsp; &nbsp; .m_throttle = 2,&nbsp; &nbsp; &nbsp; &nbsp; .m_steer = 3,&nbsp; &nbsp; &nbsp; &nbsp; .m_brake = 4,&nbsp; &nbsp; &nbsp; &nbsp; .m_clutch = 5,&nbsp; &nbsp; &nbsp; &nbsp; .m_gear = 6,&nbsp; &nbsp; &nbsp; &nbsp; .m_engineRPM = 7,&nbsp; &nbsp; &nbsp; &nbsp; .m_drs = 8,&nbsp; &nbsp; &nbsp; &nbsp; .m_revLightsPercent = 9,&nbsp; &nbsp; &nbsp; &nbsp; .m_brakesTemperature = {10, 11, 12, 13},&nbsp; &nbsp; &nbsp; &nbsp; .m_tyresSurfaceTemperature = {14, 15, 16, 17},&nbsp; &nbsp; &nbsp; &nbsp; .m_tyresInnerTemperature = {18, 19, 20, 21},&nbsp; &nbsp; &nbsp; &nbsp; .m_engineTemperature = 22,&nbsp; &nbsp; &nbsp; &nbsp; .m_tyresPressure = {23, 24, 25, 26},&nbsp; &nbsp; };&nbsp; &nbsp; std::cout << "b := []byte{" << std::hex << std::setfill('0');&nbsp; &nbsp; for (auto byte : std::as_bytes(std::span(&data, 1))) {&nbsp; &nbsp; &nbsp; &nbsp; std::cout << "0x" << std::setw(2) << static_cast<unsigned>(byte)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; << ", ";&nbsp; &nbsp; }&nbsp; &nbsp; std::cout << "}";}结果是b := []byte{0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x07, 0x00, 0x08, 0x09, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x41, 0x00, 0x00, 0xc0, 0x41, 0x00, 0x00, 0xc8, 0x41, 0x00, 0x00, 0xd0, 0x41, }让我们在 Go 中使用它:// Type your code here, or load an example.// Your function name should start with a capital letter.package mainimport (&nbsp; &nbsp; "bytes"&nbsp; &nbsp; "encoding/binary"&nbsp; &nbsp; "fmt")type CarTelemetryData struct {&nbsp; &nbsp; Speed&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint16&nbsp; &nbsp; Throttle&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uint8&nbsp; &nbsp; Steer&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;int8&nbsp; &nbsp; Brake&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint8&nbsp; &nbsp; Clutch&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uint8&nbsp; &nbsp; Gear&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int8&nbsp; &nbsp; _&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint8&nbsp; &nbsp; EngineRPM&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint16&nbsp; &nbsp; DRS&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint8&nbsp; &nbsp; RevLightsPercent&nbsp; &nbsp; &nbsp; &nbsp; uint8&nbsp; &nbsp; BrakesTemperature&nbsp; &nbsp; &nbsp; &nbsp;[4]uint16&nbsp; &nbsp; TyresSurfaceTemperature [4]uint16&nbsp; &nbsp; TyresInnerTemperature&nbsp; &nbsp;[4]uint16&nbsp; &nbsp; EngineTemperature&nbsp; &nbsp; &nbsp; &nbsp;uint16&nbsp; &nbsp; _&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint16&nbsp; &nbsp; TyresPressure&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[4]float32}func main() {&nbsp; &nbsp; b := []byte{0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x07, 0x00, 0x08, 0x09, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x41, 0x00, 0x00, 0xc0, 0x41, 0x00, 0x00, 0xc8, 0x41, 0x00, 0x00, 0xd0, 0x41}&nbsp; &nbsp; var dataStruct CarTelemetryData&nbsp; &nbsp; dataReader := bytes.NewReader(b[:])&nbsp; &nbsp; binary.Read(dataReader, binary.LittleEndian, &dataStruct)&nbsp; &nbsp; fmt.Printf("%+v", dataStruct)}哪个打印{Speed:1 Throttle:2 Steer:3 Brake:4 Clutch:5 Gear:6 _:0 EngineRPM:7 DRS:8 RevLightsPercent:9 BrakesTemperature:[10 11 12 13] TyresSurfaceTemperature:[14 15 16 17] TyresInnerTemperature:[18 19 20 21] EngineTemperature:22 _:0 TyresPressure:[23 24 25 26]}取出填充字节,它会失败。
随时随地看视频慕课网APP

相关分类

Go
我要回答