4

In a function like printf, we use stdarg.h to handle the variadic parameters.

void print(int args,...){
    va_list ap;
    va_start(ap, args);
    int i = 0;
    for(i=0; i<args; i++){
        printf("%d\n",va_arg(ap, int));
    }
    va_end(ap);
}

We want to parse the format list (the first argument given to our variadic function) to track the types of the arguments specified in the format list then, call va_arg with the appropriate type.

I make a first loop to parse the format list, store the specifiers letters into an array. So I know which type we expect and how many there is.

ex: ft_like_printf("Watch your %d %s\n", 6, "Spider pig");

specifiers_list = "ds" So d <=> int and s <=> char* (same specifiers as printf)

But how to code it dynamically? What is the syntax to call va_arg with differents types ?

I have read THIS and THAT which I think are what I'm looking for, aren't they ? If yes, what to do with it ? What are the real case scenarios of a struc containing an enum + union or struct containing an union + function pointer ?

To handle the different data types, I had started with this:

typedef struct s_flist
{
    char c;
    (*f)();
}              t_flist;

t_flist flist[] = 
    {
        { 's',  &putstr  },
        { 'i',  &put_number },
        { 'd',  &put_number }
    };
Community
  • 1
  • 1
Jay
  • 179
  • 1
  • 5
  • 18
  • I don't know what you mean by `But how to code it dynamically? What is the syntax to call va_arg with differents types ?`, I have done something similar so I might help, but I need clarification. – Iharob Al Asimi Dec 15 '14 at 12:12
  • If we have va_arg(ap, int), we can only pass int. So how to use va_arg (ap, TYPE) with a TYPE equal to the type of the current parameter ? – Jay Dec 15 '14 at 12:17
  • Exactly, so `va_arg(ap, char *)` for `char *` and so on. – Iharob Al Asimi Dec 15 '14 at 12:18
  • Did you solve the problem? – Iharob Al Asimi Dec 15 '14 at 15:05
  • Not yet. See, I can't use a lot of ifs or a switch case for all the types. Instead I have to use an array of function pointers like I've shown in my post. I know that I have to use `va_arg(ap, char *)` for `char *` and so on but to do it the way I want is more complex. I have 2 solution for va_arg, in a struct, I will have a function pointer + either 1) a `void *` 2) a `union` , like explained [HERE](http://stackoverflow.com/a/1722238/3206885) and [HERE](http://stackoverflow.com/a/5551632/3206885). Still trying to figure it out ... – Jay Dec 15 '14 at 15:16
  • do you need to pass any arguments to the function pointers? – Iharob Al Asimi Dec 15 '14 at 15:44
  • If you look at the `struct s_flist` I have created, it takes one `char` and one `(*f)()` function pointer. The char represents the letter that the parser gets from `char *format` (1st parameter of the variadic function) `exemple_printf("%s\n", "Hello");`, the function pointer represents each of the corresponding function that print: %c => a char, %s => a string, %d or %i => a number ...etc – Jay Dec 15 '14 at 16:51
  • I edited my previous answer, did you read it? – Iharob Al Asimi Dec 15 '14 at 17:11
  • Looking it right now over diner. Will try it asap. Thanks ! – Jay Dec 15 '14 at 20:16
  • @JeanBaptisteBouhier I updated the answer, see if this one works. – Iharob Al Asimi Dec 21 '14 at 16:35

1 Answers1

3

Types are not first-class citizens in C.

But there are not that many types you have to care about: you can safely cast from unsigned to signed and the other way around, same goes for char* and void*, so for a basic printf, you have to handle:

  • char
  • short
  • int
  • long
  • float
  • double
  • void*

union to the rescue !

typedef union
{
    char as_char;
    short as_short;
    int as_int;
    long as_long;
    float as_float;
    double as_double;
    void* as_ptr;
} t_value;

typedef enum {
    CHAR_T,
    INT_T,
    /* It goes on */
    ...
} t_type;

t_value get_value(va_list ap, t_type type) {
    /* You can't avoid this step, there is no way to iterate over types */
    switch (type) {
        case T_CHAR:
            return va_arg(ap, char);
        case T_INT:
            /* ... */
    }
}

Then you just have to create a lookup table, storing both a function pointer and a t_type for each valid format specifier.