Newsgroup: it.comp.lang.c
Titolo: va_list e function pointer

Salve a tutti.
Sapete se per caso è possibile avere in C una funzione a numero
arbitrario di parametri (stdarg.h) con function pointer come
parametri
opzionali?
Il mio dubbio è cosa mettere come tipo in va_arg e cosa mettere come
nome di variabile in va_start.
Grazie!


La risposta direi che è banale.. hai provato? :-)
Visto che può essere difficile utilizzare tipi così complessi ti consiglio dei typedef.
Ciao!


Ti faccio cut&paste di un esempio tratto dall'help del Borland C++:
 
#include <stdio.h>
#include <stdarg.h>
 
/* calculate sum of a 0 terminated list */
void sum(char *msg, ...)
{
   int total = 0;
   va_list ap;
   int arg;
   va_start(ap, msg);
   while ((arg = va_arg(ap,int)) != 0) {
      total += arg;
   }
   printf(msg, total);
   va_end(ap);
}
 
int main(void) {
   sum("The total of 1+2+3+4 is %d\n", 1,2,3,4,0);
   return 0;
}
 
 
Spero possa esserti utile.
-- 
andrea - ar italy, com


Beh, la domanda non era così semplice...
La domanda (da come l'ho capita) è se era possibile passare come argomenti di una funzione a variabili multiple un puntatore a una funzione che prende parametri multipli. Detto meglio e chiarificando l'ambiguità linguistica, se  va_args riusciva a gestire i puntatori a funzione (i parametri che prende una funzione per il C sono indifferenti, l'importante è che compilatore/stdlib riescano a gestire un puntatore della dimensione sufficente).
La domanda è interessante (e la riposta è certamente sì), ma va IMHO scoperta leggendo cosa effettivamente fanno le macro di stdargs.h (se si è su un sistema che le implmenta come macro, ovviamente)
Ciao!

 La domanda è interessante (e la riposta è certamente sì), ma va IMHO scoperta leggendo cosa effettivamente fanno le macro di stdargs.h (se si è su un sistema che le implmenta come macro, ovviamente)
 
Nelle 3 implementazioni che ho sotto mano va_arg e' implementata come macro,
quindi il secondo parametro non penso possa essere una semplice definizione
di tipo, pero' ho visto che funziona con il typedef:
 
Se definisco un tipo come puntatore a funzione:
 
  typedef int (*adder_f)(int, int);
 
Poi posso usarlo in va_arg in questo modo:
 
adder_f f;
...
  va_list ap; 
  va_start(ap, msg); 
  f = (adder_f)va_arg(ap, adder_f); // funziona perche trova un identificatore
...
  va_end(ap);
 
se invece di usare adder_f provo questo, non compila:
 
   f = va_arg(ap, (int (*)(int, int)) ); // NON COMPILA
 
 
Esempio completo:
 
#include <stdio.h> 
#include <stdarg.h>
 
typedef int (*adder_f)(int, int);
 
/* calculate sum of a 0 terminated list */ 
void apply(char *msg, ...) 
{ 
adder_f f;
int total; 
   va_list ap; 
   int arg; 
   va_start(ap, msg); 
   f = (adder_f)va_arg(ap, adder_f);
   total = va_arg(ap, int);
   while ((arg = va_arg(ap,int)) != 0) { 
	   total = f(total, arg );
   } 
   printf(msg, total); 
   va_end(ap); 
} 
 
int sum_f( int x, int y )
{
	return x+y;
}
int mul_f( int x, int y )
{
	return x*y;
}
 
int main(int argc, char* argv[])
{
  apply("The total of 1+2+3+4 is %d\n", sum_f, 0, 1,2,3,4,0); 
  apply("The total of 1*2*3*4 is %d\n", mul_f, 1, 1,2,3,4,0); 
 
  return 0;
}
 
ciao
G
 
 
 
 
 

 
 
Sul gcc anche sono macro, ma definite come __builtin
su gcc invece
va_arg(ap, int(*)(int,int));
funziona alla grande
( *non* funziona invece va_arg(ap, (int(*)(int,int))), così come non funziona va_arg(ap, (int)) )
Ciao!

<snip>
 
Probabilmente perche' il right value che si aspette va_arg come
secondo parametro, deve essere un tipo e non una definizione di tipo,
che sono due cose diverse.


<snip>
 
Ho provato su Borland con flag "ANSI compliance" sul compilatore, e si
lamenta.
Si vede che gcc e` di "bocca buona" e risolve le definizioni di tipi
in tipi per i fatti suoi, oppure Borland non accetta la definizione di
un tipo puntatore a funzione, come tipo. Sarebbe interessante, giusto
dal punto di vista teorico e ANSI C, sapere chi dei due si sta
comportando correttamente.
 
int x(int(*a)(int,int)){
  return 1;
}
 
Viene correttamente accettato da tutti e due.


Dopo una piccola ricerca  sul sito opengroup (IEEE), ho trovato una
risposta che ritengo valida:
 
-
The va_arg() macro shall return the next argument in the list pointed
to by ap. Each invocation of va_arg() modifies ap so that the values
of successive arguments are returned in turn. The type parameter shall
be a type name specified such that the type of a pointer to an object
that has the specified type can be obtained simply by postfixing a '*'
to type. If there is no actual next argument, or if type is not
compatible with the type of the actual next argument (as promoted
according to the default argument promotions), the behavior is
undefined
-
 
In pratica, se si scrive la definizione di tipo, cosa accettata dal
gcc, il parametro 'type' e` malformato (int(*)(int,int) *X non ha
senso, mentre mytypedef *X si, con mytypedef l'equivalente creato con
typedef), quindi il behavior di va_arg e` undefined.
 
Nel caso di gcc, se si scrive la definizione di tipo come parametro
'type', il behavior di gcc e` semplicemente di trattarla come tipo.
 
Link:
http://pubs.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html
 


 andrea - ar italy, com
 
Grande, questa è di sicuro la risposta migliore! ;)
Ciao!
 
P.S. Adesso come adesso ho rivisto lo stdarg.h che mi porto dietro sui codici embedded, ed in effetti è fatto così (chissà dove l'ho preso poi, non credo di averlo scritto io, ma tutto può essere)
 
#ifdef __GNUC__
/* ... */
#    define va_arg(v, l) __builtin_va_arg(v, l)
#else
/* ... */
    #define va_arg(v, l) \
        (*(((l*)(v=(va_list)((l*)v)+1))-1))
#endif
 
Dove in effetti si vede che se l non è un "nome di tipo puntatorabile" il risultato è qualcosa di non compilabile.
Se non usassi sempre typedef per i puntatori a funzione, forse me ne sarei pure accorto prima :)

Se non usassi sempre typedef per i puntatori a funzione, forse me ne sarei pure accorto prima :)
 
Lo faccio sempre anch'io, penso che sia cosa buona e giusta! :)
