domingo, 28 de junho de 2009

Variaridade

GCC Uma função variádica variária – em inglês variadic, variable arity, aridade¹ variável – é aquela que suporta uma quantidade variável de parâmetros.

Muitas linguagens suportam funções variárias, aliás de forma bem simples. Python usa o operador * para indicar quantidade variável de parâmetros, Lua usa o operador ... e Common Lisp o operador &rest.

Outras linguagens podem ser ainda mais simples, como por exemplo Perl, onde toda função é variária e os parâmetros são recebidos na lista @_, tradicionalmente capturados por shift.

Java não suporta funções verdadeiramente variárias devido a sua limitação forçada de tipagem, no entanto é possível simular com o uso de Object e casting («vazamento» na falta de uma tradução melhor):
void myFunction(Object... args) {
// Código do método

}


Funções variárias em C/C++


C/C++ usa o cabeçalho stdarg.h para suporte a funções variárias.

O exemplo apresentado na Wikipédia é bastante simples: uma função printargs que recebe uma quantidade arbitrária de números inteiros, encerrando com -1 (ou qualquer número negativo em nosso exemplo), e os exibe na saída padrão.

Precisamos incluir dois cabeçalhos em C++, cstdarg para suporte a funções variária e cstdio para exibir o resultado:
#include <cstdio>
#include <cstdarg>


Ou, em C:
#include <stdio.h>
#include <stdarg.h>


A assinatura da função fica assim:
void printargs(int arg1, ...) {


Para acesso aos parâmetros múltiplos é usado um objeto va_list:
    va_list args;


A função va_start() inicializa o objeto. Ela recebe dois parâmetros: o objeto va_list e o nome do último argumento antes da lista:
    va_start(args, arg1);


Portanto é preciso haver pelo menos um argumento antes da lista variável. Podemos então exibir o primeiro parâmetro, recebido no argumento arg1:
    printf("%d", arg1);


A função va_arg() retorna o argumento seguinte. Ela recebe como parâmetros o objeto va_list e o tipo do parâmetro da lista a ser recuperado.

O tipo precisar ser plenamente promovido, ou seja, ponteiro, inteiro, ponto flutuante ou precisão dupla. Outros tipos, como char precisam sofrer casting, como por exemplo (não faz parte do código de printargs):
char c = static_cast<char>(va_arg(va, int));


Continuando o código, podemos agora reiterar sobre os resultados de va_arg() até encontrarmos o valor de parada negativo:
    int arg;
while((arg = va_arg(args, int)) >= 0)
printf(" %d", arg);


Após o fim da reiteração dos parâmetros obtidos do objeto va_list através de va_arg(), é preciso encerrar o objeto:
    va_end(arg);
printf("\n");
}


Caso você queira restringir o formato de entrada da função, isso é possível usando __attribute__ em sua declaração. Por exemplo, se a função log() recebe o nível de log como primeiro parâmetro, uma string de formatação como segundo parâmetro (2) e os parâmetros variáveis a partir do terceiro (3), com formato similar ao da função printf(), isso é feito com a seguinte assinatura:
extern void
log(int level, cons char *fmt, ...)
__attribute__((format(printf, 2, 3)));


As opções para format() são printf, scanf, strftime e strfmon. Veja Declaring Attributes of Functions da documentação do GCC.

[]'s
Cacilhas, La Batalema


¹Aridade: em Matemática é o número de operandos de uma função.