Golang (cgo) — произвольный интерфейс void*

Я оборачиваю библиотеку C, имеющую структуру с полем данных void*, которое можно использовать для произвольного хранения данных. Как лучше всего (если это вообще возможно) обернуть это в идиоматический Go?

Структура довольно проста:

typedef struct _Foo {
    void * data;
} Foo;

Я надеялся сделать что-то вроде:

type Foo C.Foo

func (f *Foo) SetData(data interface{}) {
    f.data = unsafe.Pointer(&data)
}

func (f *Foo) Data() interface{} {
    return (interface{})(unsafe.Pointer(f.data))
}

Это не работает, и в любом случае это явно неправильный подход.

Я успешно установил данные void* с полем длины, используя источник []byte, но этот интерфейс без длины ускользает от меня.


person bojo    schedule 11.03.2013    source источник
comment
Для любопытных конечная цель была такой: github.com/boj/goenet - хотя все еще немного глючило , так что будьте осторожны.   -  person bojo    schedule 05.07.2014


Ответы (2)


если вы берете адрес interface, вы берете адрес типа значения (примерно struct { tInfo *typeInfo, payload uintPtr}), а не данные, «упакованные» интерфейсом. Поле полезной нагрузки может содержать реальные данные, если они помещаются в машинное слово, в противном случае это поле содержит указатель на фактическую полезную нагрузку. Но это детали реализации, и с ними не следует работать напрямую, ИМО.

Я бы не стал общим, например (непроверенный код, только схема):

func (f *Foo) SetT(p *T) {
        (*C.Foo)(f).data = unsafe.Pointer(p)
}

и

func (f *Foo) GetT() *T {
        return (*T)((*C.Foo)(f).data)
}
person zzzz    schedule 11.03.2013
comment
Да, это то, что я рассматривал с тех пор, как опубликовал свой вопрос. - person bojo; 11.03.2013
comment
Вы также можете использовать reflect.Elem() и reflect.Pointer(), чтобы получить uintptr к содержащемуся в интерфейсе значению. - person thwd; 12.03.2013
comment
указатель reflect.func (значение): Паника, если тип v равен а не Chan, Func, Map, Ptr, Slice или UnsafePointer. - person zzzz; 12.03.2013

Возвращаясь к этому после еще нескольких месяцев работы с Go, я придумал следующее (обновленное, спасибо, zupa) решение.

func (f *Foo) SetData(data interface{}) {
    f.data = unsafe.Pointer(&data)
}

func (f *Foo) Data() interface{} {
    return unsafe.Pointer(f.data)
}

И может быть использован следующим образом:

type Player struct {
    Name string
}

p := &Player{
    Name: "Player1",
}

f.SetData(p)

log.Print(f.Data().(*Player).Name) // Outputs: Player1
person bojo    schedule 28.12.2013
comment
почему вы должны хранить данные в &FooData{}? - person zupa; 04.07.2014
comment
Некоторое время назад я завершал работу над библиотекой ENet. Вы не обязаны что-либо хранить в поле данных, но клиентская библиотека использует его как механизм для подключения Peer к внутриигровой конструкции, такой как Player — enet.bespin.org/ - person bojo; 05.07.2014
comment
извините, мой вопрос не был ясен. Я не спрашиваю, зачем вам хранить данные; Я спрашиваю, почему вы заключаете data в &FooData{} вместо того, чтобы использовать f.data = unsafe.Pointer(&data) напрямую. - person zupa; 06.07.2014
comment
А, теперь я вижу. Я честно не могу вспомнить, почему это закончилось таким запутанным беспорядком. Отредактировал ответ с правильным кодом. - person bojo; 07.07.2014
comment
Хорошо, ура! Для всех, кто читает это, я спросил себя, читая фрагмент, освободит ли сборщик мусора все, на что указывает unsafe.Pointer. Нет, не будет. Объяснение здесь - person zupa; 07.07.2014
comment
что-то изменилось в golang после этого? Я только что проверил код, и у меня возникает паника при попытке ввести assert f.Data() в проигрыватель: паника: преобразование интерфейса: интерфейс {} небезопасен. Указатель, а не * main.Player. Кажется, не удается ввести assert обратно в указатель Player или даже привести результат к указателю Player с помощью (*Player): невозможно преобразовать f.Data() (интерфейс типа {}) в тип *Player: нужно утверждение типа - person James; 16.04.2016