9
votes

Meilleur moyen de stocker une version Va_List pour une utilisation ultérieure en C / C ++

J'utilise un va_list pour construire une chaîne qui est rendu.

"Player Score:%d"


4 commentaires

Pourriez-vous fournir une meilleure description de ce que vous entendez par «plus tard»? Je pense qu'il devrait être assez clair pour quiconque que "Va_List" ne peut être valide que tant que l'invocation de fonction variable correspondante est toujours active (c'est-à-dire du point de vue dépendant de la mise en oeuvre de bas niveau, tant que le cadre de pile correspondant avec des paramètres est vivant). Toute tentative d'y accéder une fois que la fonction est renvoyée est une recette de catastrophe.


l'utilisation Boost.Format au lieu de construire la chaîne. Il n'y a aucune raison de faire sauter la sécurité de type si vous pouvez l'éviter.


Oui, nous allons finir par faire quelque chose de similaire à cela, car cette méthode (celle de la question) est tout simplement imparfaite.


Je n'écrirais pas "défectueux". J'écrirais "la mise en œuvre dépendante". ;) Je souhaite que quelqu'un essaierait le compilateur Mac avec le code que j'ai posté. Je suis curieux.


5 Réponses :


8
votes

stocker le va_list n'est pas une bonne idée; La norme nécessite uniquement que l'argument va_list fonctionne avec va_start () , va_arg () et va_end () . Autant que je puisse dire, le va_list n'est pas garanti d'être copier constructible.

Mais vous n'avez pas besoin de stocker le va_list . Copiez les arguments fournis dans une autre structure de données, telle qu'un vecteur (de vide *, probablement) et les récupérer ultérieurement de la manière habituelle. Vous devrez faire attention aux types, mais c'est toujours le cas pour les fonctions de style Printf en C ++.


2 commentaires

Je me suis demandé de la copie constructible / des qualités assignables de Va_List. Je n'arrive pas à trouver une discussion dans la norme.


Ce serait parce qu'il est mise en œuvre suppose spécifique I. Je sais que certains processeurs qui stockent les premiers arguments dans les registres au lieu d'une pile, ce qui rendrait la copie du va_list un peu plus compliqué. En outre, va_list ne sait pas combien il y a des arguments. Vous avez vraiment besoin d'une structure qui contient des informations de quantité, simple accès aléatoire et la sémantique de copie bien définies. Certaines langues peuvent exiger des arguments dans un ordre différent!



2
votes

Ce que vous décrivez sur "Holding the Number (s) fourni dans la Va_List" est le moyen de l'approcher.

Le va_list maintient les pointeurs à la mémoire temporaire sur la pile (appelé "stockage automatique" dans la norme C). Une fois la fonction de variable args renvoyée, ce stockage automatique est parti et le contenu n'est plus utilisable. Pour cette raison, vous ne pouvez pas simplement conserver une copie du VA_List - La mémoire IT Les références contiennent du contenu imprévisible.

Dans l'exemple que vous donnez, vous devrez stocker deux entiers réutilisés lors de la ré-création de ce message. En fonction du nombre de cordes de format différents que vous devez faire face, votre approche pourrait varier.

Pour un type d'approche totalement général, vous devrez:

  • Écrivez un " cache_arguments () " Fonction qui crée un tampon de mémoire dynamique des valeurs trouvées dans les arguments variables.
  • Ce cache_arguments () utiliserait la chaîne printf () -Style, ainsi que le va_start , va_arg , va_end macros. Vous devrez récupérer les types selon les spécificateurs de type printf () , car tailleOf (double)! = Tailleof (int) . .
  • Stockez les arguments dans votre cache de mémoire avec le même alignement et le même remplissage attendu par VA_ARG () sur votre plate-forme. (Lisez votre varargs.h fichier.)
  • Obtenez vos appels sur VSNPRINTF () Utilisation de ce tampon de mémoire mis en cache au lieu d'un pointeur créé par VA_START () . .

    Les éléments ci-dessus sont tous possibles sur la plupart des plates-formes, y compris Linux et Windows.

    Un article que vous souhaiterais envisager de la traduction est la préoccupation de mot-ordonnance. Ce qui est écrit en anglais comme suit:

    joueur Sam a marqué 20 points.

    pourrait dans certaines langues (humaines) seulement être écrites couramment avec un ordre de mot analagous à:

    20 points ont été marqués par le joueur Sam.

    Pour cette raison, le Win32 FormatMessage () API utilise un printf () String de format, avec la différence fonctionnelle que les paramètres sont numérotés, comme dans: < / p>

    Le joueur% 1 a marqué% 2! D! Points.
    % 2! D! Les points ont été marqués par le joueur% 1.

    (le type de chaîne est supposé pour chaque argument, de sorte que % 1 est équivalent à % 1! S! ) )

    Bien sûr, vous ne pouvez pas utiliser l'API Win32, mais la fonctionnalité de modification de l'ordre des mots des arguments formatés est ce que je tente d'introduire en tant que concept. Vous voudrez peut-être implémenter cela dans votre logiciel, également.


3 commentaires

Vous montrez un point complètement valide à la fin là-bas, j'aurais simplement dû fournir un exemple avec seulement un% de la chaîne source. Ainsi, le format d'une norme Va_List une fois que vous avez démarré afin que je puisse envoyer dans un tampon que j'ai créé (via cache_arguments) dans VSPRINTF. Je veux juste m'assurer que je vous comprends correctement. Cela doit fonctionner sur Windows, Linux et Mac. Si non, je devrai simplement faire mon propre printf qui prend un vecteur de valeurs que je stocke par opposition à un ....


La mise en œuvre de VA_LIST n'est pas standard, mais la valeur renvoyée de VA_ARG () est. Pour former la mémoire requise, effectuez une «course d'essai» à travers les arguments sans lire leurs valeurs. Utilisez VA_ARG () pour renvoyer leurs adresses. Vous devrez appeler VA_ARG () un temps supplémentaire pour récupérer un caractère final imaginaire. En soustrayant la valeur de retour finale de VA_ARG () à partir de l'initiale, vous pouvez obtenir le nombre d'octets requis. Allouer ce nombre d'octets + Tailleof (int). Faites une deuxième passe à travers les arguments, en les comportant maintenant au tampon alloué - commencez à Byte Tailleof (int)


Ah, oui ... En raison de la mise en œuvre de Va_Start / VA_ARG (renvoyer le contenu d'un pointeur), il sera légal d'utiliser & VA_ARG (AP) sur toutes les plateformes - qui est nécessaire pour obtenir les adresses de paramètres.



10
votes

Cette question a vraiment piqué mon intérêt. De plus, je serai confronté à un problème similaire dans mon propre travail, de sorte que la solution conçue ici peut aussi m'aider aussi.

En bref, j'ai écrit un code de la preuve de concept qui met en cache des arguments variables pour une utilisation ultérieure - vous pouvez trouver Ci-dessous.

J'ai pu obtenir le code ci-dessous pour fonctionner correctement sur Windows et Intel basé sur Linux. J'ai compilé avec GCC sur Linux et MSVC sous Windows. Il y a un avertissement répété à deux reprises sur l'abus de VA_START () de GCC - ce qui avertit que vous pourriez désactiver dans votre maquillage.

J'aimerais savoir si ce code fonctionne sur un compilateur Mac. Cela pourrait prendre un peu de peaufie pour l'obtenir pour compiler.

Je réalise que ce code est:

  • extrême dans son abus de VA_START () tel que défini par la norme ANSI C.
  • Octe-School Octeed orienté ch.
  • théoriquement non-portable dans son utilisation de la variable VA_LIST en tant que pointeur.

    Mon utilisation de MALLOC () et GRATUIT () était très délibérée, car Va_List Macros provient de la norme C et ne sont pas des fonctionnalités C ++. Je réalise votre titre de question mention C ++, mais j'ai essayé de produire une solution entièrement compatible C, autre que d'utiliser certains commentaires de style C ++.

    Ce code a aussi des bugs ou des non-portabilités dans le traitement de la chaîne de format. Je fournis cela comme une preuve de concept que j'ai piraté ensemble en deux heures et non un échantillon de code fini prêt à être utilisé pour un usage professionnel.

    Cet avis de non-responsabilité a dit que vous trouverez le résultat aussi agréable que je l'ai fait! C'était une belle question à pirater. La nature malade et tordue du résultat me donne un rire de ventre profond. ;) xxx


1 commentaires

Je pense que c'est une longue et compliquée pour des tâches simples. Je ne sais pas exactement ce que votre extrait est servi, cela pourrait faire et maintenir d'autres fonctionnalités, mais j'ai fait une simple allocation / stockage pour les valeurs AGRRS. `Args f2_args = (args ) MALLOC (1 * TAILLEOF (INT8_T) + 1 * Tailleof (uint8_T) + 1 * Tailleof (int) + 1 * Taille de taille (Cons-Char *)); // Initialisation args f2_args = (args) {(int8_t) -10, (uint8_t) 9, (int) 90000, (const Char ) p0}; `Ce que vous pensez?



3
votes

Vous pouvez utiliser va_copy () , voici un exemple: xxx


1 commentaires

Le mot clé de la question était "utilisé ultérieur". Une fois la fonction renvoie, les variables TMP et AP ne peuvent pas être utilisées.



0
votes

Le moyen de le faire en C est d'envoyer une structure d'arguments à la fonction à la place. Vous devez transmettre la structure par référence, puis copier (memcpy) la structure vers un emplacement commun qui vous permettra de la réutiliser plus tard. Vous démêlez la structure à la destination de la même manière que vous l'avez envoyée. Vous gardez le gabarit de la structure pour «régler et obtenir».


0 commentaires