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条)

报告相同问题?

悬赏问题

  • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!
  • ¥15 永磁直线电机的电流环pi调不出来
  • ¥15 用stata实现聚类的代码
  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效