douyue2313 2018-09-28 02:03
浏览 116
已采纳

等效于C ++ reinterpret_cast void *到Golang中的结构

In C++ you can read in data from a FILE descriptor and simply reinterpret_cast it into a structure to interpret the data.

Is there an equivalent way to do this in Go?

As a very contrived example, consider the following where "ProcessBytes" is simply a callback in which you are given an array of bytes that are continuously appended to when reading from a file.

struct PayloadHeader {
  uint32_t TotalPayloadLength; 
  uint8_t  PayloadType;
};

struct TextMessage {
  PayloadHeader Header;
  uint32_t      SenderId;
  uint32_t      RecieverId;
  char          Text[64]; // null padded
};

void ProcessBytes(const uint8_t* data, size_t dataLength) {
  if(dataLength < sizeof(PayloadHeader))
    return;

  const PayloadHeader* header = reinterpret_cast<const PayloadHeader*>(data);
  if(header.PayloadType == TEXT_MESSAGE) {
    if(header.TotalLength != sizeof(TextMessage))
      return;
    const TextMessage* text = reinterpret_cast<const TextMessage*>(data);
    // Do something with the text message~

    // Adjust the *data* to 'erase' the bytes after we are done processing it
    // as a TextMessage
  }
}
  • 写回答

2条回答 默认 最新

  • dsrnwngq411594 2018-09-28 12:40
    关注

    Right now answers are suggesting unsafe, but they are not going over why you should not use unsafe and what you should do instead. So let me give it a try.

    In the C++ code you posted in OP, you seem to be writing a sort of binary format, which reads data through simple casting. Simple, but effective. The only obvious problem I can see is that it does not allow interoperability between Little Endian and Big Endian, but that's another matter.

    The way you would approach binary encoding in Go is through the use of the handy package encoding/binary, which is able to decode binary data directly into fixed-size structs (ie. without strings or slices, which are variable-length and so the length would need to be encoded arbitrarily).

    Here is how I would implement your example in Go:

    package main
    
    import (
        "bytes"
        "encoding/binary"
        "fmt"
    )
    
    const textMessage = 11
    
    func main() {
        // Set up our data - this is an example of the data I
        // imagine you want to decode.
        r := bytes.NewReader([]byte{
            // Header
            byte(textMessageLen + headerLen), 0, 0, 0,
            11,
    
            // Body
            137, 0, 0, 0,
            117, 0, 0, 0,
            // Message content
            'H', 'e', 'l', 'l', 'o', '!', 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0, 0, 0, 0, 0, 0, 
        })
    
        // We first read the header to decide what to do next.
        // Notice that we explicitly pass an argument that indicates to
        // parse integers using little endian, making this code portable.
        var h Header
        err := binary.Read(r, binary.LittleEndian, &h)
        if err != nil {
            fmt.Println(err)
            return
        }
    
    
        switch h.Type {
        case textMessage:
            // It's a text message - make sure the length is right.
            if textMessageLen != (int(h.Length) - headerLen) {
                fmt.Println("Invalid payload length")
                return
            }
    
            // Decode the data
            var t TextMessage
            err = binary.Read(r, binary.LittleEndian, &t)
            if err != nil {
                fmt.Println(err)
                return
            }
    
            // Print it out
            fmt.Printf("Sender: %d; Receiver: %d
    Message: %s
    ",
                t.Sender, t.Receiver, bytes.TrimRight(t.Text[:], "\x00"))
        default:
            fmt.Println("unknown payload type")
        }
    }
    
    // If you need to find out what the encoded size of a struct is, don't use unsafe.Sizeof;
    // use binary.Size instead.
    var headerLen = binary.Size(Header{})
    
    type Header struct {
        Length uint32
        Type   uint8
    }
    
    var textMessageLen = binary.Size(TextMessage{})
    
    type TextMessage struct {
        Sender, Receiver uint32
        Text             [64]byte
    }
    

    <kbd>Playground</kbd>

    So, here are a few things to note:

    • In Go, binary formats are usually NEVER implemented reading directly from memory. This is because 1. it's platform-dependent (Little/Big endian), 2. trouble with strings, slices and struct paddings and 3. it's, well, unsafe. If you don't tamper with memory directly, Go can pretty much guarantee that your program will run smoothly on any platform without any modifications. The moment you start doing that, you lose that guarantee.
    • We don't need to "advance the pointer" of the data we're reading - we're passing down to binary.Read an io.Reader, which means that when we read something from it the data read is discarded, so the pointer is automatically advanced.
    • There are possible implications with the GC when playing with memory with yourself - the GC might think that a point in the data is no longer referenced and free to be used - whereas really you're still using it, just not clearly referencing using a native Go pointer.
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器