问题
The library https://github.com/c42f/tinyformat/blob/2f9335afd9941688e42d60cae5166b9f0600b2d1/tinyformat.h#L1104-L1116, uses this awesome trick to do "variadic" templates on 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'; \
}
I am trying to improve it by eliminating the requirement to duplicate the function printfln
twice, i.e., one time for the base case inline void printfln(const char* fmt)
, and a second time for the "variadic" part template<TINYFORMAT_ARGTYPES(n)> void printfln(const char* fmt, TINYFORMAT_VARARGS(n))
.
They are required to split the printfln
function in two parts because the "variadic" function can only accept one parameter, i.e., printfln("something")
. On this case, the TINYFORMAT_VARARGS(n)
would have to expand to nothing, however, when doing so, it will cause the code to have a trailing comma ,
, leading to a invalid syntax on C++.
I could use the GNU GCC
expansion trick with C macro Token-Pasting Operator ##
to remove the trailing comma, however, this is not portable because it only works for GNU GCC
. Then, my goal is to define the macros already containing a leading comma as the next example:
#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" );
}
But, it does not work. The gcc
compiler is going nuts when if finds a comma
, right after the macro definition: g++ -o main -g -ggdb test_debugger.cpp --std=c++98 && ./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" );
^
In my understanding my TINYFORMAT_FOREACH_ARGNUM(m) m(0) m(1) m(2) m(3)
macro should create the following 4 valid C++ "variadic" functions:
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);
}
Why the gcc
preprocessor is not generating correctly my 4 "variadic" template functions as above?
For reference, I am using:
$ 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.
Update
Output of 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" );
}
Update 2
I tried to change the order of the definition in hope the C preprocessor would expand things right, but it just expanded exactly as before:
#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" );
}
Update 3
As @aschepler commented, I was missing the definition of TINYFORMAT_ARGTYPES_ ## n
, this is a fixed version:
#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();
}
Expanding to: 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();
}
Now the problem is when the macro template expands to something without any template parameters, i.e., template<> inline void some()
, it generates the following code:
template<>
inline void some()
{
fprintf(stderr, "variadic");
}
int main(int argc, char const *argv[])
{
some();
}
Which causes C++ to compiler to throw this error:
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();
^~~~
回答1:
A template can't have zero template parameters. The syntax that starts with template <>
instead is used for an explicit specialization: a declaration to be used instead of the template for a specific set of template arguments.
So your zero-argument version will need to skip the template <>
part. You might do something like:
#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:
Thanks to @aschepler help, I also managed to build this more general solution/example:
#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" );
}
This is the generated code: 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");
}
Running it: ./main
some_optional
some_existent varing
Related questions:
- comma (,) in C Macro Definition
- Comma in C/C++ macro
- Using comma as macro name in C or C++
- C preprocessor macro doesn't parse comma separated tokens?
- C-preprocessor: iteratively expand macro to comma-separated list
- What are the valid characters for macro names?
- Concatenation of tokens in variadic macros
- Is it possible to iterate over arguments in variadic macros?
- C++ preprocesor macro for accumulating comma-separated strings
- Variadic macros with zero arguments, and commas
- C Preprocessor Remove Trailing Comma
- Is it possible to stringify a C macro that contains a comma?
- How to expand macro and delete comma
- What type of content is allowed to be used as arguments for C preprocessor macro?
- How does expansion of the macro parameters work in c++
- How to make a preprocessor macro greedy?
- Passing a template which requires a comma to a single-argument macro
- Comma in C/C++ macro passed to another macro
- How to stringify a string which contains a comma?
来源:https://stackoverflow.com/questions/59331919/how-to-easily-create-fully-variadic-functions-with-c-98-standard