使い始めてまだやる気があるので今日も文章をかいてしまった。

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 だけでもいいかな。そういえば。まぁいいか