1

I was wondering if it's possible in C to create a variadic function that takes different types of arguments. i.e.

void fillDatabase(char* name, int age){
  writeToDatabase(name, age);
}

int main(){
  fillDatabase("Paul", 19);
  fillDatabase("Herbert");
}

Here I am trying to fill up a database with names and ages. But it's also possible to fill it up with only a name and no age. So I am wondering if I could use the same function for that or if I have to write two different ones?

Online I could only find examples for variadic functions with the same type of arguments.

Thanks in advance!

12
  • 3
    I think you're mixing up the concepts of variadic functions and function overloading. Commented Nov 16, 2015 at 18:44
  • 5
    printf is a variadic function that can take different types of arguments... Note that you would need to provide some means of explicitly providing the argument types (which printf-like functions do via the format string). Commented Nov 16, 2015 at 18:46
  • 2
    Just write two functions. Writing a variadic function, using a struct, union or any other trick just asks for trouble. Seriously. Commented Nov 16, 2015 at 18:50
  • 2
    Guys, if somebody asks you to help him shoot himself in the foot, you don't give him a gun... at the very least not without asking for a good reason... Commented Nov 16, 2015 at 18:56
  • 1
    ok, thanks, I'll just use two functions Commented Nov 16, 2015 at 19:01

2 Answers 2

4

In C99, variadic functions use stdarg(3) operations (generally implemented as macros expanding to compiler-specific magical stuff, e.g. GCC builtins). The first arguments should have some fixed known type, and generally determinates how the other arguments are fetched; in several today's ABIs, variadic function argument passing don't use registers so is less efficient than fixed arity function calls.

In your case, you'll better have several functions. For inspiration sqlite has several open functions.

Notice that POSIX defines some function with one last optional argument, in particular open(2). This is probably implemented as having a function whose last argument can be not supplied.

You could read the ABI specification and the calling conventions specific to your implementation.

BTW, lack of function overloading (like in C++) can be understood as an advantage of C (the function name -which is the only thing the linker care about, at least on Linux & Unix- determines its signature). Hence C++ practically needs name mangling. Read also about dynamic linking and dlopen

Sign up to request clarification or add additional context in comments.

5 Comments

Traditionally, no prototype for open() was supplied so programmers could call it with two or three arguments as needed. Nowadays, open() is declared as int open(const char*, int, ...); and uses variable arguments.
In practice, not really.
So POSIX is wrong in this regard?
No, I thought that implementations of open don't use va_start, but in musl-libc open.c va_start is used. So I was wrong. IIRC, some old versions of Glibc didn't do that (but I might be wrong too)
As I said, old implementations of open() just didn't supply a prototype and defined open() with a K&R-style definition and three arguments. The argument count mismatch is undefined behaviour, but works just fine on typical Unices as long as you never touch arguments that weren't supplied.
2

Check the man page for va_list, va_start, etc.

You declare your function prototype using the ... sequence to denote variable args:

void myfunction(FILE *, *fmt, ...);

For example, we declatre a struct of type va_list (assuming you included stdarg):

struct va_list l;

Initialise the list using the parameter BEFORE the ... - se we'd pass the *fmt pointer to va_start:

va_start(l, fmt);

Now, we can call va_arg to get a value...if we're processing a format string we might have a loop something like this:

while (*fmt) {
    /* Look for % format specifier... */
    if (*fmt == '%') {
        /* Following character can be c for char, i for int or f for float. */
        // Opps - advance to char following opening %...
        fmt++;

        if (*fmt == 'c')
            fprintf(stream, "%c", (char) va_arg(l, int));
        else if (*fmt == 'i')
            fprintf(stream, "%i", (int) va_arg(l, int));
        else if (*fmt == 'f')
            fprintf(stream, "%f", (float) va_arg(l, double));
        ... and so on...

You're relying on the character following the % to tell you what data type to expect in the corresponding parameter. Suggest you read the man pages for va_list.

2 Comments

So how would you apply all this knowledge for the particular problem?
Well...I don't really know the ins and outs of the particular problem I'm not the OP. Let the OP decide if my answer is adequate for his/her purposes, if it is then great...if not then sorry, keep looking. I don't really know what you want or expect me to tell you...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.