233 votes

Transmettre un appel d’une fonction variadique en C

En C, est-il possible de transmettre l'invocation d'un variadic fonction? Comme dans,

int my_printf(char *fmt, ...) {
  fprintf(stderr, "Calling printf with fmt %s", fmt);
  return SOMEHOW_INVOKE_LIBC_PRINTF;
}

La redirection de l'invocation de la manière ci-dessus n'est évidemment pas strictement nécessaire dans ce cas (puisque vous pourriez vous connecter invocations par d'autres moyens, ou de l'utilisation vfprintf), mais le code je suis en train de travailler sur la nécessite le wrapper pour faire du travail réel, et n'a pas (et ne peut pas avoir ajouté) une fonction d'aide semblable à vfprintf.

[Mise à jour: il semble y avoir une certaine confusion sur la base des réponses qui ont été fournies jusqu'à présent. De formuler la question d'une autre manière: en général, pouvez-vous enveloppez de l'arbitraire variadic fonction sans modification de la fonction de définition.]

175voto

Adam Rosenfield Points 176408

Si vous n’avez pas un analogue de la fonction de qui prend un au lieu d’un nombre variable d’arguments, vous ne pouvez pas le faire. Voir http://c-faq.com/varargs/handoff.html.

79voto

Charles Bailey Points 244082

Pas directement, mais il est fréquent (et vous trouverez presque universellement le cas dans la bibliothèque standard) pour les variadic fonctions à venir en paires avec un varargs style autre fonction. par exemple, printf/vprintf

Le v... les fonctions d'une va_list paramètre, la mise en œuvre de ce qui se fait souvent avec un compilateur spécifique "macro magique", mais vous avez la garantie que l'appel de la v... le style de fonction à partir d'un variadic fonction comme ceci:

int m_printf(char *fmt, ...)
{
    int ret;

    /* Declare a va_list type variable */
    va_list myargs;

    /* Initialise the va_list variable with the ... after fmt */

    va_start(myargs, fmt);

    /* Forward the '...' to vprintf */
    ret = vprintf(fmt, myargs);

    /* Clean up the va_list */
    va_end(myargs);

    return ret;
}

Cela devrait vous donner l'effet que vous recherchez.

Si vous envisagez d'écrire un variadic fonction de la bibliothèque vous devriez également envisager de faire une va_list style compagnon disponibles en tant que partie de la bibliothèque. Comme vous pouvez le voir à partir de votre question, il peut s'avérer utile pour vos utilisateurs.

58voto

Commodore Jaeger Points 11949

C99 prend en charge les macros à l’aide d’arguments variadique; selon votre compilateur, vous pourriez être en mesure de déclarer une macro qui fait ce que vous voulez :

En général, cependant, la meilleure solution est d’utiliser le formulaire de va_list de la fonction que vous essayez de l’enveloppe, si elle existe.

15voto

coltox Points 71

Comme il n'est pas possible de transférer les appels en douceur, nous avons travaillé autour de ce par la mise en place d'un nouveau cadre de pile avec une copie de l'original du cadre de pile. Cependant c'est très portables et fait toutes sortes d'hypothèses, par exemple, que le code utilise des pointeurs de frame et le "standard" des conventions d'appel.

Ce fichier d'en-tête permet d'envelopper les variadic fonctions pour x86_64 et i386 (GCC). Il ne fonctionne pas pour virgule flottante arguments, mais doit être simple à prolonger pour soutenir ceux-ci.

#ifndef _VA_ARGS_WRAPPER_H
#define _VA_ARGS_WRAPPER_H
#include <limits.h>
#include <stdint.h>
#include <alloca.h>
#include <inttypes.h>
#include <string.h>

/* This macros allow wrapping variadic functions.
 * Currently we don't care about floating point arguments and
 * we assume that the standard calling conventions are used.
 *
 * The wrapper function has to start with VA_WRAP_PROLOGUE()
 * and the original function can be called by
 * VA_WRAP_CALL(function, ret), whereas the return value will
 * be stored in ret.  The caller has to provide ret
 * even if the original function was returning void.
 */

#define __VA_WRAP_CALL_FUNC __attribute__ ((noinline))

#define VA_WRAP_CALL_COMMON()                                        \
    uintptr_t va_wrap_this_bp,va_wrap_old_bp;                        \
    va_wrap_this_bp  = va_wrap_get_bp();                             \
    va_wrap_old_bp   = *(uintptr_t *) va_wrap_this_bp;               \
    va_wrap_this_bp += 2 * sizeof(uintptr_t);                        \
    size_t volatile va_wrap_size = va_wrap_old_bp - va_wrap_this_bp; \
    uintptr_t *va_wrap_stack = alloca(va_wrap_size);                 \
    memcpy((void *) va_wrap_stack,                                   \
        (void *)(va_wrap_this_bp), va_wrap_size);


#if ( __WORDSIZE == 64 )

/* System V AMD64 AB calling convention */

static inline uintptr_t __attribute__((always_inline)) 
va_wrap_get_bp()
{
    uintptr_t ret;
    asm volatile ("mov %%rbp, %0":"=r"(ret));
    return ret;
}


#define VA_WRAP_PROLOGUE()           \
    uintptr_t va_wrap_ret;           \
    uintptr_t va_wrap_saved_args[7]; \
    asm volatile  (                  \
    "mov %%rsi,     (%%rax)\n\t"     \
    "mov %%rdi,  0x8(%%rax)\n\t"     \
    "mov %%rdx, 0x10(%%rax)\n\t"     \
    "mov %%rcx, 0x18(%%rax)\n\t"     \
    "mov %%r8,  0x20(%%rax)\n\t"     \
    "mov %%r9,  0x28(%%rax)\n\t"     \
    :                                \
    :"a"(va_wrap_saved_args)         \
    );

#define VA_WRAP_CALL(func, ret)            \
    VA_WRAP_CALL_COMMON();                 \
    va_wrap_saved_args[6] = (uintptr_t)va_wrap_stack;  \
    asm volatile (                         \
    "mov      (%%rax), %%rsi \n\t"         \
    "mov   0x8(%%rax), %%rdi \n\t"         \
    "mov  0x10(%%rax), %%rdx \n\t"         \
    "mov  0x18(%%rax), %%rcx \n\t"         \
    "mov  0x20(%%rax),  %%r8 \n\t"         \
    "mov  0x28(%%rax),  %%r9 \n\t"         \
    "mov           $0, %%rax \n\t"         \
    "call             *%%rbx \n\t"         \
    : "=a" (va_wrap_ret)                   \
    : "b" (func), "a" (va_wrap_saved_args) \
    :  "%rcx", "%rdx",                     \
      "%rsi", "%rdi", "%r8", "%r9",        \
      "%r10", "%r11", "%r12", "%r14",      \
      "%r15"                               \
    );                                     \
    ret = (typeof(ret)) va_wrap_ret;

#else

/* x86 stdcall */

static inline uintptr_t __attribute__((always_inline))
va_wrap_get_bp()
{
    uintptr_t ret;
    asm volatile ("mov %%ebp, %0":"=a"(ret));
    return ret;
}

#define VA_WRAP_PROLOGUE() \
    uintptr_t va_wrap_ret;

#define VA_WRAP_CALL(func, ret)        \
    VA_WRAP_CALL_COMMON();             \
    asm volatile (                     \
    "mov    %2, %%esp \n\t"            \
    "call  *%1        \n\t"            \
    : "=a"(va_wrap_ret)                \
    : "r" (func),                      \
      "r"(va_wrap_stack)               \
    : "%ebx", "%ecx", "%edx"   \
    );                                 \
    ret = (typeof(ret))va_wrap_ret;
#endif

#endif

En fin de compte, vous pouvez encapsuler les appels comme ceci:

int __VA_WRAP_CALL_FUNC wrap_printf(char *str, ...)
{
    VA_WRAP_PROLOGUE();
    int ret;
    VA_WRAP_CALL(printf, ret);
    printf("printf returned with %d \n", ret);
    return ret;
}

12voto

Greg Hewgill Points 356191

Presque, en utilisant les installations disponibles dans `` :

Notez que vous devrez utiliser la `` version plutôt que plaine . Il n’est pas une façon d’appeler directement une fonction variadique dans cette situation sans utiliser .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X