2016-04-26 6 views
0

Ich benutze Protobuf für Golang. Protobuf generiert Nachrichtentypen, bei denen der Typzeiger proto.Message() implementiert. z.B.Cast boxed struct zu boxed pointer - golang

func (*SomeMessage) Message() {} 

Die protobuf lib haben Methoden wie Marshal(proto.Message)

Nun zu meiner eigentlichen Frage.

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 
    } 
} 

Das obige funktioniert gut. Wenn ich die Nachricht jedoch nicht als Zeiger übergebe, stimmt der Code in SendMessage nicht überein, da die Schnittstelle nur im SomeMessage-Zeiger und nicht im Wert implementiert ist.

Was ich mag würde zu tun ist:

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 
    } 
} 

bevorzugt würde ich beide in der Lage sein mag als Zeiger übergeben und als Wert. Der Grund, warum ich nach Wert zu übergeben will, ist, dass dies als eine schlechte mans Isolation wirken kann, wenn Nachrichten über goroutines/Threads usw. (in Mangel an Unveränderlichkeit) diesen

alle vorbei wahrscheinlich vermieden werden kann, wenn die Protobuf Generator generierte erlaubte Werte, die wie proto.Message() behandelt werden. Oder wenn es eine bessere Möglichkeit gäbe, unveränderliche Nachrichten zu machen.

Es ist nicht super wichtig, wenn sie möglich, cool, wenn sie nicht, meh :-)

[EDIT]

Wenn ich die reflect.Type der Nachricht und die reflect.Type von der Zeigertyp der Nachricht Ist es irgendwie möglich, eine Instanz des Zeigertyps zu erstellen, der mit "reflect" auf den Wert zeigt?

Antwort

1

Normalerweise können Sie die Adresse eines Werts nicht annehmen, was bedeutet, dass Sie die Schnittstelle {} nicht einfach in einen Zeiger konvertieren können, um die Anforderungen von Protobuf zu erfüllen.

Das heißt, Sie können einen neuen Zeiger dynamisch erstellen, kopieren Sie dann den Wert in das, dann übergeben Sie den neu zugewiesenen Zeiger auf Protobuf.

ein Hier example on Play

Die Wert -> Zeiger Umwandlung ist:

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() 
} 
  • wir zuerst sehen, ob es ein Zeiger ist, wenn ja, nur den Wert zurück.
  • Dann überprüfen wir, ob es adressierbar ist und geben das zurück.
  • Zuletzt erstellen wir eine neue Instanz des Typs und kopieren den Inhalt darauf und geben ihn zurück.

Da dies die Daten kopiert, ist es möglicherweise nicht für Ihre Zwecke geeignet. Es hängt alles von der Größe der Nachricht und der erwarteten Aufrufrate mit einem Wert ab (da dies mehr Müll erzeugt).

+0

Das funktioniert, danke! –

+0

@RogerAlsing Ich möchte darauf hinweisen, dass es dabei zwar extrem langsam und überentwickelt ist und in keinem realen Projekt verwendet werden sollte. – OneOfOne

+0

Ja, ich bekomme das, die beste Lösung wäre, wenn ich ein Protokoll erzeugen würde, das unveränderliche Strukturen erzeugt. Aber es war immer noch interessant zu sehen, dass es möglich war. –

Verwandte Themen