Hacker News new | ask | show | jobs
by nkurz 3786 days ago
I'm not familiar with MSVC, but when I tried this example:

  #include <stdio.h>

  void bar(int n, const char *p[]) {
    for (int i = 0; i < n; i++) {
      printf("%s\t", p[i]);
    }
    putchar('\n');
  }

  #define bar(...) (bar( \
  sizeof((const char*[]) { NULL, __VA_ARGS__ }) / sizeof(const char*) - 1, \
  (const char*[]) { NULL, __VA_ARGS__ } + 1))

  int main(/* int argc, char **argv */) {
    bar();
    bar("a");
    bar("a", "b");
    bar("a", "b", "c");
    return 0;
  }
Using the online MSVC compiler at http://webcompiler.cloudapp.net/ (which runs Visual C++ 19.00.23720.0), it failed with these error message:

  Compiled with  /EHsc /nologo /W4 /c
  main.cpp
  main.cpp(20): error C4576: a parenthesized type followed by    an 
  initializer list is a non-standard explicit type conversion syntax
  main.cpp(21): error C4576: a parenthesized type followed by an initializer
  list is a non-standard explicit type conversion syntax
  main.cpp(22): error C4576: a parenthesized type followed by an initializer list
  is a non-standard explicit type conversion syntax
  main.cpp(23): error C4576: a parenthesized type followed by an initializer list
  is a non-standard explicit type conversion syntax
2 comments

If you don't need the return value, the do-while approach works as you suggested:

  #define bar(...) do {		                   	 \
	const char *_args[] = {NULL, __VA_ARGS__};       \
	bar(sizeof(_args)/sizeof(*_args) - 1, _args + 1);\
  } while (0)
If for some absurd reason you really do need to emulate a "compound statement expression" in MSVC (because you need to declare some temp variables in a macro that can be used for assignment), it turns out that it can be done with a "lambda expression":

  #include <stdio.h>

  int bar(int n, const char *p[])
  {
    int total = 0; 
    for (int i = 0; i < n; i++) {
      total += printf("%s\t", p[i]);
    }
    putchar('\n');
    return total;
  }

  #define bar(...) [](){                                       \
    const char *_args[] = {NULL, __VA_ARGS__};                 \
    return bar(sizeof(_args)/sizeof(*_args) - 1, _args + 1);   \
  }()

  int main(/* int argc, char **argv */) {
    int total = 0;
    total += bar();
    total += bar("a");
    total += bar("a", "b");
    total += bar("a", "b", "c");
    printf("total: %d\n", total);
    return 0;
  }
Compound literals were never formally added to C++.

Still, that hasn't stopped various front-ends (including GCC and Clang) from adding them to their C++ dialects. My code compiles with Clang, but not GCC. GCC complains that it's not OK to take the address of the array compound literal.

FWIW, C99 makes it clear that the object has automatic storage duration lasting until the end of the enclosing block. i.e. There shouldn't be an issue with dangling pointers.