Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 250
hutorny Avatar asked Aug 27 '15 05:08

hutorny


People also ask

What is template argument in C++?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can we pass Nontype parameters to templates?

Template non-type arguments in C++ It is also possible to use non-type arguments (basic/derived data types) i.e., in addition to the type argument T, it can also use other arguments such as strings, function names, constant expressions, and built-in data types.

How are templates different from macros in C++?

Macro vs Template Macro are used in C and C++ for replacement of numbers, small inline functions etc. Template is only available in C++ language. It is used to write small macro like functions etc.


2 Answers

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);
like image 80
Chris Beck Avatar answered Oct 06 '22 01:10

Chris Beck


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:

  1. 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.

  2. 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.

  3. 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.)

  4. 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())
    
like image 38
rici Avatar answered Oct 06 '22 01:10

rici