Эквивалент C ++ reinterpret_cast void * для структуры в Голанге - PullRequest
0 голосов
/ 28 сентября 2018

В C ++ вы можете читать данные из дескриптора FILE и просто повторно интерпретировать их в структуру для интерпретации данных.

Есть ли эквивалентный способ сделать это в Go?

Какочень надуманный пример, рассмотрим следующее, где «ProcessBytes» - это просто обратный вызов, в котором вам дается массив байтов, к которым непрерывно добавляются при чтении из файла.

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 ]

0 голосов
/ 28 сентября 2018

Прямо сейчас ответы предлагают unsafe, но они не обсуждают, почему вы не должны использовать unsafe и что вы должны делать вместо этого.Итак, позвольте мне попробовать.

В коде C ++, который вы разместили в OP, вы, кажется, пишете своего рода двоичный формат, который читает данные посредством простого преобразования.Простой, но эффективный.Единственная очевидная проблема, которую я вижу, состоит в том, что она не позволяет взаимодействовать между Little Endian и Big Endian, но это другой вопрос.

Способ, которым вы подходите к двоичному кодированию в Go, заключается в использовании удобного пакета encoding/binary, который способен декодировать двоичные данные непосредственно в структуры фиксированного размера (т. Е. Без строк или секций, которые имеют переменную длину и поэтому длина должна быть закодирована произвольно).

Вот какЯ бы реализовал ваш пример в 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\nMessage: %s\n",
            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
}

Детская площадка

Итак, вот несколько вещей, на которые стоит обратить внимание:

  • В Go двоичные форматы, как правило, НИКОГДА не реализуются при чтении непосредственно из памяти.Это потому, что 1. это зависит от платформы (Little / Big endian), 2. проблемы со строками, слайсами и заполнением структур и 3. это, ну, в общем, небезопасно.Если вы не вмешиваетесь непосредственно в память, Go может в значительной степени гарантировать, что ваша программа будет работать на любой платформе без каких-либо изменений.В тот момент, когда вы начинаете это делать, вы теряете эту гарантию.
  • Нам не нужно «продвигать указатель» данных, которые мы читаем, - мы передаем binary.Read io.ReaderЭто означает, что когда мы читаем что-то из него, считанные данные отбрасываются, поэтому указатель автоматически продвигается.
  • При работе с памятью с самим собой возможны последствия, когда ГК может думать, что точкав данных больше нет ссылок и их можно свободно использовать - тогда как на самом деле вы все еще используете их, просто не четко ссылаетесь с помощью собственного указателя Go.
0 голосов
/ 28 сентября 2018

Вы можете, но не должны, потому что это небезопасно.Голанг не определяет модель памяти (как я знаю) struct, она может меняться между версиями Голанга.У маршала / анмаршала это обычный путь.

package main

import (
    "fmt"
    "unsafe"
)

type mys struct{
    a int
}

func main() {
    v := mys{0x1234abcd}
    unsafeArrayP := (*[unsafe.Sizeof(v)]byte)(unsafe.Pointer(&v))
    for i:=0;i<4;i++ {
        fmt.Printf("%x,", unsafeArrayP[i])
    }

    v2 := 0x1234abcd
    v3 := *((*mys)(unsafe.Pointer(&v2)))
    fmt.Println(v == v3)
}

печать cd,ab,34,12,true

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...