0

I am receiving a string (e.g: "3 10 ABC") from a function and want to pass it to another function like function(3,10,ABC).

I tried to capture it in an array.

But there may be variable number of arguments so is there any way to do this using va_list?

bcperth
  • 2,191
  • 1
  • 10
  • 16
  • Use dynamic allocation and reallocation to create the "array"? – Some programmer dude Nov 02 '18 at 03:05
  • Allocating is not necessary. Just find spaces and then pass pointers. And yes, you have to use varargs. – Hoblovski Nov 02 '18 at 03:08
  • @Hoblovski That's not possible to do at run-time, as the amount of arguments to a function is fixed at compile-time. – Some programmer dude Nov 02 '18 at 03:13
  • " I tried to capture it in an array" --> post that code. – chux - Reinstate Monica Nov 02 '18 at 03:19
  • @Hoblovski can you share how do to it using varags. – Vijay Dubey Nov 02 '18 at 03:23
  • If you are receiving `"3 10 ABC"` and simply need to split it into three separate strings, then look at `strtok` or simply use a pair of pointers and pick out the individual space separated groups of characters.. If you simply need to pass the string `"3,10,ABC"` as a single `char*` argument, then if the string you receive isn't a string-literal, simply replace the spaces with `','`, otherwise, make a copy first and do the same. Please provide [**A Minimal, Complete, and Verifiable Example (MCVE)**](http://stackoverflow.com/help/mcve) of what you have tried and the function declaration. – David C. Rankin Nov 02 '18 at 04:14

2 Answers2

0

va_list is an interface to the underlying variable number of arguments implementation, and isn’t a method of constructing a variable length array. Although intriguing, part of va_* is doing things like spilling register based calling conventions into a traditional array, which makes it too cumbersome to rely upon for what you want.

Constructing an array of the sort you are interested in isn’t difficult. Your question indicates you want mixed types; that implies that each member has to be “fat”:

struct Element {
     enum { Int, String, Other }  Type;
     union {
           long   val;
           char   *str;
           void   *anon;
     };
};

Then you just want to (re)allocate an array of them until your parsing is done. Presuming your parsing is space delimited, you can use a traditional:

for (s = strtok(str, “ \t”); s; s = strtok(NULL, “ \t”)) {
     struct Element *e = Extend(&EList);

     if (isdigit(*s)) {
          e->type = Int;
          e->val  = strtol(s, NULL, 0);
     } else if (isalpha(*s)) {
          e->type = String;
          e->str = strdup(s);
     } else {
          e->type = Other;
          e->anon = strdup(s);
     }
}
...

And finally, you want a way to construct the list:

struct ElementList {
      struct Element *list;
      int             nalloc;
      int             nused;
};
struct Element *Extend(struct ElementList *el) {
     if (el->nused == el->nalloc) {
         int nsize = el->nalloc ? el->nalloc * 3 / 2 : 10 ;
         struct Element *t = realloc(el->list, sizeof(*t) * nsize);
         if (t == NULL) {
              return NULL;
         }
         el->nalloc = nsize;
         el->list = t;
     }
     return el->list + el->nused++;
}
#define ELIST_INITIALIZER { .list = NULL, .nalloc = 0, .nused = 0 }

ps/disclaimer: I wrote this in this editor, it likely has small problems, but should illustrate what you want to do.

mevets
  • 10,070
  • 1
  • 21
  • 33
0

The short answer is No. va_list requires you to determine the number of arguments on your own. So if you wanted to call:

function(3, "3", "10", "ABC");

Where the first argument is the count, that's one way to do it. Functions like printf() et.al. get around this by counting the number of % in the format string. But we could do something like look for an ending-tag of NULL:

function("3", "10", "ABC", NULL);

Giving the code:

void function(const char *first, ...)
{
    va_list ap;
    const char *arg = first;

    va_start(ap, first);
    while (arg != NULL)
    {
        /* do whatever with <arg> */
        printf("Argument: [%s]\n", arg);

        /* get the next argument */
        arg = va_arg(ap, char*);
    }
    va_end(ap);
}

EDIT: Brute-force, but incomplete approach (handles up to 20 arguments), based upon idea found in the comment, and elsewhere.

#define TWENTY2TH_ARGUMENT(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, ...) arg22
#define COUNT_ARGUMENTS(...) TWENTY2TH_ARGUMENT(dummy, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

void _printArgs2(size_t count, ...)
{
    va_list ap;
    const char *arg;

    va_start(ap, count);
    for (size_t i=0; i<count; i++)
    {
        /* get the next argument */
        arg = va_arg(ap, char*);

        /* do whatever with <arg> */
        printf("Argument: [%s]\n", arg);
    }
    va_end(ap);
}

#define printArgs2(...) _printArgs2(COUNT_ARGUMENTS(__VA_ARGS__), ##__VA_ARGS__)

int main(void)
{
    printArgs2("One");
    printf("\n");

    printArgs2("One", "Two");
    printf("\n");

    printArgs2("One", "Two", "Three");
    printf("\n"); 
}
Kingsley
  • 14,398
  • 5
  • 31
  • 53
  • Actually maybe I'm incorrect: https://stackoverflow.com/a/9204947/1730895 (well it's a work-around) – Kingsley Nov 02 '18 at 03:46