2

I'm trying to write a function, with a variable number of integer/int array parameters, that concatenates all the elements into a single 1-dimensional array. I'm struggling with one of the two scenarios where current_item turns out to be an array instead of just an integer. How can I access the individual elements of this array and assign them to pOutList?

typedef unsigned short      WORD;

int PinListJoin(WORD * pOutList, ...) {

    int index=0;
    boolean isArray = false;

    va_list next_item;
    va_start(next_item, pOutList);
    WORD current_item = va_arg(next_item, WORD);
    int current_item_size = sizeof(current_item) / sizeof(WORD);

    if (current_item_size > 1) {
        isArray = true;
        for (int pinidx = 0; pinidx < current_item_size; pinidx++) {
            pOutList[index] = current_item;
            index++;
        }
    }
    else {
        isArray = false;
        pOutList[index] = current_item;
        index++;
    }

    va_end(next_item);

    return(current_item_size);

}
FartVader
  • 143
  • 1
  • 3
  • 13
  • 2
    You should be using C++ templates, instead of C's variadic parameter hacks. As shown, this cannot actually be done with C's variadic parameter hack, since your C function has no knowledge, whatsoever, however many actual parameters it received. It must deduce it, somehow, which gets messy, and is error prone. On the other hand, this is exactly what C++ templates are for. Have you considered using proper C++ templates, instead of the prehistoric C variadic parameter macros? You should realize that the shown code is fatally flawed, because `current_item_size` will always be obviously 1. – Sam Varshavchik Dec 29 '19 at 04:41
  • It's completely new to me, variadic template. Looking at some examples right now. – FartVader Dec 29 '19 at 04:54
  • Finding [examples](https://en.cppreference.com/w/cpp/language/parameter_pack) is easy...once you know 2) what you're looking for and 2) where to look for it :D. –  Dec 29 '19 at 04:58
  • What I know at the moment is to declare as follows: template int PinListJoin(Arg1 pOutList, Args ...args) I'm struggling on definition. and grappling with the idea that recursion is necessary – FartVader Dec 29 '19 at 05:12
  • So, do I need to define both the "base case" and the "recursive case" functions? – FartVader Dec 29 '19 at 05:15
  • Yes, I believe recursion is how that is usually handled. It's technically a "specialization" of the vardic function, I believe. –  Dec 29 '19 at 05:40
  • You seem to be writing C code, not C++ code. If that is your intent, you should change the tag. – Chris Dodd Dec 29 '19 at 05:40
  • What you need to do is [to read a good C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) that explains how to use parameter packs. This is one of the most complicated parts of C++, and simply cannot be fully explained in a brief one or two paragraph answer on stackoverflow.com – Sam Varshavchik Dec 29 '19 at 22:48

1 Answers1

1
boolean isArray = false;

There is no boolean datatype in C++.

Also, the value of neither isArray nor index variable is ever used in the function.

typedef unsigned short      WORD;
WORD current_item = va_arg(next_item, WORD);

This isn't going to work. Variadic arguments are promoted. unsigned short promotes to int (typically; on some exotic system, it might be unsigned int). Using the non-promoted type with va_arg will cause undefined behaviour. You could use a trick like this to get the correct type for any system:

using promoted_word = decltype(+WORD(0));
WORD current_item = WORD(va_arg(next_item, promoted_word));
WORD current_item = va_arg(next_item, WORD);
int current_item_size = sizeof(current_item) / sizeof(WORD);

The size of WORD divided by the size of WORD is always 1. There's no point in doing this.

I'm struggling with one of the two scenarios where current_item turns out to be an array instead of just an integer. How can I access the individual elements of this array and assign them to pOutList?

Function argument cannot be an array. But it can be a pointer to an element of an array, which I assume is what you mean. If that is the case, then you can get the pointer out of varargs like this:

WORD* current_item = va_arg(next_item, WORD*);

You can then copy the elements from array to array just like you would using any pointer to an element.

There's still two problems though: 1. There is no way of finding out the size of the array based on that pointer and 2. There is no way of finding out what type of arguments were passed (i.e. whether it was a( pointer to a)n array or an integer). You can take a look at the interface of printf for an idea of how that problem may be solved. It is solved there using a format string where those types are specified. The length of the array is solved by using a sentinel value (the null terminator character). Another approach is to pass the length as a separate argument.


More generally though: I recommend that you not use C style varargs at all in C++. It's just too easy to shoot yourself in the foot. Variadic templates are much safer and more powerful way to achieve similar things.

That said, I don't quite understand what you're attempting to do, so I cannot confirm whether it makes sense to use any form of variadics.

eerorika
  • 232,697
  • 12
  • 197
  • 326