12
votes

Itérer sur la structure; Afficher facilement les champs de struct et les valeurs dans une boîte de richedit

est-il un moyen plus facile d'afficher les champs struct et leurs valeurs correspondantes dans richedit contrôle?

C'est ce que je fais maintenant: xxx

etc ...

est-il un moyen plus facile que d'avoir à appeler individuellement chacun d'eux? Je veux lire un fichier binaire, puis afficher la structure correspondante dans un contrôle richedit pour un petit utilitaire que je suis immobilier et n'a trouvé aucun autre moyen. Je sais comment lire des fichiers binaires et lire les valeurs dans le struct déjà.


7 commentaires

Votre problème est-il avec le riche édition ou avec la sortie de la structure confortablement?


La structure ... Ce serait bien s'il y avait un moyen facile de parcourir la structure. À l'heure actuelle, je dois faire référence à chaque champ individuellement et il est gênant d'avoir plus de 100 lignes de code répétitif.


@Roboto: Avoir plus de 100 membres de la structure ressemble à un problème en soi. Peut-être devriez-vous poser une question sur la façon de redéfinir cette classe ...


Il n'y a pas de problème avec la structure. Il a été utilisé depuis plus de 10 ans - aucun problème. La structure contient un fichier de configuration binaire de 4 octets.


@Roboto: Je suis un peu confus. Comment quatre octets peuvent-ils sortir à plus de 100 membres?


Correction: 4 limites d'octets (c'est-à-dire quatre caractères non signés groupés), ou un int, etc .. Le fichier de configuration est d'environ 6 Ko


L'une des questions est que le compilateur connaît les noms des membres de la structure et de l'ordre, mais ne met pas cette information dans l'exécutable; Il n'est pas non plus disponible pour l'utilisateur.


9 Réponses :


2
votes

Je n'utilise pas C ++ Builder, de sorte que certains des détails de ceci sont susceptibles d'être un peu désactivés, mais l'idée générale devrait être au moins raisonnablement proche: xxx

la base L'idée est assez simple: un front-end pour un contrôle Richedit, qui fournit un opérateur modélisé <<. L'opérateur met un élément dans un StringStream pour le convertir en une chaîne. Il reçoit alors la chaîne résultante et l'appendez aux lignes du contrôle. Puisqu'il est modelé, il peut fonctionner avec tous les types habituels supportés par un StringStream.

Cela a des lacunes - sans plus de travail, vous ne pourrez pas utiliser de manipulateurs pour contrôler la mise en forme des données. C'est converti en une chaîne. Étant donné qu'il utilise une chaîne pour convertir des choses aux cordes, il est probablement également un peu plus lent que votre code codant explicitement du type de chaque conversion. Dans le même temps, vous pouvez utiliser un code idiomatique assez propre, simple et idiomatique pour un investissement assez minime.


7 commentaires

C'est bon, mais comment puis-je itération sur la structure pour effectuer ce qui précède?


@ROBOTO: Il n'y a pas grand chose que vous puissiez faire pour éviter d'itération sur les membres de la structure, à moins que ce ne soit du même type, vous pouvez également utiliser des tableaux.


Ils ne sont pas du même type (mélange d'int, uint, de char et de char *)


@Roboto: Dans ce cas, vous pouvez créer quatre tableaux (un de chaque type) puis itérer sur chaque tableau dans une boucle normale ...


Je ne pense pas que cela fonctionnera dans mon scénario ... Y a-t-il un moyen de itération sur une structure?


Non, vous ne pouvez pas vraiment itération sur une structure, bien qu'il y ait des imitations qui pourraient fonctionner. Une possibilité serait boost :: Tuple, qui permet de spécifier des membres par index, mais uniquement à la compilation (en tant que paramètres de modèle). Un autre serait de générer le code de manière externe - à l'aide du code que j'ai donné, vous pouvez avoir (par exemple) une liste de membres, et utilisez quelque chose comme AWK pour générer du code pour les afficher.


Bien que vous ne puissiez pas directement itérer sur un struct , vous pouvez écrire une méthode visiteur qui envoie chaque méthode et son nom à un objet de fonction visiteur . Cela présente l'avantage de créer des visiteurs à des fins différentes et de ne pas modifier le code ou la structure de l'objet d'origine (mise en page).



1
votes

Je suggère de créer des méthodes modèles pour écrire dans la zone de texte:

void
annotate(TRichTextEdit& textbox)
{
  Write_To_Textbox(member1, "member1", textbox);
//...
}


1 commentaires

P.s. Ajouter un #if pour le débogage empêcherait ce code de la libération.



12
votes

Si je comprends bien correctement, le cœur de la question initiale est de savoir comment itération sur une structure. En bref, alors que Jerry Coffin a souligné dans un commentaire, cela ne peut pas être fait. Je vais essayer d'expliquer pourquoi, puis je vais essayer d'expliquer comment faire la meilleure chose à faire.

Une structure est stockée en mémoire comme une pièce monolithique de données sans métadonnées décrivant sa structure. Par exemple, la structure suivante: p> xxx pré>

peut être représentée en mémoire en utilisant une notation hexadécimale comme suit p> xxx pré>

où le premier 3 octets contiennent les champs de caractères, le quatrième est une valeur aléatoire utilisée pour le remplissage, et les quatre octets suivants sont la représentation Little-Endian de l'entier 122. Cette mise en page varie d'un compilateur au compilateur et au système au système. En bref, la représentation binaire ne vous dit pas quelles sont les données ou où des champs individuels sont stockés. P>

Alors, comment les champs d'accès du compilateur dans des structures? Le code p> xxx pré>

est traduit dans une instruction comme p> xxx pré>

en d'autres termes, le compilateur code les décalages littéraux de la champs dans le code. Encore une fois, cela ne nous aide pas. P>

Par conséquent, nous devons annoter la structure nous-mêmes. Cela peut être fait en ajoutant des informations à l'intérieur de la structure pour la transformer en une sorte de magasin de valeur de clé ou en ajoutant une deuxième structure. Vous ne voulez pas changer la structure d'origine, une deuxième structure est donc la voie à suivre. P>

Je suppose que votre structure ne contient que des types de base: int, char, etc. Si vous êtes complexe d'autres classes dans la structure, alors je suggère d'ajouter une méthode de tostring () à leur classe de base et d'appeler cette méthode - c'est comme ça que C # et Java le font. P>

Foo tmp;

#define FIELD_OFFSET(f) ((char*)&(tmp.f) - (char*)&tmp)

enum FieldType { INT_FIELD, CHAR_FIELD, OBJECT_FIELD };

struct StructMeta {
    FieldType type;
    size_t offset;
};

StructMeta[] metadata = {
   {CHAR_FIELD, FIELD_OFFSET(a)},
   {CHAR_FIELD, FIELD_OFFSET(b)},   
   {CHAR_FIELD, FIELD_OFFSET(c)},
   {INT_FIELD, FIELD_OFFSET(i)},
   {OBJECT_FIELD, FIELD_OFFSET(o)},
}

void RenderStruct(Foo* f)
{
    for (int i = 0; i < sizeof(metadata)/sizeof(StructMeta); i++)
    {
        switch (metadata[i].type)
        {
             case CHAR_FIELD:
                 char c = *((char*)f + metadata[i].offset);
                 // render c
                 break;
             case INT_FIELD:
                 int i = *(int*)((char*)f + metadata[i].offset);
                 // render i
                 break;
             case OBJECT_FIELD:
                 Object* o = (object*)((char*)f + metadata[i].offset);
                 const char* s = o->ToString();
                 // render s
                 break;    
        }
    }
}


3 commentaires

C'est bien! Et si la structure contient également une structure? Je suppose que vous devez avoir des métadonnées de métadonnées ?!


Ma suggestion serait de mettre en œuvre une méthode Tostring () sur la struct ou une classe de base.


Comment imprimez-vous les noms de champs de la structure?



1
votes

Étant donné que vous avez un nombre assez grand de champs dans la structure, utilisez un analyseur ou écrivez le vôtre pour générer un code source pour imprimer les membres, leurs noms et leurs valeurs.

comme un exercice intéressant, le temps vous-même comme vous écrivez l'utilitaire. Vous pouvez savoir que l'utilisation d'un éditeur disposant de la recherche d'expression régulière et de remplacer la capacité peut être plus rapide.

Sinon, jetez votre conception actuelle et adoptez un nouveau. J'ai utilisé une conception d'enregistrements et de champs. Chaque enregistrement (structure) a un vecteur d'un ou plusieurs pointeurs à un field_interface . Le field_interface a des méthodes telles que get_field_name () et get_sql_data_type_text () . N'oubliez pas non plus que Java Favoris Tostring () qui renvoie la valeur de champ en tant que chaîne. Cette technique vous permet d'iTerrer sur un conteneur de champs et d'imprimer leurs valeurs (en utilisant Tostring ) et leur nom (utilisant get_field_name () ).

Ajouter le motif visiteur pour la lecture et l'écriture (j'appelle les lecteurs et les écrivains) et vous avez des champs et des enregistrements hautement adaptables sans changer leur contenu interne. En outre, cela conduit merveilleusement dans la programmation générique où vous pouvez utiliser des champs et des enregistrements sans connaître leurs types; ou avoir cela pris en charge au niveau des feuilles.

BTW, au moment où vous avez attendu la réponse parfaite, vous auriez pu écrire une fonction pour "itérer" ou visiter les membres de la structure.


1 commentaires

Le problème de la refonte est la compatibilité à l'envers. Cela prendrait beaucoup plus de temps et d'efforts pour assurer la compatibilité à l'envers en repensant. Ce format est utilisé depuis plus de 10 ans et nous obtenons de temps en temps quelqu'un qui doit être mis à niveau depuis plus de 8 ans, le format de configuration doit donc correspondre ou que les bits ne seront plus au même endroit.



5
votes

Il n'y a aucun moyen de itérer les membres d'une structure à moins que vous ne construisiez vos propres métadonnées pour décrire la structure. Le compilateur C ++ n'émet tout simplement pas l'information dont vous auriez besoin automatiquement.

Cependant, avec un peu de macro Magic, vous pouvez construire les métadonnées dont vous auriez besoin assez facilement. J'ai écrit un certain code pour le faire (en fait un contrôle personnalisé Windows soufflé complet) il y a de nombreuses années et je l'utilise toujours tout le temps.

Le truc de base consiste à utiliser une magie macro-magie de bit d'obtenir le compilateur vous aider à construire les métadonnées. xxx


0 commentaires

1
votes

Alors, vous devez faire vos informations de type disponible lors de l'exécution. Ces métadonnées sont disponibles au moment de la compilation, mais est ensuite mis au rebut. Nous avons juste besoin d'un moyen de le sauver du compilateur.

  1. explicite Metadata , comme le démontre antonmarkov et John Knoeller. Vous devez maintenir en phase avec la structure, mais il a la vertu de ne pas toucher votre définition de la structure d'origine.

    1.1 Génération de code Si votre définition de struct est assez régulière, vous pourriez être en mesure d'automatiser la génération de cette table de métadonnées en utilisant awk.

  2. Métaprogrammation : si vous ne me dérange pas de réécrire la structure (mais en laissant la mise en page même, de sorte que vous gardez la compatibilité binaire) vous pouvez obtenir le compilateur de faire la levée de lourdes pour vous . Vous pouvez utiliser Boost.tuple déclarer votre structure et itérer sur ses éléments en utilisant Boost .Fusion .


0 commentaires


3
votes

Il n'y a aucun moyen de itération sur les membres d'une structure ordinaire. Vous devez fournir ces informations en dehors de votre déclaration de structure.

Vous pouvez le faire à la compilation, car certaines des réponses précédentes ont montré. Cependant, vous pouvez aussi le faire au moment de l'exécution. Ceci est similaire à la manière dont certaines bibliothèques "sérialisement" fonctionnent. P>

Vous pouvez avoir la classe suivante: p> xxx pré>

Cette classe contient un vecteur de "classe Membres "(Memberstore :: Datainfo), chacun de l'un a: p>

  • Décalage de la base de la classe. li>
  • Une méthode pour les convertir en STD :: Strings. Cette méthode est automatiquement générée s'il est possible d'utiliser STD :: Stringstream pour la conversion. Si ce n'est pas possible, il devrait être possible de spécialiser le modèle. LI> ul>

    Vous pouvez ajouter des éléments à cette classe à l'aide de & opérateur (vous pouvez concaténer plusieurs et opérateurs). Après cela, vous pouvez parcourir les membres et les convertir à STD :: String à l'aide de son index: P>

    struct StructureIWantToPrint
    {
      char a;
      int b;
      double c;
    };
    
    int main(int argc, wchar_t* argv[])
    {
      StructureIWantToPrint myData;
      myData.a = 'b';
      myData.b = 18;
      myData.c = 3.9;
    
      MemberStore myDataMembers(myData);
      myDataMembers & myData.a & myData.b & myData.c;
    
      for(size_t i=0;i<myDataMembers.size();++i) {
        std::cout << myDataMembers.convert(i) << std::endl;
      }
    
        return 0;
    }
    


0 commentaires

25
votes

boost_fusion_ADAPT_Struct_structeur semble aller bien ici. Par exemple: xxx


2 commentaires