Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: use the type's own Clone method #11

Closed
molon opened this issue Feb 24, 2023 · 7 comments
Closed

Suggestion: use the type's own Clone method #11

molon opened this issue Feb 24, 2023 · 7 comments
Labels
enhancement New feature or request

Comments

@molon
Copy link

molon commented Feb 24, 2023

example:
*tls.Config Clone method will do some thread-safe processing internally

@molon
Copy link
Author

molon commented Feb 24, 2023

Might be better to use SetCustomFunc

@molon molon closed this as completed Feb 24, 2023
@molon
Copy link
Author

molon commented Feb 24, 2023

Might be better to use SetCustomFunc

英文不行,中文说吧。

又发现目前SetCustomFunc不算很灵活。
例如只想单独修改一些很大的结构体里某一项的Clone行为,会比较倾向于当前类型的原Clone行为先执行。

// 例如这段改成这样
func (st *structType) Copy(src, nv reflect.Value) {
	origFn := func() {
		ptr := unsafe.Pointer(nv.Pointer())
		shadowCopy(src, ptr)
	}
	
	if st.fn != nil {
		dst := nv.Elem()
		if !src.CanInterface() {
			src = forceClearROFlag(src)
		}

		st.fn(origFn, src, dst)
		return
	}

	origFn()
}

@molon molon reopened this Feb 24, 2023
@huandu
Copy link
Owner

huandu commented Feb 25, 2023

我理解你的意思是说,类似 tls.Config 这种类型,你想直接使用它自己的 Clone()。这种功能其实很容易用 SetCustomFunc 实现,下面是参考代码。

SetCustomFunc(reflect.TypeOf(tls.Config{}), func(allocator *Allocator, old, new reflect.Value) {
	// As tls.Config must be used as a pointer, it's quite unlikely to be unaddressable.
	if !old.CanAddr() {
		panic("tls.Config must be used as a pointer")
	}

	// Convert old to *tls.Config.
	oldConfig := old.Addr().Interface().(*tls.Config)

	// Call tls.Config's Clone() to create a copy of old.
	newConfig := oldConfig.Clone()

	// Copy the newly created cloned value to new.
	// We don't need to worry about copying mutex inside tls.Config.
	// It's safe to copy a zero mutex as it's just a zero value without any side effect.
	new.Set(reflect.ValueOf(newConfig).Elem())
})

@huandu huandu added the question Further information is requested label Feb 25, 2023
@molon
Copy link
Author

molon commented Feb 27, 2023

多谢回复,这个问题在2楼我已经意识到了,确实通过SetCustomFunc来实现更合适。

现在比较关心的是例如有一个结构体里有巨多的字段,且部分字段的类型实现了SetCustomFunc
例如

type M struct {
       // ... balabalabala,假装有很多其他字段
       TLSConfig *tls.Config // 实现了`SetCustomFunc`
       // ... balabalabala,假装有很多其他实现了`SetCustomFunc`的字段
       Name string
}

然后我想对这个结构体类型定义其自身的SetCustomFunc,只想实现给Name字段加上前缀,并且想保留其他字段类型自身已设置的SetCustomFunc特性,例如此例中TLSConfig

那这个结构体类型的SetCustomFunc实现就会比较麻烦,需要一个字段一个字段的设置,且原字段类型的CustomFunc都需要搞一遍。

所以希望能将

origFn := func() {
		ptr := unsafe.Pointer(nv.Pointer())
		shadowCopy(src, ptr)
	}

透出给SetCustomFunc方法,甚至 public 所有类型设置过的 CustomFunc供外界调用。

@huandu
Copy link
Owner

huandu commented Feb 27, 2023

有一种 hack 的方法能够简单的实现,下面是样例代码。基本原理是,将 old 强制转成另一种类型,这样就能简单的调用 allocator.Clone 来完成工作了。这个我考虑下,或许可以加个补丁,让 allocator.Clone 在这种情况下允许调用而不产生递归。

func ExampleSetCustomFunc_partialClone() {
	type T1 struct {
		Value int
	}

	type T2 struct {
		Value int
	}

	type MyStruct struct {
		S1 *T1
		S2 *T2
		S3 string
	}
	type MyStructAlias MyStruct
	var typeOfMyStruct = reflect.TypeOf(MyStruct{})
	var typeOfMyStructAlias = reflect.TypeOf(MyStructAlias{})

	SetCustomFunc(reflect.TypeOf(T1{}), func(allocator *Allocator, old, new reflect.Value) {
		oldField := old.FieldByName("Value")
		newField := new.FieldByName("Value")
		newField.SetInt(oldField.Int() + 100)
	})

	SetCustomFunc(reflect.TypeOf(T2{}), func(allocator *Allocator, old, new reflect.Value) {
		oldField := old.FieldByName("Value")
		newField := new.FieldByName("Value")
		newField.SetInt(oldField.Int() + 100)
	})

	SetCustomFunc(reflect.TypeOf(MyStruct{}), func(allocator *Allocator, old, new reflect.Value) {
		old = old.Convert(typeOfMyStructAlias)
		new.Set(allocator.Clone(old).Convert(typeOfMyStruct))

		field := new.FieldByName("S3")
		field.SetString(field.String() + "_suffix")
	})

	st := &MyStruct{
		S1: &T1{
			Value: 1,
		},
		S2: &T2{
			Value: 2,
		},
		S3: "abc",
	}
	cloned := Clone(st).(*MyStruct)
	json, _ := json.Marshal(cloned)
	fmt.Println(string(json))

	// Output:
	// {"S1":{"Value":101},"S2":{"Value":102},"S3":"abc_suffix"}
}

@huandu
Copy link
Owner

huandu commented Feb 27, 2023

提交了一个 PR #12,有了这个之后就能够在 SetCustomFunc 直接调用 allocator.Clone(old) 了,这样就能很方便的先做一次 clone,然后再修改其中某些字段。

@huandu huandu added enhancement New feature or request and removed question Further information is requested labels Feb 27, 2023
@huandu huandu closed this as completed in 6fef9e4 Feb 27, 2023
@molon
Copy link
Author

molon commented Mar 1, 2023

简直太棒了,在这学习到一些东西。 👍👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants