2013-03-11 3 views
3

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

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

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, но этот интерфейс без длины ускользает от меня.

+0

Для любознательных конечной целью было это - https://github.com/boj/goenet - хотя все еще немного глючит, так берегитесь. – bojo

ответ

2

Возвращаясь к этому после нескольких месяцев 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 
+0

Почему вы должны хранить данные в '& FooData {}'? – zupa

+0

Я обволакивал библиотеку ENet некоторое время назад. Вы не обязаны хранить что-либо в поле данных, но оно существует как механизм для клиентской библиотеки для подключения Peer к встроенной игре, такой как Player - http://enet.bespin.org/structENetPeer. html # a1873959810db7ac7a02da90469ee384e – bojo

+0

извините, мой вопрос был не ясен. Я не спрашиваю, почему вы должны хранить данные; Я спрашиваю, почему вы завершаете 'data' в' & FooData {} 'вместо того, чтобы напрямую использовать' f.data = unsafe.Pointer (& data) '. – zupa

3

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

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

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

и

func (f *Foo) GetT() *T { 
     return (*T)((*C.Foo)(f).data) 
} 
+0

Да, это то, что я рассматривал, так как я разместил свой вопрос. – bojo

+1

Вы также можете использовать 'reflect.Elem()' и 'reflect.Pointer()', чтобы получить 'uintptr' значение, содержащееся в интерфейсе. – thwd

+0

[reflect.func (Value) Pointer] (http://golang.org/pkg/reflect/#Value.Pointer): _ «Паника, если v's Kind не Chan, Func, Map, Ptr, Slice или UnsafePointer . » – zzzz

Смежные вопросы