Variadic macro with no arguments for its variadic parameter - c++

Is it legal to invoke a variadic macro M with no arguments for its variadic parameter?
The relevant standard quote is [cpp.replace]/4:
If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...). There shall exist a ) preprocessing token that terminates the invocation.
For the case with no non-variadic parameters, an invocation in the form M() should be legal as the invocation has one argument (consisting of no preprocessing tokens). So there is one more argument than non-variadic parameters.
For the case with one non-variadic parameter, should there be a trailing , as in M(1,) to introduce an argument consisting of no preprocessing tokens for the variadic parameter? Otherwise, the number of arguments would be equal to the number of non-variadic parameters. i.e.,
#define variadic(x,...) #__VA_ARGS__
variadic(1,) // 2 arguments: ok
variadic(1) // 1 argument: wrong?
Both Clang and GCC, however, accept the following test case:
#include <iostream>
#define variadic(x,...) #__VA_ARGS__
int main()
{
std::cout << "'" variadic(1) "'" << std::endl;
}
and produce as output:
''
Is this non-standard behavior?

Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).
This very extract from the standard shows that your code should not be valid: you have one parameter plus the ellipsis. If we follow the portion of the standard above, you should have at least two arguments. When you write varidadic(1), you just provide one argument. Your code is not valid.
By the way, clang produces a warning:
main.cpp:7:32: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu]
std::cout << "'" variadic(1) "'" << std::endl;
And GCC also produces a warning:
main.cpp:7:32: warning: ISO C99 requires rest arguments to be used [enabled by default]
std::cout << "'" variadic(1) "'" << std::endl;
Since that may be a bother to the programmer, and since the programmer's intent is easy to guess, they both consider variadic(1) is equivalent to variadic(1,).

Related

C++ variadic function syntax

In the C++03 standard, [dcl.fct] p.2 states that:
The parameter-declaration-clause determines the arguments that can be specified, and their processing, when the func- tion is called. [ Note: the parameter-declaration-clause is used to convert the arguments specified on the function call; see5.2.2. —endnote]Iftheparameter-declaration-clauseisempty,thefunctiontakesnoarguments.Theparameter list (void) is equivalent to the empty parameter list. Except for this special case, void shall not be a parameter type (though types derived from void, such as void*, can). If the parameter-declaration-clause terminates with an ellipsis, the number of arguments shall be equal to or greater than the number of parameters that do not have a default argument. Where syntactically correct, “, ...” is synonymous with “...”.
The grammar for a parameter-declaration-clause allows it to end in either ... or , .... I found this question and the answers said that initially the grammar allowed only ..., and the comma variant (, ...) was introduced for compatibility with C.
My question is why does the quoted paragraph say "where syntactically correct"? Considering that function parameter packs or pack expansions were not present in C++03, are there any cases where it would be "syntactically incorrect" to consider ... synonymous with , ...?
Thank you.
for my understanding, it just mean that when you use ,... with a correct syntax, then it can be replaced by ...
for instance :
int printf(const char*, ...); is a correct syntax and can be replaced by int printf(const char*...);
int printf(,...); is not syntactically correct and is not equivalent to int printf(...);
You can easily try in a C code by just adding the following prototypes:
void printf(...); --> works
void printf(,...); --> expected primary-expression before ‘,’ token
are there any cases where it would be "syntactically incorrect" to consider ... synonymous with , ...?
Exactly as Guillaume says, it would be "syntactically incorrect" to rewrite
foo(...)
as
foo(, ...)
so that clause just handles the corner case where there is no prior parameter from which the ellipsis can be separated by a comma.

Is it legal for a function-like macro to "steal" commas from a parenthesized template argument list?

I was just surprised that providing a type with two template arguments to a function-like macro resulted in the compiler complaining.
This (conceptually similar) example code:
template<typename T> struct foo{};
template<typename T, U> struct bar{};
#define p(x) printf("sizeof(" #x ") = %u\n", sizeof(x));
int main()
{
p(foo<int>); // works, of course
p(bar<int,int>); // does not work
p((bar<int,int>)); // does not work either
return 0;
}
makes GCC (6.2.0) complain macro "p" passed 2 arguments, but takes just 1.
Well, of course, the preprocessor is a preprocessor doing text replacement, it's not a real C++ compiler which understands templates or all other rules of the language.
Maybe I'm asking too much by expecting that the preprocessor recognizes the angle brackets, granted... but at least parentheses are explicitly mentioned in the specification.
16.3 (paragraphs 10 to 12) states outermost parentheses delimiting the bounded sequence of tokens. The word "outermost" suggests that there may possibly also be further (not-outermost) parentheses which the preprocessor recognizes.
Also, it explicitly states "skipping intervening matched pairs of left and right parenthesis" as well as "comma preprocessing tokens between matching inner parentheses do not separate arguments" -- which means that if I am reading correctly, then at least the last line should in my understanding pass.
What am I understanding wrong?
p((bar<int,int>)) is a valid invocation of the p macro with a single macro argument, which is (bar<int,int>). Your understanding so far is correct.
Unfortunately, its expansion includes sizeof((bar<int,int>)), and sizeof does not accept doubly-parenthesised types.
Variadic macros (C++11) work well here as an alternative.
#define p(...) printf("sizeof(" #__VA_ARGS__ ") = %u\n", sizeof(__VA_ARGS__));

Function not implemented macro?

I'm trying to create a macro so I can specify that a function is not implemented yet (and will crash the program when invoked). Here's what I came up with:
#define NIMPL crash(__PRETTY_FUNCTION__ " not implemented yet")
And you'd use it like so:
void myFunction() {
NIMPL;
}
However, this usage causes this error:
../feta/include/feta.h:19:41: error: expected ‘)’ before string constant
#define NIMPL crash(__PRETTY_FUNCTION__ " not implemented yet")
^
crash() is a function that accepts one string as a parameter, prints out the message, and calls exit(1).
I can confirm that __PRETTY_FUNCTION__ alone, without the concatenation works fine. Also, concatenating two strings without __PRETTY_FUNCTION__ works fine: "<stuff>""<other stuff>". But doing both at the same time does not work.
I've tried using the stringizing operator, but it did not work (if it even exists in GCC).
Standard C and C++ provide the "magic identifier" __func__, which acts as if it were the name of a static char array containing the name of the current function. (Actually the value is implementation-defined in C++.)
gcc, as an extension, provides __PRETTY_FUNCTION__, which is identical to __func__ for C, and provides some additional information for C++.
That doesn't solve the problem you're asking about, which is that string literal concatenation applies only to string literals.
You say your crash() function takes a single string as an argument.
I suggest either modifying crash() so it takes two arguments, or writing a new wrapper function that takes two arguments. You can then use:
#define NIMPL crash(__PRETTY_FUNCTION__, " not implemented yet")
(Since you're using C++ you can overload it with the same name.) Concatenating the two strings is left as an exercise.
You could also add a second string argument to your existing crash() function and give it a default value of "" to avoid breaking existing calls.
In C (as the question was originally tagged), __PRETTY_FUNCTION__ is another name for __func__, which is not a string literal. In C++, it is decorated with the function signature, but it is still not a string literal.
From the documentation (especially note the last sentence):
The identifier __func__ is implicitly declared by the translator as if, immediately following the opening brace of each function definition, the declaration
static const char __func__[] = "function-name";
appeared, where function-name is the name of the lexically-enclosing function. This name is the unadorned name of the function. As an extension, at file (or, in C++, namespace scope), __func__ evaluates to the empty string.
...
In C, __PRETTY_FUNCTION__ is yet another name for __func__, except that at file (or, in C++, namespace scope), it evaluates to the string "top level". In addition, in C++, __PRETTY_FUNCTION__ contains the signature of the function as well as its bare name.
...
These identifiers are variables, not preprocessor macros, and may not be used to initialize char arrays or be concatenated with string literals.
Since you are using C++, you may consider defining crash as a variadic template function rather than a macro, to provide some more flexibility.
void crash_t (std::ostringstream &oss)
{
oss << std::endl;
write(2, &oss.str()[0], oss.str().size());
exit(1);
}
template <typename A, typename... REST>
void crash_t (std::ostringstream &oss, A arg, REST... rest)
{
oss << arg;
crash_t(oss, rest...);
}
template <typename... ARGS>
void crash (ARGS... args)
{
std::ostringstream oss;
crash_t(oss, args...);
}

C++ preprocessors are not aware of template arguments?

As it appears, C++ preprocessor fails if a template instantiation with multiple arguments passed to a macro as an argument.
See an example below.
#include <stdio.h>
#define FOO(v) printf("%d\n",v::val())
template<int N>
struct bar {
static int val() { return N; }
};
template<int N, int M>
struct baz {
static int val() { return N+M; }
};
int main() {
printf("%d\n",bar<1>::val());
printf("%d\n",baz<1,2>::val());
FOO(bar<10>); // OK
FOO(baz<20,30>); // error: too many arguments provided to function-like macro invocation
FOO((baz<20,30>)); // error: '::val' has not been declared
}
Tested with clang++ and g++
Should it be considered as a bug?
No, it's not a bug.
The c preprocessor is a different beast from the rest of the language and it plays by its own rules. Changing this would break compatibility in a massive way, CPP is highly rigorously standardized.
The usual way to work around these comma issues is,
typedef baz<20,30> baz2030_type;
FOO(baz2030_type);
The C/C++ preprocessor recognizes commas as macro argument separators unless they are nested inside parentheses. Just parentheses. Brackets, braces and template markers don't count:
The individual arguments within the list are separated by comma preprocessing tokens, but comma preprocessing tokens between matching inner parentheses do not separate arguments. (C++14 §16.3/11; C11 §6.10.3/11)
(A side effect of the above is that you can use unbalanced braces and brackets as macro arguments. That's usually not a very good idea, but you can do it if you have to.)
Problems occasionally crop up as a result; a common one is unwanted multiple arguments when the argument is supposed to be a block of code:
MY_FANCY_MACRO(1000, { int i=0, j=42; ... })
Here, the macro is called with (at least) 3 arguments, although it was probably written to accept 2.
With modern C++ (and C) compilers, you have a few options. In a fairly subjective order:
Rewrite the macro as an inline function. If the argument is a code block, consider using a templated function which could accept a lambda or other functor. If the argument is a type, make it a template argument instead.
If surrounding the argument with redundant parentheses is syntactically valid, do that. But in such a case it is almost certainly the case that suggestion (1) above would have worked.
Define:
#define COMMA ,
and use it where necessary:
FOO(baz<20 COMMA 30>);
This doesn't require modifying the macro definition in any way, but it will fail if the macro passes the argument to another macro. (The replacement will be done before the inner macro call is parsed, so the multiple argument problem will just be deferred to the inner call.)
If you expect that one macro argument might contain unprotected commas, and it is the last or only argument, and you're in a position to modify the macro, and you're using C++11/C99 or better (or gcc, which has allowed this as an extension for some time), make the macro variadic:
#define FOO(...) printf("%d\n",__VA_ARGS__::val())
The macro's argument is treated as plain text string and the arguments are separated using commas. Hence the comma in the template will be treated as a delimiter. Thus the preprocessor will think that you have passed on two arguments to a single argument macro, hence the error.
BOOST_IDENTITY_TYPE is the solution for that: https://www.boost.org/doc/libs/1_73_0/libs/utility/identity_type/doc/html/index.html
You can also wrap the type into decltype: decltype(std::pair<int, int>()) var; which also adds an extra parentheses, but this unfortunately does an extra ctor call.

Is the `gnu-zero-variadic-macro-arguments` safe to ignore?

Consider the following code (live example):
#define TEST_VA(mX, ...) TEST
#define STRINGIFY_IMPL(mX) #mX
#define STRINGIFY(mX) STRINGIFY_IMPL(mX)
#include <iostream>
int main()
{
std::cout << STRINGIFY(TEST_VA(1)) << std::endl;
std::cout << STRINGIFY(TEST_VA()) << std::endl;
return 0;
}
clang++ 3.4 complains:
main.cpp:9:37: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
std::cout << STRINGIFY(TEST_VA(1)) << std::endl;
^
main.cpp:1:9: note: macro 'TEST_VA' defined here
#define TEST_VA(mX, ...) TEST
^
main.cpp:10:33: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
std::cout << STRINGIFY(TEST_VA()) << std::endl;
^
main.cpp:1:9: note: macro 'TEST_VA' defined here
#define TEST_VA(mX, ...) TEST
I use some variadic macros like in the example code in my projects.
Is it safe to pass no arguments for the ... parameter of variadic macros? (Ignoring the warning, basically)
Or could it lead to problems?
Is it explicitly forbidden by the standard?
Starting from C++20, passing zero arguments to a variadic macro parameter is allowed, but Clang (at least Clang 12) doesn't respect this change yet.
So if you're targeting C++20 or newer and using -pedantic-errors, then -Wno-gnu-zero-variadic-macro-arguments is recommended.
[cpp.replace.general]/5
... there shall be at least as many arguments in the invocation as there are parameters in the macro definition (excluding the ...).
It's technically forbidden by the standard. From C++11 §16.3/4 (emphasis added):
If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including
those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal
the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...). There shall exist a ) preprocessing
token that terminates the invocation.
Some compilers may allow this as an extension, but if you want standards-compliant code, you should make sure to always include at least one argument for the ellipsis parameter of a variadic macro.