Is this argumentation of the macro arguments tactful?

I knew about macrosVA_NARGS

as described in C Preprocessor, Macro Overloading, etc. for a while, but I was always distracted by the sheer amount of boilerplate it took to get it to work.

I recently needed this functionality, so I gritted my teeth and wrote all the necessary macro codes in all my "glory".

In my particular case, I can consistently rely on all varargs arguments of a particular type. This made me think that there is perhaps a better way using sizeof

and array type. I tried this on my local system and it seems to work. However, I am concerned that this solution might be fragile (besides the type constraint).

My questions are: Is this really a safe and reasonably sane solution to the problem? Or maybe: What problem am I asking, on line if I use this? And finally: To what extent are there problems with trying (see below), are there some settings that can be applied to save the overall approach?

Here's the code as well as a demo function main()

. In this case, the varargs arguments should be ints:

#include <stdio.h>

#define ARG_ARRAY(...) ((int[]) { __VA_ARGS__ })
#define ARG_COUNT(...) (sizeof (ARG_ARRAY(__VA_ARGS__)) / sizeof (int))

#define STUFF(label, ...) \
    stuff(label, ARG_COUNT(__VA_ARGS__), ARG_ARRAY(__VA_ARGS__))

void stuff(char *label, int count, int *values) {
    printf("[%s] count %d", label, count);

    for (int i = 0; i < count; i++) {
        printf("%s %d", (i == 0) ? ":" : ",", values[i]);
    }

    printf("\n");
}

int return1(void) {
    printf("Called `return1()`.\n");
    return 1;
}

int main(int argc, char **argv) {
    STUFF("blort");
    STUFF("frotz", return1());
    STUFF("fizmo", 2 + 3, 6 + 1);
    STUFF("glorf", 99, 999, 999);
    STUFF("igram", 9, 8, 7, 6, 5, 4, 3, 2, 1);
}

      

Here's the transcript:

[blort] count 0
Called `return1()`.
[frotz] count 1: 1
[fizmo] count 2: 5, 7
[glorf] count 3: 99, 999, 999
[igram] count 9: 9, 8, 7, 6, 5, 4, 3, 2, 1

      

The printout is return1()

meant to check that the function is not called twice.


UPDATE:

It was pointed out in the comments that (int[]) { args }

- C99, but not C ++. In my case, I can count on using a C compiler for the code in question, but it's still good to know this particular limitation.

The already deleted answer pointed out that C99 requires the varargs macro argument to be populated with at least one actual argument (although I find the spec is ambiguous at best in this regard). The compiler I have at my fingertips (Clang on OS X) accepts code from -Wall

, but actually complains about -pedantic

, as @ 2501 clearly demonstrates in his comment.

-pedantic

also complains about a zero-sized array (no-args extension (int[]) { }

), although this could be fixed always, including a dummy element.

+3


source to share


1 answer


If the macro arguments are valid and non-void expressions, you can try and use decltype

, for example:

#include <tuple>
#define NARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value

      



Macro arguments are not evaluated. For example:

#include <iostream>
#include <type_traits>

int main(int argc, char *argv[])
{
    std::cout << "Have "
              << NARGS("bar", 1, argc, 3, std::declval<int>())
              << " things\n";
}

      

+1


source







All Articles