Em C, é possível encaminhar a invocação de uma função variável? Como em,
int my_printf(char *fmt, ...) {
fprintf(stderr, "Calling printf with fmt %s", fmt);
return SOMEHOW_INVOKE_LIBC_PRINTF;
}
O encaminhamento da invocação da maneira acima obviamente não é estritamente necessário neste caso (já que você pode registrar invocações de outras maneiras ou usar vfprintf), mas a base de código em que estou trabalhando exige que o wrapper faça algum trabalho real, e não possui (e não pode ter adicionado) uma função auxiliar semelhante ao vfprintf.
[Atualização: parece haver alguma confusão com base nas respostas que foram fornecidas até agora. Para formular a pergunta de outra maneira: em geral, você pode agrupar alguma função variável arbitrária sem modificar a definição dessa função .]
Respostas:
Se você não tiver uma função análoga à
vfprintf
que exige umva_list
número variável de argumentos, não poderá fazê-lo . Vejohttp://c-faq.com/varargs/handoff.html .Exemplo:
fonte
Não diretamente, no entanto, é comum (e você encontrará quase universalmente o caso na biblioteca padrão) para funções variadas virem em pares com uma
varargs
função alternativa de estilo. por exemploprintf
/vprintf
As funções v ... assumem um parâmetro va_list, cuja implementação geralmente é feita com 'macro magic' específica do compilador, mas você tem certeza de que chamar a função estilo v ... de uma função variável como esta funcionará:
Isso deve lhe dar o efeito que você está procurando.
Se você estiver pensando em escrever uma função de biblioteca variável, considere também disponibilizar um companheiro de estilo va_list como parte da biblioteca. Como você pode ver na sua pergunta, pode ser útil para seus usuários.
fonte
va_copy
antes de passar amyargs
variável para outra função. Consulte o MSC39-C , onde afirma que o que você está fazendo é um comportamento indefinido.va_arg()
umva_list
que tenha um valor indeterminado", não faço isso porque nunca usova_arg
na minha função de chamada. O valor demyargs
após a chamada (neste caso) paravprintf
é indeterminado (supondo que ele nos façava_arg
). A norma diz quemyargs
"deve ser passado para ava_end
macro antes de qualquer referência adicional a [it]"; que é exatamente o que eu faço. Não preciso copiar esses argumentos, porque não tenho a intenção de iterá-los na função de chamada.ap
for passado para uma função que usava_arg(ap,type)
, o valor deap
é indefinido após o retorno dessa função.O C99 suporta macros com argumentos variados ; dependendo do seu compilador, você poderá declarar uma macro que faz o que deseja:
Em geral, porém, a melhor solução é usar a forma va_list da função que você está tentando agrupar , caso exista.
fonte
Quase, usando as facilidades disponíveis em
<stdarg.h>
:Observe que você precisará usar a
vprintf
versão em vez de simplesprintf
. Não existe uma maneira de chamar diretamente uma função variável nesta situação sem usarva_list
.fonte
Como não é realmente possível encaminhar essas chamadas de maneira agradável, resolvemos isso configurando um novo quadro de pilha com uma cópia do quadro de pilha original. No entanto, isso é altamente não transportável e faz todos os tipos de suposições , por exemplo, que o código usa ponteiros de quadro e as convenções de chamada 'padrão'.
Esse arquivo de cabeçalho permite agrupar funções variadas para x86_64 e i386 (GCC). Ele não funciona para argumentos de ponto flutuante, mas deve ser direto para estender o suporte a eles.
No final, você pode encerrar chamadas assim:
fonte
Use vfprintf:
fonte
Não há como encaminhar essas chamadas de função porque o único local onde você pode recuperar os elementos brutos da pilha está
my_print()
. A maneira usual de agrupar chamadas dessa maneira é ter duas funções, uma que apenas converte os argumentos em váriasvarargs
estruturas, e outra que realmente opera sobre essas estruturas. Usando esse modelo de função dupla, você pode (por exemplo) quebrarprintf()
, inicializando as estruturasmy_printf()
comva_start()
e depois passá-las paravfprintf()
.fonte
Sim, você pode fazê-lo, mas é um pouco feio e você precisa saber o número máximo de argumentos. Além disso, se você estiver em uma arquitetura em que os argumentos não sejam passados na pilha como o x86 (por exemplo, PowerPC), será necessário saber se tipos "especiais" (double, floats, altivec etc.) são usados e se então, lide com eles de acordo. Pode ser doloroso rapidamente, mas se você estiver no x86 ou se a função original tiver um perímetro bem definido e limitado, ela poderá funcionar. Ainda será um hack , use-o para fins de depuração. Não crie seu software em torno disso. De qualquer forma, aqui está um exemplo de trabalho no x86:
Por alguma razão, você não pode usar carros alegóricos com va_arg, o gcc diz que eles são convertidos para o dobro, mas o programa trava. Isso por si só demonstra que esta solução é um hack e que não há uma solução geral. No meu exemplo, assumi que o número máximo de argumentos era 8, mas você pode aumentar esse número. A função empacotada também usou apenas números inteiros, mas funciona da mesma maneira com outros parâmetros 'normais', pois eles sempre são convertidos em números inteiros. A função de destino conhecerá seus tipos, mas seu invólucro intermediário não precisa. O wrapper também não precisa saber o número certo de argumentos, pois a função de destino também o conhecerá. Para fazer um trabalho útil (exceto apenas registrar a chamada), você provavelmente precisará conhecer as duas coisas.
fonte
O gcc oferece uma extensão que pode fazer isso:
__builtin_apply
e parentes. Consulte Construindo chamadas de função no manual do gcc.Um exemplo:
Experimente no godbolt
Existem alguns cuidados na documentação que podem não funcionar em situações mais complicadas. E você tem que codificar um tamanho máximo para os argumentos (aqui eu usei 1000). Mas pode ser uma alternativa razoável às outras abordagens que envolvem dissecar a pilha na linguagem C ou assembly.
fonte
Existem essencialmente três opções.
Uma é não repassá-lo, mas usar a implementação variável de sua função de destino e não repassar as elipses. O outro é usar uma macro variável. A terceira opção é tudo o que está faltando.
Eu costumo ir com a opção um, porque eu sinto que isso é realmente fácil de manusear. A opção dois tem uma desvantagem, porque existem algumas limitações para chamar macros variadicas.
Aqui está um exemplo de código:
fonte
A melhor maneira de fazer isso é
fonte
Não tenho certeza se isso ajuda a responder à pergunta do OP, pois não sei por que a restrição de uso de uma função auxiliar semelhante a vfprintf na função wrapper se aplica. Penso que o principal problema aqui é que é difícil encaminhar a lista de argumentos variados sem interpretá-los. O que é possível é executar a formatação (usando uma função auxiliar semelhante a vfprintf: vsnprintf) e encaminhar a saída formatada para a função agrupada com argumentos variados (isto é, não modificar a definição da função agrupada). Aqui vamos nos:
Me deparei com esta solução aqui .
fonte