La macro va_arg

Utilisation

va_arg( aVaList, parameterType );

Cette macro (attention, ce n'est pas une fonction) permet d'extraire le paramètre suivant. Comme ce paramètre n'est pas nommé, et donc pas typé, il est nécessaire de passer l'information sur le type de ce paramètre : c'est notamment pour cette raison que va_arg ne peut pas être une fonction (il n'est pas possible de passer un type à un appel de fonction). Durant la substitution de la macro, un sizeof( parameterType ) sera produit afin de connaitre le nombre d'octets à lire.

En conséquence, il faut aussi bien mémoriser qu'un cast est requis pour correctement typer le résultat de cette macro, en fonction du type du paramètre considéré.

Paramètres

  • aVaList : une variable de type va_list que la macro va initialiser.
  • parameterType : le type du paramètre à extraire (int, double, ... ).

Exemple de code

L'exemple ci-dessous vous montre une version extrêmmement simplifiée de la fonction printf. Effectivement, seul trois types de données peuvent être injectés dans le format : char *, double et int. De plus, les possibilités de formatage telles que %5.2f, par exemple, ne sont pas supportées.

Néanmoins le nombre de paramètres passé à la fonction myPrintf est directement lié au nombre de caractères % présent dans le format. Le type de données à extraire via va_arg est lui aussi dépendant des informations stockées dans le format. Un très bon courage à ceux qui voudront poursuivre l'implémentation de cette fonction afin de fonctionner à l'identique de la fonction printf.

#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>


size_t displayInteger( int value ) {
    if ( value < 10 ) {
        putchar( 0x30 + value );
        return 1;
    }
    size_t size = displayInteger( value / 10);
    putchar( 0x30 + value % 10 );
    return 1 + size;
}


/*
 * This function is a very simplified version of printf.
 * The only supported formats are:
 *      %s : inject a string
 *      %d : inject an integer value
 *      %f : inject a double value
 */
int myPrintf( const char * format, ... ) {

    va_list parametersInfos;
    /* Initialize the va_list structure */
    va_start( parametersInfos, format );

    size_t writtenCharacters = 0;
    char currentChar;

    while( (currentChar = *format) != '\0' ) {
        format ++;
        if ( currentChar != '%' ) {
            putchar( currentChar );
            writtenCharacters ++;
            continue;
        }

        switch( *format ++ ) {
            case 'd':
                {
                    int integerValue = (int) va_arg( parametersInfos, int );
                    writtenCharacters += displayInteger( integerValue );
                }
                break;
            case 'f':
                {
                    double doubleValue = (double) va_arg( parametersInfos, double );
                    int leftPart = (int) doubleValue;
                    writtenCharacters += displayInteger( leftPart );
                    doubleValue -= leftPart;

                    putchar( '.' );
                    writtenCharacters ++;

                    while( ( doubleValue - (int) doubleValue ) > 10e-6 ) {
                        doubleValue *= 10;
                    }
                    writtenCharacters += displayInteger( (int) doubleValue );
                }
                break;
            case 's':
                {
                    const char * string = (const char *) va_arg( parametersInfos, const char * );
                    while( *string != '\0' ) {
                        putchar( *string ++ );
                        writtenCharacters ++;
                    }
                }
                break;
            default:
                fprintf( stderr, "Specified format is not supported!" );
                abort();
        }
    }

    /* Release va_list resources */
    va_end( parametersInfos );

    return writtenCharacters;
}


int main( int argc, char * argv[] ) {

    size_t size = myPrintf( "Begin %s End\n", "Middle" );
    assert( size == 17 );

    size = myPrintf( "Begin %d End\n", 123 );
    assert( size == 14 );

    size = myPrintf( "Begin %f End\n", 123.456 );
    assert( size == 18 );

    size = myPrintf( "Begin %s %d %f End\n", "toto", 421, 123.456 );
    assert( size == 27 );

    return EXIT_SUCCESS;

}

Voici les résultats produits par l'exécution de ce programme.

$> sample
Begin Middle End
Begin 123 End
Begin 123.456 End
Begin toto 421 123.456 End
$>

Sujets connexes

va_copy
va_end
va_list
va_start