En C, ¿es posible reenviar la invocación de una función variable? Como en,
int my_printf(char *fmt, ...) {
fprintf(stderr, "Calling printf with fmt %s", fmt);
return SOMEHOW_INVOKE_LIBC_PRINTF;
}
Reenviar la invocación de la manera anterior obviamente no es estrictamente necesario en este caso (ya que podría iniciar invocaciones de otras maneras, o usar vfprintf), pero la base de código en la que estoy trabajando requiere que el contenedor haga un trabajo real, y no No tiene (y no puede haber agregado) una función auxiliar similar a vfprintf.
[Actualización: parece haber cierta confusión basada en las respuestas que se han proporcionado hasta ahora. Para formular la pregunta de otra manera: en general, ¿puede ajustar alguna función variable arbitraria sin modificar la definición de esa función ?]
Respuestas:
Si no tiene una función análoga a la
vfprintf
que toma unva_list
número variable de argumentos en lugar de uno variable, no puede hacerlo . Verhttp://c-faq.com/varargs/handoff.html .Ejemplo:
fuente
No directamente, sin embargo, es común (y encontrará casi universalmente el caso en la biblioteca estándar) para que las funciones variadas vengan en pares con una
varargs
función alternativa de estilo. por ejemploprintf
/vprintf
Las funciones v ... toman un parámetro va_list, cuya implementación a menudo se realiza con 'macro magic' específica del compilador, pero se garantiza que llamar a la función v ... style desde una función variadic como esta funcionará:
Esto debería darte el efecto que estás buscando.
Si está considerando escribir una función de biblioteca variadic, también debería considerar hacer que un compañero de estilo va_list esté disponible como parte de la biblioteca. Como puede ver en su pregunta, puede resultar útil para sus usuarios.
fuente
va_copy
antes de pasar lamyargs
variable a otra función. Consulte MSC39-C , donde dice que lo que está haciendo es un comportamiento indefinido.va_arg()
a unva_list
que tenga un valor indeterminado", no lo hago porque nunca lo usova_arg
en mi función de llamada. El valor demyargs
after the call (en este caso) avprintf
es indeterminado (suponiendo que nos hagava_arg
). La norma dice quemyargs
"se pasará a lava_end
macro antes de cualquier referencia adicional a [it]"; que es exactamente lo que hago No necesito copiar esos argumentos porque no tengo intención de repetirlos en la función de llamada.ap
se pasa a una función que utilizava_arg(ap,type)
, el valor deap
no está definido después del retorno de esa función.C99 soporta macros con argumentos variadic ; dependiendo de su compilador, puede declarar una macro que hace lo que quiere:
Sin embargo, en general, la mejor solución es usar la forma va_list de la función que está intentando ajustar, en caso de que exista.
fuente
Casi, utilizando las instalaciones disponibles en
<stdarg.h>
:Tenga en cuenta que deberá usar la
vprintf
versión en lugar de la simpleprintf
. No hay una manera de llamar directamente a una función variadic en esta situación sin usarva_list
.fuente
Como en realidad no es posible reenviar esas llamadas de una manera agradable, trabajamos alrededor de esto configurando un nuevo marco de pila con una copia del marco de pila original. Sin embargo, esto es altamente inportable y hace todo tipo de suposiciones , por ejemplo, que el código utiliza punteros de trama y las convenciones de llamada 'estándar'.
Este archivo de encabezado permite ajustar funciones variadas para x86_64 e i386 (GCC). No funciona para argumentos de punto flotante, pero debería extenderse directamente para admitirlos.
Al final puedes envolver llamadas como esta:
fuente
Use vfprintf:
fuente
No hay forma de reenviar tales llamadas a funciones porque está en la única ubicación donde puede recuperar elementos de pila sin procesar
my_print()
. La forma habitual de encapsular llamadas como esas es tener dos funciones, una que simplemente convierte los argumentos en variasvarargs
estructuras, y otra que realmente opera sobre esas estructuras. Usando un modelo de doble función, puede (por ejemplo) ajustarprintf()
inicializando las estructurasmy_printf()
conva_start()
, y luego pasarlas avfprintf()
.fuente
Sí, puedes hacerlo, pero es algo feo y tienes que saber la cantidad máxima de argumentos. Además, si está en una arquitectura donde los argumentos no se pasan en la pila como el x86 (por ejemplo, PowerPC), tendrá que saber si se usan tipos "especiales" (doble, flotantes, altivec, etc.) y si entonces, trate con ellos en consecuencia. Puede ser doloroso rápidamente, pero si está en x86 o si la función original tiene un perímetro bien definido y limitado, puede funcionar. Todavía será un hack , úsalo para propósitos de depuración. No construyas tu software alrededor de eso. De todos modos, aquí hay un ejemplo de trabajo en x86:
Por alguna razón, no puede usar flotantes con va_arg, gcc dice que se convierten a doble pero el programa falla. Eso por sí solo demuestra que esta solución es un truco y que no hay una solución general. En mi ejemplo supuse que el número máximo de argumentos era 8, pero puede aumentar ese número. La función envuelta también solo usaba enteros, pero funciona de la misma manera con otros parámetros 'normales' ya que siempre se convierten a enteros. La función de destino conocerá sus tipos, pero su envoltorio intermediario no necesita saberlo. El contenedor tampoco necesita saber el número correcto de argumentos ya que la función de destino también lo sabrá. Sin embargo, para hacer un trabajo útil (excepto solo registrar la llamada), probablemente tenga que saber ambos.
fuente
gcc ofrece una extensión que puede hacer esto:
__builtin_apply
y parientes. Consulte Construcción de llamadas de función en el manual de gcc.Un ejemplo:
Pruébalo en Godbolt
Hay algunas precauciones en la documentación que podrían no funcionar en situaciones más complicadas. Y tienes que codificar un tamaño máximo para los argumentos (aquí usé 1000). Pero podría ser una alternativa razonable a los otros enfoques que implican diseccionar la pila en lenguaje C o ensamblador.
fuente
Hay esencialmente tres opciones.
Una es no transmitirla, sino utilizar la implementación variada de la función de destino y no transmitir las elipses. El otro es usar una macro variadic. La tercera opción es todo lo que me falta.
Por lo general, voy con la opción uno, ya que siento que esto es realmente fácil de manejar. La opción dos tiene un inconveniente porque hay algunas limitaciones para llamar a macros variables.
Aquí hay un código de ejemplo:
fuente
La mejor manera de hacer esto es
fuente
No estoy seguro de si esto ayuda a responder la pregunta de OP, ya que no sé por qué se aplica la restricción para usar una función auxiliar similar a vfprintf en la función de contenedor. Creo que el problema clave aquí es que reenviar la lista de argumentos variables sin interpretarlos es difícil. Lo que es posible es realizar el formateo (usando una función auxiliar similar a vfprintf: vsnprintf) y reenviar la salida formateada a la función ajustada con argumentos variables (es decir, sin modificar la definición de la función ajustada). Así que, aquí vamos:
Encontré esta solución aquí .
fuente