Appeler dynamiquement une fonction C avec un argument varargs
Je suis à la programmation en C à l'encontre d'un tiers de la bibliothèque (en HP/Mercury Loadrunner) qui permet à un varargs style de taille variable de la liste d'arguments pour l'un de ses fonctions. Je tiens à appeler cette fonction, mais je ne sais pas à l'avant combien d'arguments, je vais avoir.
Il y a une fonction faite par un de mes prédécesseurs qui sert un peu, mais le problème est que cette fonction suppose que le scénario du pire des cas (plus de 3000 arguments) et de la main-codes pour.
Pour éclairer, voici le (début du) du code. La fonction nous l'appelons, est web_submit_data()
. Il HTTP post un ensemble de données de formulaire. Cette mise en œuvre est venu à propos, lorsque l'on traite avec générées dynamiquement les formulaires avec un nombre arbitraire de champs.
(Nettoyé un peu juste de l'original, la main qui codé de l'index de la main ainsi..)
web_submit_data_buffer_gazillion_items( const char *bufferName, const char *bufferValue)
{
const int size = 129;
int i = 0;
int j = 11;
web_submit_data(&bufferName[i++ * size], //"some form"
&bufferName[i++ * size], //"Action=https://blah.blah/form");
&bufferName[i++ * size], //"Method=POST");
&bufferName[i++ * size], //"TargetFrame=");
&bufferName[i++ * size], //"RecContentType=text/html");
&bufferName[i++ * size], //"Referer=https://blah.blah/index.html");
&bufferName[i++ * size], //"Snapshot=t1.inf");
&bufferName[i++ * size], //"Mode=HTML");
ITEMDATA, //missing in action: indexes 8 through 10
&bufferName[j * size],&bufferValue[j++ * size], ENDITEM,
&bufferName[j * size],&bufferValue[j++ * size], ENDITEM,
&bufferName[j * size],&bufferValue[j++ * size], ENDITEM,
..
(repeat the last 3 lines ad nauseum)
..
&bufferName[j * size],&bufferValue[j++ * size], ENDITEM,
&bufferName[j * size]);
}
Maintenant, j'ai trouvé une bibliothèque externe qui pourrait fonctionner ( http://www.dyncall.org ), mais je préférerais ne pas) être complètement processeur personne à charge, et b) d'enseigner Loadrunner sur la liaison dans des sources externes..
Edit:
La fonction d'origine utilisé codé en dur index au lieu d'utiliser une variable. Pouvez toujours y revenir si il s'avère être trop imprévisible. Cependant, comme je suis peu probable pour exécuter ce avec un compilateur différent ou de matériel /OS je doute que ce est vraiment la peine.
Aussi: je n'ai pas de contrôle sur la mise en œuvre de web_submit_data(). Donc, juste repousser le problème à plus d'un niveau ne va pas le couper..
Une autre chose à noter: La spec pour web_submit_data()
utilise une constante appelée DERNIER pour marquer la fin de la liste d'arguments. La mise en œuvre originale ne l'utilise pas. Sans doute le callsite n' ..
source d'informationauteur Randakar
Vous devez vous connecter pour publier un commentaire.
Dans CamelBones-je utiliser libffi appeler objc_msgSend(), qui est un varargs fonction. Fonctionne un régal.
De longueur Variable arguments sont fondamentalement juste un pointeur vers un tas de paniers de données qui est transmis à la fonction requise. Il est de la responsabilité de la fonction appelée à interpréter le présent paniers de données.
L'architecture de la façon sécuritaire de le faire est d'utiliser la va_list macros (n-alexandre mentionné), sinon, vous risquez de rencontrer des problèmes avec la façon dont les divers types de données sont complétées dans la mémoire.
La bonne façon de concevoir les varargs fonctions est d'avoir deux versions, l'une qui accepte les '...', qui à son tour extrait va_list et passe à une fonction qui prend une va_list. De cette façon, vous pouvez construire dynamiquement les arguments si vous en avez besoin et peut, au lieu d'appeler la va_list version de la fonction.
Plus standard IO fonctions ont varargs versions: vprintf pour printf, vsprintf pour sprintf... vous voyez l'idée. Voir si votre bibliothèque implémente une fonction nommée "vweb_submit_data" ou quelque chose à cet effet. S'ils ne le font pas, les envoyer par courriel et dites-leur à fixer leur bibliothèque.
3000 lignes de la même chose (même si elle est induite par le préprocesseur) me fait grincer des dents
Puisqu'il n'est généralement pas un problème pour passer plus des arguments à une fonction prenant variable d'arguments que la fonction attend (voir note de bas de page n ° 1), vous pouvez faire quelque chose comme ce qui suit:
Note de bas de page n ° 1: Si vous pensez à la façon dont les macros dans
stdargs.h
loi, cela devient clair. Cependant, je ne prétends pas que cette technique pourrait être conformes aux normes. En fait, dans l'histoire récente, le stackoverflow réponses que j'ai posté où j'ai fait cet avertissement ont en effet été jugés non-conformes aux normes (en général, par le vigilant litb). Afin d'utiliser cette technique à vos propres risques, et de vérifier, vérifier, vérifier).Il n'existe pas de portable façon de construire une liste d'arguments pour une variable d'argument de fonction en C au moment de l'exécution. Il y a quelques dépend de l'implémentation astucesde la dyncall bibliothèque vous trouvé qui ressemble à un bon et sans doute plus portable que la plupart.
Remarque: le code est déjà compilateur-dépendante (mais peut-être pas de processeur-dépendant), parce que l'invocation de
web_submit_data
suppose que l'argument sous-expressions dans un appel de procédure sont évaluées de gauche à droite, mais le langage C, les feuilles de l'ordre de l'argument de l'évaluation quelconque.Voir pour référence: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value
Alors peut-être que la non-solution portable ne va pas faire des choses bien pire pour vous.
Pouvez vous restructurez votre code, de sorte que ce n'est pas nécessaire? Vous pourriez peut-être prendre le tampon entrante et de la rendre plus déterministe:
Désolé, ce ne pouvait pas être plus étoffé - ma femme m'a appelé pour le petit déjeuner. 🙂
Écrire une fois avec le préprocesseur et ne jamais regarder en arrière.
Ou si le nombre d'arguments est fixé pour chaque appel, écrire un script pour générer des préprocesseur pour cacher la façon dont odieux que l'appel est.
Noter que l'exemple de code que vous avez posté, a un comportement indéfini - les virgules qui séparent les paramètres de la fonction ne sont pas de la séquence de points (ceux des virgules ne sont pas de l'opérateur virgule), de sorte que la modification de
i
et ouj
à plusieurs reprises dans l'appel de fonction en argument la liste des résultats dans un comportement indéfini.Ce n'est pas de mentionner que l'ordre d'évaluation des arguments d'appel de fonction n'est pas spécifié par la norme, de sorte que même si vous avez fait la modification de
i
etj
à l'aide de fonctions pour évaluer les arguments (les appels de fonction sont eux-mêmes de la séquence de points de), vous devez être à peu près en passant les pointeurs dans une durée indéterminée afin.Aussi, je ne vois pas comment
web_submit_data()
sait combien d'arguments, il a été transmise - je ne vois pas un nombre ou une définitive sentinelle argument à la fin. Mais je suppose que votre exemple est peut-être juste que - un exemple qui n'aurait pas complète, précise les détails. D'autre part, il estweb_submit_data()
'problème de toute façon, non?Il y a deux façon de passer un nombre variable d'arguments: une fonction qui accepte "..." ou à une fonction qui accepte va_list.
Vous ne pouvez pas définir dynamiquement le nombre d'arguments pour les "..." de l'interface, mais vous devriez être capable de le faire pour le va_list. Google pour va_start, va_end, et va_list.
Je sais que c'est un vieux thread, mais j'ai juste couru à travers elle. La bonne façon de gérer de longueur variable pour envoyer des données de formulaire dans LoadRunner est d'utiliser un web_custom_request(). Vous construisez la paire nom|valeur de la structure de la longueur variable des arguments comme une chaîne de caractères et de le transmettre qu'une partie de la fonction.
Enregistrement de la un appel comme un web_custom_request() et la structure de la chaîne d'argument pour les paires nom|valeur deviendra évident. Utilisez simplement toute chaîne C les fonctions de gestion vous souhaitez construire la chaîne en question et de les inclure en tant que partie de la liste d'arguments pour la web_custom_request().