2017-02-28 1 views
4

Zunächst einmal bin ich nicht sehr erfahren mit C++, also vielleicht beaufsichtige ich etwas hier. Ich versuche, Nachrichten von Proto-Dateien mit dem folgenden Code dynamisch zu generieren protobuf:Dynamisch generieren Protobuf-Nachricht und einen Zeiger darauf zurück

int init_msg(const std::string & filename, protobuf::Arena* arena, protobuf::Message** new_msg){ 
    using namespace google::protobuf; 
    using namespace google::protobuf::compiler; 

    DiskSourceTree source_tree; 
    source_tree.MapPath("file", filename); 

    MuFiErCo error_mist; 
    Importer imp(&source_tree, &error_mist); 

    printf("Lade Datei:%s \n", filename.c_str()); 

    const FileDescriptor* f_desc = imp.Import("file"); 

    const Descriptor* desc = f_desc->FindMessageTypeByName("TestNachricht"); 

    const Message* new_msg_proto = dmf.GetPrototype(desc); 

    *new_msg = new_msg_proto->New(arena); 

    //Debug 
    cout << (*new_msg)->GetTypeName() << endl; 

    return 0; 
} 

int main(int argc, char* argv[]){ 

    protobuf::Arena arena; 

    protobuf::Message *adr2, *adr1; 

    init_msg("schema-1.proto", &arena, &adr1); 
    init_msg("schema-1.proto", &arena, &adr2); 

    printf("MSG_Pointer: %p, %p\n", adr1, adr2); 

    cout << adr1->GetTypeName() << endl; 

    arena.Reset(); 

    return 0; 
}  

Ich dachte, wenn ich Arena verwenden, die neue Nachricht nicht in den Anwendungsbereich der Funktion ist ebenfalls verfügbar. Aber es gibt immer einen segfault, wenn ich versuche, auf die Nachricht zuzugreifen. Ich denke, es ist ein einfacher Fehler, aber ich konnte nicht herausfinden, wie man das löst. Hier

ist der Ausgang:

Lade Datei:schema-1.proto 
packet.TestNachricht 
Lade Datei:schema-1.proto 
packet.TestNachricht 
MSG_Pointer: 0x1b293b0, 0x1b287f0 
Speicherzugriffsfehler (Speicherabzug geschrieben) 
+1

Das Problem, ich glaube, ist, dass 'FileDescriptor' et al, wenn' init_msg' kehrt zerstört werden, ohne die Möglichkeit, die neu erstellte Nachricht zu hinterlassen ihre .proto abzufragen Definition. Sie müssten 'Importer'-Instanz nach' main' verschieben und am Leben erhalten. Das hat nichts mit Arenen zu tun. –

+0

Das war die Lösung, ich werde später ein funktionierendes Codebeispiel bereitstellen. Vielen Dank! – LoopingLouie

Antwort

1

Das Problem, denke ich, ist, dass FileDescriptor et al zerstört werden, wenn init_msg zurückkehrt, so dass die neu erstellte Nachricht mit keiner Weise zu verhören seine .proto Definition. Sie müssen die Importer Instanz in main verschieben und am Leben erhalten. Das hat nichts mit Arenen zu tun. - Igor Tandetnik

Das war die Lösung.

Hier finden Sie einige Arbeitsbeispielcode

#include <string> 
#include <stdio.h> 
#include <stdlib.h> 
#include <iostream> 
#include <memory> 
#include <google/protobuf/descriptor.h> 
#include <google/protobuf/message.h> 
#include <google/protobuf/compiler/importer.h> 
#include <google/protobuf/dynamic_message.h> 
#include <google/protobuf/arena.h> 


using namespace std; 
using namespace google::protobuf; 


class MuFiErCo : public compiler::MultiFileErrorCollector 
{ 
public: 
    void AddError(const string & filename, int line, int column, const string & message){ 
     printf("Err: %s\n", message.c_str()); 
    } 
    void AddWarning(const string & filename, int line, int column, const string & message){ 
     printf("Warn: %s\n", message.c_str()); 
    } 

}; 


compiler::Importer* init_proto_dir(Arena* arena, const std::string &root_dir){ 
    using namespace compiler; 

    static DiskSourceTree source_tree; 
    source_tree.MapPath("", root_dir); 

    static MuFiErCo error_mist; 
    static Importer* imp = Arena::Create<Importer>(arena, &source_tree, &error_mist); 

    return imp; 
} 


void init_proto_def(compiler::Importer* imp, const std::string &proto_file){ 
    using namespace compiler; 

    imp->Import(proto_file); 

    return; 
} 


Message* init_msg(compiler::Importer* imp, Arena* arena, const std::string &msg_name){ 

    const DescriptorPool* pool = imp->pool(); 

    static DynamicMessageFactory dmf; 

    const Descriptor* desc = pool->FindMessageTypeByName(msg_name); 

    const Message* msg_proto = dmf.GetPrototype(desc); 

    return msg_proto->New(arena); 
} 


int set_value(Message* msg, const char* value_name, unsigned long int value){ 
    const Message::Reflection* reflec = msg->GetReflection(); 
    const Descriptor* desc = msg->GetDescriptor(); 

    const FieldDescriptor* fdesc = desc->FindFieldByName(value_name); 

    reflec->SetUInt64(msg, fdesc, value); 

    return 0; 

} 


int main(int argc, char* argv[]){ 

    Arena arena; 

    compiler::Importer* imp = init_proto_dir(&arena, ""); 
    init_proto_def(imp, "schema-1.proto"); 

    Message* msg = init_msg(imp, &arena, "packet.TestNachricht"); 

    set_value(msg, "zahl", 23434); 

    cout << msg->DebugString() << endl; 

    return 0; 
} 
+0

Ich würde es ohne 'statische' Variablen bevorzugen, aber dieser Beispielcode ist hilfreich. Vielen Dank. –

+0

Ich habe eine Weile an diesem Code nicht gearbeitet, aber ich denke, die statischen Variablen kamen aus der Verwendung als DLL für LabView. Fühlen Sie sich frei, ein verbessertes Beispiel zu veröffentlichen. – LoopingLouie