2016-02-10 1 views
0

Je travaille avec protobuf pour créer un système qui supporte un certain nombre de messages différents, et je me base sur les messages dynamiques, l'usine et l'interface de réflexion. Je peux obtenir et définir tous les différents types de données de champ (uint32, int64, ... etc.) sauf le type de chaîne. Lorsque j'essaie d'utiliser l'un des getters ou setters pour un champ avec un type de données string, mon code segfaults. Le premier morceau de code est l'exemple minimal (désolé, c'est long, mais l'importateur, l'usine, etc. doivent être en place pour que cela ait du sens). Le deuxième morceau de code est le fichier proto que j'ai utilisé pour tester. Ma question est de savoir comment accéder au champ de chaîne pour définir ou obtenir les données.Protobuf SetString GetString GetStringReference segfault

#include <vector> 
#include <string> 
#include <sstream> 
#include <dirent.h> 
#include <stdexcept> 
#include <iostream> 

#include <google/protobuf/message.h> 
#include <google/protobuf/descriptor.pb.h> 
#include <google/protobuf/io/zero_copy_stream_impl.h> 
#include <google/protobuf/io/tokenizer.h> 
#include <google/protobuf/compiler/parser.h> 
#include <google/protobuf/descriptor_database.h> 
#include <google/protobuf/dynamic_message.h> 
#include <google/protobuf/compiler/importer.h> 
#include <google/protobuf/repeated_field.h> 
#include <google/protobuf/arena.h> 

class errCollector : public google::protobuf::compiler::MultiFileErrorCollector 
{ 
public: 
    ~errCollector() {}; 
    void AddError(const std::string& filename, 
        int line, int column, const std::string& message) {}; 
}; 

google::protobuf::compiler::Importer* createImporter(); 
void buildSubMessages(const google::protobuf::Descriptor* desc, 
         google::protobuf::Message* msg, const google::protobuf::Reflection* ref, 
         google::protobuf::DynamicMessageFactory* factory); 

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

    // Usage: ProtobufMinimalExample <message type name> 
    std::string msgType(argv[1]); 

    // Error message stream 
    std::ostringstream msgStream; 

    // Create the importer object which holds all of the .proto files. 
    google::protobuf::compiler::Importer* importer = createImporter(); 
    // Create the descriptor pool which holds all of the message descriptors. 
    const google::protobuf::DescriptorPool* pool(importer->pool()); 
    // Create an object to hold the top-level message. 
    google::protobuf::Message* mutableMsg; 
    // Create an object to hold the top-level message descriptor. 
    const google::protobuf::Descriptor* messageDesc; 
    // Create a message to hold the prototype description of the dynamic message 
    const google::protobuf::Message* protoTypeMsg; 
    // Create the message factory. 
    google::protobuf::DynamicMessageFactory* factory(new google::protobuf::DynamicMessageFactory); 

    messageDesc = pool->FindMessageTypeByName(msgType); 
    protoTypeMsg = factory->GetPrototype(messageDesc); 
    mutableMsg = protoTypeMsg->New(); 

    // Get a submessage in the field named "yellow". 
    const google::protobuf::FieldDescriptor* field = messageDesc->FindFieldByName("yellow"); 
    const google::protobuf::Descriptor* subMsgDesc = field->message_type(); 
    const google::protobuf::Message* subProtoTypeMsg = factory->GetPrototype(subMsgDesc); 
    google::protobuf::Message* subMutableMsg = subProtoTypeMsg->New(); 
    const google::protobuf::Reflection* subReflection = (subMutableMsg->GetReflection()); 
    const google::protobuf::Descriptor* subMutableDesc = subMutableMsg->GetDescriptor(); 

    std::string* name = new std::string(); 

    // Segfault 
    //const google::protobuf::FieldDescriptor* subField = subMutableDesc->FindFieldByName("name"); 
    //subReflection->SetString(mutableMsg, subField, "Hello"); 
    //subReflection->GetString(*(mutableMsg), subField); 
    //subReflection->GetStringReference(*(mutableMsg), subField, name); 

    // Works 
    //const google::protobuf::FieldDescriptor* subField = subMutableDesc->FindFieldByName("score"); 
    //subReflection->SetUInt32(mutableMsg, subField, 9); 
    //subReflection->GetUInt32(*(mutableMsg), subField); 

    return 0; 
} 

google::protobuf::compiler::Importer* createImporter() 
{ 
    google::protobuf::compiler::DiskSourceTree srcTree; 
    errCollector error; 
    google::protobuf::compiler::Importer* fdProtoImporter(new google::protobuf::compiler::Importer(&srcTree, &error)); 
    const char* protoFilePath = "./proto"; 

    struct dirent* entry; 
    DIR* dp; 
    std::vector<std::string> importProtoFilenames; 

    dp = opendir(protoFilePath); 

    unsigned char isFile = 0x8; 
    while((entry = readdir(dp))) 
    { 
     if(entry->d_type != isFile) 
      continue; 
     importProtoFilenames.push_back(std::string(entry->d_name)); 
    } 
    closedir(dp); 

    srcTree.MapPath("", protoFilePath); 

    for(auto protoFilename : importProtoFilenames) 
    { 
     if(fdProtoImporter->Import(protoFilename) == NULL) 
     { 
      continue; 
     } 
    } 
    return std::move(fdProtoImporter); 
} 

message SSL_Referee { 

required uint64 packet_timestamp = 1; 

enum Stage { 
    NORMAL_FIRST_HALF_PRE = 0; 
    NORMAL_FIRST_HALF = 1; 
    NORMAL_HALF_TIME = 2; 
    NORMAL_SECOND_HALF_PRE = 3; 
    NORMAL_SECOND_HALF = 4; 
    EXTRA_TIME_BREAK = 5; 
    EXTRA_FIRST_HALF_PRE = 6; 
    EXTRA_FIRST_HALF = 7; 
    EXTRA_HALF_TIME = 8; 
    EXTRA_SECOND_HALF_PRE = 9; 
    EXTRA_SECOND_HALF = 10; 
    PENALTY_SHOOTOUT_BREAK = 11; 
    PENALTY_SHOOTOUT = 12; 
    POST_GAME = 13; 
} 
required Stage stage = 2; 

optional sint32 stage_time_left = 3; 

// These are the "fine" states of play on the field. 
enum Command { 
    // All robots should completely stop moving. 
    HALT = 0; 
    // Robots must keep 50 cm from the ball. 
    STOP = 1; 
    // A prepared kickoff or penalty may now be taken. 
    NORMAL_START = 2; 
    // The ball is dropped and free for either team. 
    FORCE_START = 3; 
    // The yellow team may move into kickoff position. 
    PREPARE_KICKOFF_YELLOW = 4; 
    // The blue team may move into kickoff position. 
    PREPARE_KICKOFF_BLUE = 5; 
    // The yellow team may move into penalty position. 
    PREPARE_PENALTY_YELLOW = 6; 
    // The blue team may move into penalty position. 
    PREPARE_PENALTY_BLUE = 7; 
    // The yellow team may take a direct free kick. 
    DIRECT_FREE_YELLOW = 8; 
    // The blue team may take a direct free kick. 
    DIRECT_FREE_BLUE = 9; 
    // The yellow team may take an indirect free kick. 
    INDIRECT_FREE_YELLOW = 10; 
    // The blue team may take an indirect free kick. 
    INDIRECT_FREE_BLUE = 11; 
    // The yellow team is currently in a timeout. 
    TIMEOUT_YELLOW = 12; 
    // The blue team is currently in a timeout. 
    TIMEOUT_BLUE = 13; 
    // The yellow team just scored a goal. 
    // For information only. 
    // For rules compliance, teams must treat as STOP. 
    GOAL_YELLOW = 14; 
    // The blue team just scored a goal. 
    GOAL_BLUE = 15; 
} 
required Command command = 4; 

// The number of commands issued since startup (mod 2^32). 
required uint32 command_counter = 5; 

// The UNIX timestamp when the command was issued, in microseconds. 
// This value changes only when a new command is issued, not on each packet. 
required uint64 command_timestamp = 6; 

// Information about a single team. 
message TeamInfo { 
    // The team's name (empty string if operator has not typed anything). 
    required string name = 1; 
    // The number of goals scored by the team during normal play and overtime. 
    required uint32 score = 2; 
    // The number of red cards issued to the team since the beginning of the game. 
    required uint32 red_cards = 3; 
    // The amount of time (in microseconds) left on each yellow card issued to the team. 
    // If no yellow cards are issued, this array has no elements. 
    // Otherwise, times are ordered from smallest to largest. 
    repeated uint32 yellow_card_times = 4 [packed=true]; 
    // The total number of yellow cards ever issued to the team. 
    required uint32 yellow_cards = 5; 
    // The number of timeouts this team can still call. 
    // If in a timeout right now, that timeout is excluded. 
    required uint32 timeouts = 6; 
    // The number of microseconds of timeout this team can use. 
    required uint32 timeout_time = 7; 
    // The pattern number of this team's goalie. 
    required uint32 goalie = 8; 
} 

// Information about the two teams. 
required TeamInfo yellow = 7; 
required TeamInfo blue = 8; 
} 
+0

Vous obtenez 'subField' de' subMutableMsg', mais vous essayez de définir la valeur sur 'mutableMsg'. Le champ 'score' ne semble fonctionner que par accident - vous définissez en fait' SSL_Referee.stage' (un champ qui possède la même balise), et les deux champs sont suffisamment compatibles avec les binaires. –

+0

@IgorTandetnik Votre première phrase est correcte mais la seconde ne l'est pas. La réflexion fonctionne sur les objets en mémoire, et non sur les messages codés, de sorte que les numéros de champ et la compatibilité binaire ne sont pas pertinents. Le fait est que modifier une chaîne implique de suivre les pointeurs, alors que modifier une valeur numérique ne modifie pas une chaîne du mauvais type, et risque de segmenter la mémoire en modifiant une valeur numérique du mauvais type. La mémoire corrompt dépend de la disposition en mémoire des classes, pas des numéros de champs. –

Répondre

0
// Segfault 
const google::protobuf::FieldDescriptor* subField = subMutableDesc->FindFieldByName("name"); 
subReflection->SetString(mutableMsg, subField, "Hello"); 
subReflection->GetString(*(mutableMsg), subField); 
subReflection->GetStringReference(*(mutableMsg), subField, name); 

Je crois que vous avez écrit où mutableMsg dans ce code, que vous vouliez dire subMutableMsg. Ainsi, vous essayez de modifier le mauvais message, et le message que vous transmettez a une disposition différente. Par conséquent, lorsque le code de réflexion tente d'accéder à un champ typé, il finit par essayer de suivre un pointeur invalide.

// Works 
const google::protobuf::FieldDescriptor* subField = subMutableDesc->FindFieldByName("score"); 
subReflection->SetUInt32(mutableMsg, subField, 9); 
subReflection->GetUInt32(*(mutableMsg), subField); 

Ce cas ne fonctionne pas, il ne peut pas segmenter. Je crois que ce code va en fait corrompre la mémoire dans mutableMsg, mais parce que vous modifiez un champ primitif simple, le code de réflexion n'essaie pas de suivre les pointeurs, donc ne segfault pas.