【问题标题】:How to easily create fully "variadic" functions with C++ 98 standard?如何使用 C++ 98 标准轻松创建完全“可变参数”函数?
【发布时间】:2019-12-14 02:47:06
【问题描述】:

图书馆 https://github.com/c42f/tinyformat/blob/2f9335afd9941688e42d60cae5166b9f0600b2d1/tinyformat.h#L1104-L1116,使用这个很棒的技巧在 C++ 98 上做“可变参数”模板:

inline void printfln(const char* fmt)
{
    format(std::cout, fmt);
    std::cout << '\n';
}

template<TINYFORMAT_ARGTYPES(n)>                                          \
void printfln(const char* fmt, TINYFORMAT_VARARGS(n))                     \
{                                                                         \
    format(std::cout, fmt, TINYFORMAT_PASSARGS(n));                       \
    std::cout << '\n';                                                    \
}

我正在尝试通过消除复制函数 printfln 两次的要求来改进它,即一次用于基本情况 inline void printfln(const char* fmt),然后“可变参数”部分的第二次 template&lt;TINYFORMAT_ARGTYPES(n)&gt; void printfln(const char* fmt, TINYFORMAT_VARARGS(n))

他们需要将printfln函数分成两部分,因为“variadic”函数只能接受一个参数,即printfln("something")。在这种情况下,TINYFORMAT_VARARGS(n) 必须扩展为空,但是,这样做会导致代码有一个尾随逗号 ,,导致到 C++ 上的无效语法。

我可以使用带有 C 宏标记粘贴运算符 ##GNU GCC 扩展技巧来删除尾随逗号,但是,这不是可移植的,因为它仅适用于 GNU GCC。然后,我的目标是定义已经包含前导逗号的宏作为下一个示例:

#include <stdio.h>

#define TINYFORMAT_ARGTYPES_0
#define TINYFORMAT_ARGTYPES_1 , class T1
#define TINYFORMAT_ARGTYPES_2 , class T1, class T2
#define TINYFORMAT_ARGTYPES_3 , class T1, class T2, class T3

#define TINYFORMAT_VARARGS_0
#define TINYFORMAT_VARARGS_1 , const T1& v1
#define TINYFORMAT_VARARGS_2 , const T1& v1, const T2& v2
#define TINYFORMAT_VARARGS_3 , const T1& v1, const T2& v2, const T3& v3

#define TINYFORMAT_PASSARGS_0
#define TINYFORMAT_PASSARGS_1 , v1
#define TINYFORMAT_PASSARGS_2 , v1, v2
#define TINYFORMAT_PASSARGS_3 , v1, v2, v3

#define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)

#define FACTORY(n) \
template<typename T0 TINYFORMAT_ARGTYPES(n)> \
inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
{ \
    fprintf(stderr, v0 TINYFORMAT_PASSARGS(n) ); \
}

TINYFORMAT_FOREACH_ARGNUM(FACTORY)

int main(int argc, char const *argv[])
{
    some( "Something %s.", "New" );
}

但是,它不起作用。 gcc 编译器如果在宏定义之后找到 comma 就会发疯:g++ -o main -g -ggdb test_debugger.cpp --std=c++98 &amp;&amp; ./main

test_debugger.cpp:21:19: error: expected nested-name-specifier before ‘T0’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                   ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:21:22: error: expected ‘>’ before ‘TINYFORMAT_ARGTYPES’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                      ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:24: error: ‘T0’ does not name a type
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                        ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:31: error: expected ‘,’ or ‘...’ before ‘TINYFORMAT_VARARGS’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                               ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:52: error: default template arguments may not be used in function templates without -std=c++11 or -std=gnu++11
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                                                    ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp: In function ‘void some(const int&)’:
test_debugger.cpp:24:24: error: expected ‘)’ before ‘TINYFORMAT_PASSARGS’
     fprintf(stderr, v0 TINYFORMAT_PASSARGS(n) ); \
                        ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp: At global scope:
test_debugger.cpp:21:19: error: expected nested-name-specifier before ‘T0’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                   ^
test_debugger.cpp:18:43: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                           ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:21:22: error: expected ‘>’ before ‘TINYFORMAT_ARGTYPES’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                      ^
test_debugger.cpp:18:43: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                           ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:24: error: ‘T0’ does not name a type
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                        ^
test_debugger.cpp:18:43: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                           ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:31: error: expected ‘,’ or ‘...’ before ‘TINYFORMAT_VARARGS’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                               ^
test_debugger.cpp:18:43: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                           ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:52: error: default template arguments may not be used in function templates without -std=c++11 or -std=gnu++11
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                                                    ^
test_debugger.cpp:18:43: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                           ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: error: redefinition of ‘template<<declaration error> > void some(const int&)’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:43: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                           ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: note: ‘template<<declaration error> > void some(const int&)’ previously declared here
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:21:19: error: expected nested-name-specifier before ‘T0’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                   ^
test_debugger.cpp:18:48: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:21:22: error: expected ‘>’ before ‘TINYFORMAT_ARGTYPES’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                      ^
test_debugger.cpp:18:48: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:24: error: ‘T0’ does not name a type
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                        ^
test_debugger.cpp:18:48: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:31: error: expected ‘,’ or ‘...’ before ‘TINYFORMAT_VARARGS’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                               ^
test_debugger.cpp:18:48: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:52: error: default template arguments may not be used in function templates without -std=c++11 or -std=gnu++11
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                                                    ^
test_debugger.cpp:18:48: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: error: redefinition of ‘template<<declaration error> > void some(const int&)’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:48: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: note: ‘template<<declaration error> > void some(const int&)’ previously declared here
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:21:19: error: expected nested-name-specifier before ‘T0’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                   ^
test_debugger.cpp:18:53: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                     ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:21:22: error: expected ‘>’ before ‘TINYFORMAT_ARGTYPES’
 template<typename T0 TINYFORMAT_ARGTYPES(n)> \
                      ^
test_debugger.cpp:18:53: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                     ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:24: error: ‘T0’ does not name a type
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                        ^
test_debugger.cpp:18:53: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                     ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:31: error: expected ‘,’ or ‘...’ before ‘TINYFORMAT_VARARGS’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                               ^
test_debugger.cpp:18:53: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                     ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:52: error: default template arguments may not be used in function templates without -std=c++11 or -std=gnu++11
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
                                                    ^
test_debugger.cpp:18:53: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                     ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: error: redefinition of ‘template<<declaration error> > void some(const int&)’
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:53: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                                     ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: note: ‘template<<declaration error> > void some(const int&)’ previously declared here
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:31:34: error: no matching function for call to ‘some(const char [14], const char [4])’
     some( "Something %s.", "New" );
                                  ^
test_debugger.cpp:22:13: note: candidate: template<<declaration error> > void some(const int&)
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:22:13: note:   template argument deduction/substitution failed:
 inline void some(const T0& v0 TINYFORMAT_VARARGS(n)) \
             ^
test_debugger.cpp:18:38: note: in expansion of macro ‘FACTORY’
 #define TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
                                      ^
test_debugger.cpp:27:1: note: in expansion of macro ‘TINYFORMAT_FOREACH_ARGNUM’
 TINYFORMAT_FOREACH_ARGNUM(FACTORY)
 ^~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:31:34: note:   candidate expects 1 argument, 2 provided
     some( "Something %s.", "New" );
                                  ^

据我了解,我的 TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3) 宏应该创建以下 4 个有效的 C++“可变参数”函数:

template<typename T0> 
inline void some(const T0& v0) 
{ 
    fprintf(stderr, v0 ); 
}

template<typename T0, class T1> 
inline void some(const T0& v0, const T1& v1) 
{ 
    fprintf(stderr, v0, v1 ); 
}

template<typename T0, class T1, class T2> 
inline void some(const T0& v0, const T1& v1, const T1& v2) 
{ 
    fprintf(stderr, v0, v1, v2); 
}

template<typename T0, class T1, class T2, class T3> 
inline void some(const T0& v0, const T1& v1, const T1& v2, const T1& v3) 
{ 
    fprintf(stderr, v0, v1, v2, v3); 
}

为什么 gcc 预处理器不能正确生成我上面的 4 个“可变参数”模板函数?

作为参考,我正在使用:

$ g++ --version
g++ (GCC) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

更新

g++ -o main -E -g -ggdb test_debugger.cpp --std=c++98

的输出
# 797 "/usr/include/stdio.h" 3 4
}
# 2 "test_debugger.cpp" 2
# 27 "test_debugger.cpp"

# 27 "test_debugger.cpp"
template<typename T0 TINYFORMAT_ARGTYPES(0)> 
inline void some(const T0& v0 TINYFORMAT_VARARGS(0)) { fprintf(
# 27 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 27 "test_debugger.cpp"
, v0 TINYFORMAT_PASSARGS(0) ); } template<typename T0 TINYFORMAT_ARGTYPES(1)> 
inline void some(const T0& v0 TINYFORMAT_VARARGS(1)) { fprintf(
# 27 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 27 "test_debugger.cpp"
, v0 TINYFORMAT_PASSARGS(1) ); } template<typename T0 TINYFORMAT_ARGTYPES(2)> 
inline void some(const T0& v0 TINYFORMAT_VARARGS(2)) { fprintf(
# 27 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 27 "test_debugger.cpp"
, v0 TINYFORMAT_PASSARGS(2) ); } template<typename T0 TINYFORMAT_ARGTYPES(3)> 
inline void some(const T0& v0 TINYFORMAT_VARARGS(3)) { fprintf(
# 27 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 27 "test_debugger.cpp"
, v0 TINYFORMAT_PASSARGS(3) ); }

int main(int argc, char const *argv[])
{
    some( "Something %s.", "New" );
}

更新 2

我试图更改定义的顺序,希望 C 预处理器能够正确扩展,但它只是像以前一样扩展:

#include <stdio.h>

#define TINYFORMAT_ARGTYPES_0
#define TINYFORMAT_ARGTYPES_1 , class T1
#define TINYFORMAT_ARGTYPES_2 , class T1, class T2
#define TINYFORMAT_ARGTYPES_3 , class T1, class T2, class T3

#define TINYFORMAT_VARARGS_0
#define TINYFORMAT_VARARGS_1 , const T1& v1
#define TINYFORMAT_VARARGS_2 , const T1& v1, const T2& v2
#define TINYFORMAT_VARARGS_3 , const T1& v1, const T2& v2, const T3& v3

#define TINYFORMAT_PASSARGS_0
#define TINYFORMAT_PASSARGS_1 , v1
#define TINYFORMAT_PASSARGS_2 , v1, v2
#define TINYFORMAT_PASSARGS_3 , v1, v2, v3

#define TINYFORMAT_FOREACH_ARGNUM(m) \
    m(TINYFORMAT_ARGTYPES(0),TINYFORMAT_VARARGS(0),TINYFORMAT_PASSARGS(0)) \
    m(TINYFORMAT_ARGTYPES(1),TINYFORMAT_VARARGS(1),TINYFORMAT_PASSARGS(1)) \
    m(TINYFORMAT_ARGTYPES(2),TINYFORMAT_VARARGS(2),TINYFORMAT_PASSARGS(2)) \
    m(TINYFORMAT_ARGTYPES(3),TINYFORMAT_VARARGS(3),TINYFORMAT_PASSARGS(3))

#define FACTORY(argtypes,varargs,passargs) \
template<typename T0 argtypes> \
inline void some(const T0& v0 varargs) \
{ \
    fprintf(stderr, v0 passargs); \
}
TINYFORMAT_FOREACH_ARGNUM(FACTORY)

int main(int argc, char const *argv[])
{
    some( "Something %s.", "New" );
}

更新 3

正如@aschepler 所评论的,我缺少TINYFORMAT_ARGTYPES_ ## n 的定义,这是一个固定版本:

#include <stdio.h>

#define TINYFORMAT_ARGTYPES_0(...)
#define TINYFORMAT_ARGTYPES_1(...) __VA_ARGS__ class T1
#define TINYFORMAT_ARGTYPES_2(...) __VA_ARGS__ class T1, class T2
#define TINYFORMAT_ARGTYPES_3(...) __VA_ARGS__ class T1, class T2, class T3

#define TINYFORMAT_VARARGS_0(...)
#define TINYFORMAT_VARARGS_1(...) __VA_ARGS__ const T1& v1
#define TINYFORMAT_VARARGS_2(...) __VA_ARGS__ const T1& v1, const T2& v2
#define TINYFORMAT_VARARGS_3(...) __VA_ARGS__ const T1& v1, const T2& v2, const T3& v3

#define TINYFORMAT_PASSARGS_0(...)
#define TINYFORMAT_PASSARGS_1(...) __VA_ARGS__ v1
#define TINYFORMAT_PASSARGS_2(...) __VA_ARGS__ v1, v2
#define TINYFORMAT_PASSARGS_3(...) __VA_ARGS__ v1, v2, v3

#define TINYFORMAT_ARGTYPES(n,...) TINYFORMAT_ARGTYPES_ ## n (__VA_ARGS__)
#define TINYFORMAT_VARARGS(n,...)  TINYFORMAT_VARARGS_  ## n (__VA_ARGS__)
#define TINYFORMAT_PASSARGS(n,...) TINYFORMAT_PASSARGS_ ## n (__VA_ARGS__)

#define TINYFORMAT_FOREACH_ARGNUM(m,...) \
        m(0) m(1,__VA_ARGS__) m(2,__VA_ARGS__) m(3,__VA_ARGS__)

#define FACTORY(n,...) \
template<TINYFORMAT_ARGTYPES(n,__VA_ARGS__)> \
inline void some(TINYFORMAT_VARARGS(n,__VA_ARGS__)) \
{ \
    fprintf(stderr, "variadic" TINYFORMAT_PASSARGS(n,,) ); \
}
TINYFORMAT_FOREACH_ARGNUM(FACTORY,)

int main(int argc, char const *argv[]) {
    some();
}

扩展至:g++ -o main -E -g -ggdb test_debugger.cpp --std=c++98

# 797 "/usr/include/stdio.h" 3 4
}
# 2 "test_debugger.cpp" 2
# 31 "test_debugger.cpp"

# 31 "test_debugger.cpp"
template<> 
inline void some() { fprintf(
# 31 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 31 "test_debugger.cpp"
, "variadic" ); } template< class T1> 
inline void some( const T1& v1) { fprintf(
# 31 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 31 "test_debugger.cpp"
, "variadic" , v1 ); } template< class T1, class T2> 
inline void some( const T1& v1, const T2& v2) { fprintf(
# 31 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 31 "test_debugger.cpp"
, "variadic" , v1, v2 ); } template< class T1, class T2, class T3> 
inline void some( const T1& v1, const T2& v2, const T3& v3) { fprintf(
# 31 "test_debugger.cpp" 3 4
((__getreent())->_stderr)
# 31 "test_debugger.cpp"
, "variadic" , v1, v2, v3 ); }

int main(int argc, char const *argv[]) {
    some();
}

现在的问题是,当宏模板扩展为没有任何模板参数的东西时,即template&lt;&gt; inline void some(),它会生成以下代码:

template<>
inline void some()
{
    fprintf(stderr, "variadic");
}

int main(int argc, char const *argv[])
{
    some();
}

导致 C++ 编译器抛出此错误的原因:

test_debugger.cpp: error: ‘some’ is not a template function
 inline void some()
                  ^
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp: error: ‘some’ was not declared in this scope
     some();
     ^~~~

【问题讨论】:

  • 使用“g++ -E ...”命令查看作为编译器步骤输入的预处理器的输出。但看起来TINYFORMAT_ARGTYPES 从未定义过,因此实际上并没有看到您期望的逗号。
  • @aschepler,我运行了他们似乎没有扩展的命令。我用输出更新了问题。
  • 好吧,我没有看到TINYFORMAT_ARGTYPES 的任何定义。你似乎错过了#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n
  • @aschepler,你说得对,我已经解决了这个问题,现在我正在处理在声明模板时没有模板类型的边缘情况,即 template&lt;&gt; inline void some() .然后,编译器说:error: ‘some’ is not a template function。我用这个新问题更新了问题。

标签: macros variadic-templates variadic-functions c++98 variadic-macros


【解决方案1】:

模板不能有零模板参数。以 template &lt;&gt; 开头的语法被用于显式特化:使用声明而不是模板用于一组特定的模板参数。

因此,您的零参数版本需要跳过 template &lt;&gt; 部分。你可能会这样做:

#define TINYFORMAT_TEMPLATE_HEAD_0(...)
#define TINYFORMAT_TEMPLATE_HEAD_1(...) template < TINYFORMAT_ARGTYPES_1(__VA_ARGS__) >
#define TINYFORMAT_TEMPLATE_HEAD_2(...) template < TINYFORAMT_ARGTYPES_2(__VA_ARGS__) >
#define TINYFORMAT_TEMPLATE_HEAD_3(...) template < TINYFORMAT_ARGTYPES_3(__VA_ARGS__) >

#define TINYFORMAT_TEMPLATE_HEAD(n, ...) TINYFORMAT_TEMPLATE_HEAD_ ## n (__VA_ARGS__)

#define FACTORY(n,...) \
TINYFORMAT_TEMPLATE_HEAD(n,__VA_ARGS__) \
inline void some(TINYFORMAT_VARARGS(n,__VA_ARGS__)) \
{ \
    fprintf(stderr, "variadic" TINYFORMAT_PASSARGS(n,,) ); \
}

【讨论】:

    【解决方案2】:

    感谢@aschepler 的帮助,我还设法构建了这个更通用的解决方案/示例:

    #include <stdio.h>
    
    #define TINYFORMAT_ARGTYPES_0(...)
    #define TINYFORMAT_ARGTYPES_1(begin,end,...) begin __VA_ARGS__ class T1 end
    #define TINYFORMAT_ARGTYPES_2(begin,end,...) begin __VA_ARGS__ class T1, class T2 end
    #define TINYFORMAT_ARGTYPES_3(begin,end,...) begin __VA_ARGS__ class T1, class T2, class T3 end
    
    #define TINYFORMAT_VARARGS_0(...)
    #define TINYFORMAT_VARARGS_1(...) __VA_ARGS__ const T1& v1
    #define TINYFORMAT_VARARGS_2(...) __VA_ARGS__ const T1& v1, const T2& v2
    #define TINYFORMAT_VARARGS_3(...) __VA_ARGS__ const T1& v1, const T2& v2, const T3& v3
    
    #define TINYFORMAT_PASSARGS_0(...)
    #define TINYFORMAT_PASSARGS_1(...) __VA_ARGS__ v1
    #define TINYFORMAT_PASSARGS_2(...) __VA_ARGS__ v1, v2
    #define TINYFORMAT_PASSARGS_3(...) __VA_ARGS__ v1, v2, v3
    
    #define TINYFORMAT_ARGTYPES(n,begin,end,...) TINYFORMAT_ARGTYPES_ ## n (begin,end,__VA_ARGS__)
    #define TINYFORMAT_VARARGS(n,...)  TINYFORMAT_VARARGS_  ## n (__VA_ARGS__)
    #define TINYFORMAT_PASSARGS(n,...) TINYFORMAT_PASSARGS_ ## n (__VA_ARGS__)
    
    #define TINYFORMAT_FOREACH_ARGNUM(m,...) \
            m(0) m(1,__VA_ARGS__) m(2,__VA_ARGS__) m(3,__VA_ARGS__)
    
    #define FACTORY_FULLY_OPTIONAL(n,...) \
    TINYFORMAT_ARGTYPES(n,template<,>,__VA_ARGS__) \
    inline void some_optional(TINYFORMAT_VARARGS(n,__VA_ARGS__)) \
    { \
        fprintf( stderr, "some_optional\n" TINYFORMAT_PASSARGS(n,,) ); \
    }
    
    #define FACTORY_WITH_EXISTENT_ARGS(n,...) \
    template<class T0 TINYFORMAT_ARGTYPES(n,,,__VA_ARGS__)> \
    inline void some_existent(const T0& v0 TINYFORMAT_VARARGS(n,__VA_ARGS__)) \
    { \
        fprintf( stderr, "some_existent %s\n", v0 TINYFORMAT_PASSARGS(n,__VA_ARGS__) ); \
    }
    
    TINYFORMAT_FOREACH_ARGNUM(FACTORY_FULLY_OPTIONAL,)
    TINYFORMAT_FOREACH_ARGNUM(FACTORY_WITH_EXISTENT_ARGS,,)
    
    int main(int argc, char const *argv[])
    {
        some_optional();
        some_existent( "varing" );
    }
    

    这是生成的代码:g++ -o main -E -g -ggdb test_debugger.cpp --std=c++98

    # 797 "/usr/include/stdio.h" 3 4
    }
    # 2 "test_debugger.cpp" 2
    # 38 "test_debugger.cpp"
    
    # 38 "test_debugger.cpp"
    inline void some_optional() { fprintf(
    # 38 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 38 "test_debugger.cpp"
    , "some_optional\n" ); } template< class T1 > 
    inline void some_optional( const T1& v1) { fprintf(
    # 38 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 38 "test_debugger.cpp"
    , "some_optional\n" , v1 ); } template< class T1, class T2 > 
    inline void some_optional( const T1& v1, const T2& v2) { fprintf(
    # 38 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 38 "test_debugger.cpp"
    , "some_optional\n" , v1, v2 ); } template< class T1, class T2, class T3 > 
    inline void some_optional( const T1& v1, const T2& v2, const T3& v3) { fprintf(
    # 38 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 38 "test_debugger.cpp"
    , "some_optional\n" , v1, v2, v3 ); }
    
    template<class T0 > 
    inline void some_existent(const T0& v0 ) { fprintf(
    # 39 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 39 "test_debugger.cpp"
    , "some_existent %s\n", v0 ); } template<class T0 , class T1 > 
    inline void some_existent(const T0& v0 , const T1& v1) { fprintf(
    # 39 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 39 "test_debugger.cpp"
    , "some_existent %s\n", v0 , v1 ); } template<class T0 , class T1, class T2 > 
    inline void some_existent(const T0& v0 , const T1& v1, const T2& v2) { fprintf(
    # 39 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 39 "test_debugger.cpp"
    , "some_existent %s\n", v0 , v1, v2 ); } template<class T0 , class T1, class T2, class T3 > 
    inline void some_existent(const T0& v0 , const T1& v1, const T2& v2, const T3& v3) { fprintf(
    # 39 "test_debugger.cpp" 3 4
    ((__getreent())->_stderr)
    # 39 "test_debugger.cpp"
    , "some_existent %s\n", v0 , v1, v2, v3 ); }
    
    int main(int argc, char const *argv[])
    {
        some_optional();
        some_existent("varing");
    }
    

    运行它:./main

    some_optional
    some_existent varing
    

    相关问题:

    1. comma (,) in C Macro Definition
    2. Comma in C/C++ macro
    3. Using comma as macro name in C or C++
    4. C preprocessor macro doesn't parse comma separated tokens?
    5. C-preprocessor: iteratively expand macro to comma-separated list
    6. What are the valid characters for macro names?
    7. Concatenation of tokens in variadic macros
    8. Is it possible to iterate over arguments in variadic macros?
    9. C++ preprocesor macro for accumulating comma-separated strings
    10. Variadic macros with zero arguments, and commas
    11. C Preprocessor Remove Trailing Comma
    12. Is it possible to stringify a C macro that contains a comma?
    13. How to expand macro and delete comma
    14. What type of content is allowed to be used as arguments for C preprocessor macro?
    15. How does expansion of the macro parameters work in c++
    16. How to make a preprocessor macro greedy?
    17. Passing a template which requires a comma to a single-argument macro
    18. Comma in C/C++ macro passed to another macro
    19. How to stringify a string which contains a comma?

    【讨论】:

      猜你喜欢
      • 2014-11-29
      • 2013-07-13
      • 1970-01-01
      • 2021-10-30
      • 2015-08-03
      • 1970-01-01
      • 2018-06-17
      • 2017-10-22
      • 1970-01-01
      相关资源
      最近更新 更多