Passing an ellipsis to another variadic function [duplicate]
Asked Answered
F

4

75

I have approximately 30 variadic functions. Each one accepts a path as the final argument, e.g.:

bool do_foo(struct *f, int q, const char *fmt, ...)

In each function, I have to check that the expanded format is less then or equal to a certain size. So, I find myself copy / pasting the same chunk of code to check for how many characters vsnprintf() didn't print, set errno accordingly and bail out of the write.

What I would like to do is write a function to do this, which would return a statically allocated (expanded) string that is known to be a safe size, or newly initialized string on failure, which could be checked against NULL. The checks also have to determine if the string is an absolute or relative path, which influences the safe size of the string. It's a lot of duplicate code and it's starting to smell.

Is there a way that I can pass the contents of the elipsis from my function's entry to another function? Or do I have to call va_start() first, and then pass the va_list to the helper function?

Edit:

I am not at all opposed to passing the va_list to the helper, I just wanted to make sure that nothing else existed. It seems to me the compiler understands where the variadic arguments begin, so I was just curious if I could tell it to pass them along.

Filet answered 30/3, 2009 at 4:10 Comment(2)
Is there any reason you are adverse to passing the va_list to the helper?Brote
I have had to do similar type things but had to strip off a few of the args... wasn't pleasant code to maintain.Brote
C
76

You can't, you can only pass the arguments as a va_list. See the comp.lang.c FAQ.

In general, if you're writing variadic functions (that is, functions which take a variable number of arguments) in C, you should write two versions of each function: one which takes an ellipsis (...), and one which takes a va_list. The version taking an ellipsis should call va_start, call the version taking a va_list, call va_end, and return. There's no need for code duplication between the two versions of the function, since one calls the other.

A simple use-case could be:

int vadd(int num_count, va_list args) {
    int sum = 0;
    for(int k = 0; k < num_count; k++) {
        sum += va_arg(args, int);
    }

    return sum;
}

int add(int num_count, ...) {
    va_list args;
    int sum = 0;

    va_start(args, num_count);
    sum = vadd(num_count, args);
    va_end(args);

    return sum;
}
Colman answered 30/3, 2009 at 4:21 Comment(2)
What should I do if I need to do stuff with va_list twice? Normally, I'd have to call va_end and va_start between usages (otherwise it's UB), but I can't call it in a function that takes va_list: ‘va_start’ used in function with fixed args.Pneumoconiosis
Nevermind, figured it out - I need to use va_copy for every subsequent usage.Pneumoconiosis
S
12

Probably you can use variadic macros - like this:

#define FOO(...)  do { do_some_checks; myfun(__VA_ARGS__); } while (0)

NB! Variadic macros are C99-only

Strephon answered 30/3, 2009 at 6:0 Comment(2)
I was looking at those, that saves me from having to add a wrapper around the function that actually does the write.Filet
c99 is no problem, my program is rather gcc / linux specificFilet
L
1

I don't know if this will help, you can access the variables by reference. This is kind of a sneaky trick, but it unfortunately won't allow you to use ellipsis in the final function definition.

#include <stdio.h>

void print_vars(int *n)
{
  int i;
  for(i=0;i<=*n;i++)
    printf("%X %d  ", (int)(n+i), *(n+i));
  printf("\n");
}

void pass_vars(int n, ...)
{
  print_vars(&n);
}

int main()
{
    pass_vars(4, 6, 7, 8, 0);
    return 0;
}

On my pc it outputs

$ ./a.out
BFFEB0B0 4  BFFEB0B4 6  BFFEB0B8 7  BFFEB0BC 8  BFFEB0C0 0
Lenni answered 1/6, 2012 at 4:53 Comment(1)
Not portable...Talkie
D
0

You have to pass va_list to the helper.

Derwin answered 30/3, 2009 at 4:15 Comment(2)
This answer is way to terse to be useful. It should either be expand with additional useful details or removed.Stephainestephan
@rjstelling I found this answer useful when I asked the question. If you notice, I indicated that I basically acquiesced to passing the va_list, but felt like there might be a different way of doing it. Andrew pretty much confirmed that there wasn't one (no compiler magic I could use). While other answers went into more depth, this did sufficiently answer my question.Filet

© 2022 - 2024 — McMap. All rights reserved.