使い始めてまだやる気があるので今日も文章をかいてしまった。
genny でkernel のlist_head スタイルのcontainer/list をつくったの続き。
非常に便利なんだけどそれでも一番大きな欠点としてやはり genny, go generate でコード生成しないといけない。
前の記事かいてたら、ふとおもいついて、できるんじゃね?ってことで github.com/kazu/loncha/lista_encabezado というpackage をつくってみた。
type List interface {
Offset() uintptr
PtrListHead() *ListHead
}
func ElementOf(l List, head *ListHead) unsafe.Pointer {
if head == nil || l == nil {
return nil
}
return unsafe.Pointer(uintptr(unsafe.Pointer(head)) - l.Offset())
}
こう定義されてる. ElementOf は 以下ようなどっかでみたやつだw。 Offset() て関数は、struct からListHead までのオフセットを返す関数を定義しておくと。 それでListHead からstruct のポインタが取れるというやつ。
List interface にそって定義していく
import (
list_head "github.com/kazu/loncha/lista_encabezado"
)
type Element struct {
ID int
Name string
list_head.ListHead
}
func (d *Element) Offset() uintptr {
return unsafe.Offsetof(d.ListHead)
}
func (d *Element) PtrListHead() *list_head.ListHead {
return &(d.ListHead)
}
func PtrElement(ptr unsafe.Pointer) *Element {
return (*Element)(ptr)
}
こんな感じ。でも これだと 実は毎回計算 unsafe.Offsetof とかしてるんで、
return unsafe.Offsetof(&Element{}.ListHead)
のほうがいいかもしれない。
とりあえずその件はおいておく
first := &Element{ID: 123, Name: "first-kun"}
first.Init()
second := &Element{ID: 456, Name: "second-kun"}
second.Init()
first.Add(&second.ListHead)
next := (*Element)(ElementOf(first, first.Next())
fmt.Println(next.Name) // => "second-kun"
go genarete したほうが便利ではあるけど。これでも別に困らない気がするので。さらに被せればいいだけ まぁ悪くないかなぁ。Offset() でとった差分からとる
func ElementOf(l List, head *ListHead) unsafe.Pointer {
if head == nil || l == nil {
return nil
}
return unsafe.Pointer(uintptr(unsafe.Pointer(head)) - l.Offset())
}
んー List interface に必要な関数は Offset だけでもいいかな。そういえば。まぁいいか