将封装的结构体转换为封装的指针 – golang

huangapple go评论90阅读模式
英文:

Cast boxed struct to boxed pointer - golang

问题

我正在使用Protobuf来编写Golang代码。
Protobuf会生成实现proto.Message()的类型指针的消息类型。
例如:

func (*SomeMessage) Message() {}

Protobuf库中有像Marshal(proto.Message)这样的方法。

现在是我真正的问题。

message := SomeMessage {}
SendMessage(&message)

func SendMessage(message interface{}) {
   switch msg := message.(type) {
      case proto.Message:
          //发送到网络或其他地方
      default:
          //非proto消息,抛出异常或其他操作
   }
}

上述代码运行良好。
然而,如果我不将消息作为指针传递,那么在SendMessage函数中的代码将无法匹配,因为接口只在SomeMessage指针上实现,而不是在值上实现。

我想要做的是:

message := SomeMessage {}
SendMessage(message) //按值传递
//在我的真实代码中还有更多的内容,但只展示相关部分

func SendMessage(message interface{}) {
   //将指针和值都匹配为proto.Message
   //然后将值转换为指针,以便其他函数或protobuf可以使用

   message = MagicallyTurnBoxedValueIntoBoxedStruct(message)       

   switch msg := message.(type) {
      case proto.Message:
          //发送到网络或其他地方
      default:
          //非proto消息,抛出异常或其他操作
   }
}

最好能够同时传递指针和值。
我之所以想按值传递,是因为这可以作为在goroutine/线程之间传递消息时的一种简单的隔离方式(缺乏不可变性)。

如果Protobuf生成器允许将值视为proto.Message(),或者有一种更好的方法来处理不可变消息,那么所有这些可能都可以避免。

这并不是非常重要,如果可以实现就很好,如果不能,也没关系 将封装的结构体转换为封装的指针 – golang

[编辑]

如果我有消息的reflect.Type和消息指针类型的reflect.Type,
是否可以使用"reflect"创建一个指向该值的指针类型的实例?

英文:

I'm using Protobuf for Golang.
Protobuf generates message types where type pointer implements proto.Message().
e.g.

func (*SomeMessage) Message() {}

The protobuf lib have methods like Marshal(proto.Message)

Now to my actual issue.

message := SomeMessage {}
SendMessage(&message)

func SendMessage(message interface{}) {
   switch msg := message.(type) {
      case proto.Message:
          //send across the wire or whatever
      default:
          //non proto message, panic or whatever
   }
}

The above works fine.
However, If I don't pass the message as a pointer, then the code in SendMessage will not match, as the interface is only implemented on the SomeMessage pointer, not on the value.

What I would like to do is:

message := SomeMessage {}
SendMessage(message) //pass by value
//there are more stuff going on in my real code, but just trying to show the relevant parts

func SendMessage(message interface{}) {
   //match both pointer and value as proto.Message
   //and then turn the value into a pointer so that
   //other funcs or protobuf can consume it   

   message = MagicallyTurnBoxedValueIntoBoxedStruct(message)       

   switch msg := message.(type) {
      case proto.Message:
          //send across the wire or whatever
      default:
          //non proto message, panic or whatever
   }
}

preferably I'd like to be able to pass both as pointer and as value.
The reason why I want to pass by value, is that this can act as a poor mans isolation when passing messages across goroutines/threads etc.
(in lack of immutability)

All of this could probably be avoided if the protobuf generator generated allowed values to be treated as proto.Message() too.
Or if there was some nicer way to do immutable messages.

It's not super important,if its possible, cool, if its not, meh 将封装的结构体转换为封装的指针 – golang

[EDIT]

If I have the reflect.Type of the message and the reflect.Type of the pointer type of the message.
Is it somehow possible to create an instance of the pointer type pointing to the value using "reflect" ?

答案1

得分: 1

通常情况下,你不能获取一个值的地址,这意味着你不能简单地将interface{}转换为指针来满足Protobuf的要求。

不过,你可以动态创建一个新的指针,然后将值复制到该指针中,然后将新分配的指针传递给Protobuf。

这里有一个示例代码

值到指针的转换如下:

func mkPointer(i interface{}) interface{} {
    val := reflect.ValueOf(i)
    if val.Kind() == reflect.Ptr {
        return i
    }
    if val.CanAddr() {
        return val.Addr().Interface()
    }
    nv := reflect.New(reflect.TypeOf(i))
    nv.Elem().Set(val)
    return nv.Interface()
}
  • 首先,我们检查它是否是一个指针,如果是,直接返回该值。
  • 然后,我们检查它是否可寻址,并返回其地址。
  • 最后,我们创建一个该类型的新实例,并将内容复制到其中,然后返回它。

由于这种方法会复制数据,对于你的目的可能不太实用。这将完全取决于消息的大小和对使用值调用的预期频率(因为这将生成更多的垃圾)。

英文:

Normally, you can't take the address of a value which means you can't simply convert the interface{} to a pointer to satisfy Protobuf's requirement.

That said, you can dynamically create a new pointer then copy the value in to that then pass the newly allocated pointer to protobuf.

Here's an example on Play

The value -> pointer conversion is:

func mkPointer(i interface{}) interface{} {
	val := reflect.ValueOf(i)
	if val.Kind() == reflect.Ptr {
		return i
	}
	if val.CanAddr() {
		return val.Addr().Interface()
	}
	nv := reflect.New(reflect.TypeOf(i))
	nv.Elem().Set(val)
	return nv.Interface()
}
  • We first see if it's a pointer, if so, just return the value.
  • Then we check to see if it's addressable and return that.
  • Lastly, we make a new instance of the type and copy the contents to that and return it.

Since this this copies the data, it may not be practical for your purposes. It will all depend on size of message and expected rate of calling with a value (as that will generate more garbage).

huangapple
  • 本文由 发表于 2016年4月26日 20:04:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/36864450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定