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();
^~~~
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,,) ); \
}
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?
Related
Is it possible to replace the same identifier with different replacements?
I tried
#define M a
#define REPLACE_WITH_a(X) X
#undef M
#define M b
#define REPLACE_WITH_b(X) X
#undef M
#define FOO(X) REPLACE_WITH_a(X) REPLACE_WITH_b(X)
//FOO(M) evaluates to M M instead of a b
but M doesn't get replaced with either a or b
My infix operator for powering isn't working. It adds or subtracts.
precedencegroup SquareSumOperatorPrecedence {
lowerThan: MultiplicationPrecedence
higherThan: AdditionPrecedence
associativity: left
assignment: false
}
infix operator ^: SquareSumOperatorPrecedence
func ^(lhs: Double, rhs: Double) -> Double {
return pow(lhs, rhs)
}
9 ^ 2// equals 81
// It gives me 11
I thought 9 ^ 2 would give me 81, but it gave me 11
^ is already defined as “bitwise XOR” operator for integer operands, and that is what 9 ^ 2 computes:
9 ^ 2 = 0b1001 ^ 0b0010 = 0b1011 = 11
(See Advanced Operators for more information about bitwise operators.)
You defined an operator for double operands, and that works as expected if you actually call it with Double values:
print(9.0 ^ 2.0) // 81
Consider the following code
module class_type
implicit none
class(*), pointer :: fnzo => null()
type, abstract :: gen
real :: ss
integer :: sdsd
class(gen), pointer :: next =>null()
end type
type, extends(gen) :: final1
real :: ss1
end type final1
type, extends(gen) :: final2
real :: x1(10)
end type
end module class_type
program test_class
use class_type
implicit none
class(gen), pointer :: test
type(final1) :: test1
allocate(test, source = test1)
print*, test% ss1
end program
I am trying to work out a a linked - list where each next element either inherits final1 or final2. Currently, just testing simple cases and I don't understand why I cannot access test%ss1. Anyone to help
As for "why we cannot access test% ss1", the following code might be useful for considering the reason. In this code, I get a user input (inp) and determine whether ptr points to variables of type test1 or test2. If ptr points to a variable of test2, accessing ptr% ss1 is clearly meaningless, so the compiler needs to guard against such an incorrect access. I think that is why the compiler allows only the access of components of gen (= declared type) unless select type (for actual data in memory) is provided.
program test_class
use class_type
implicit none
class(gen), pointer :: ptr !! parent type
type(final1), target :: test1 !! child type
type(final2), target :: test2 !! child type
integer :: inp
print *, "input some integer"
read *, inp
if ( inp == 1 ) ptr => test1
if ( inp == 2 ) ptr => test2
print*, "ptr% ss = ", ptr% ss !! OK
! print*, "ptr% ss1 = ", ptr% ss1 !! error (Line1)
select type ( ptr )
type is ( final1 ); print*, "ss1 = ", ptr% ss1 !! OK
type is ( final2 ); print*, "x1 = ", ptr% x1 !! OK
endselect
end program
If the Line1 is uncommented, it gives an error (with gfortran-8)
print*, "ptr% ss1 = ", ptr% ss1 !! error
1
Error: 'ss1' at (1) is not a member of the 'gen' structure; did you mean 'ss'?
I guess the situation is similar to other (statically typed) languages. For example, the following code in C++ gives an error when we access ptr->b.
#include <iostream>
using namespace std;
struct A {
int a = 1;
};
struct B : A {
int b = 100;
};
int main() {
A *ptr;
ptr = new B;
cout << ptr->a << endl; // 1
// cout << ptr->b << endl; // error: 'struct A' has no member named 'b'
delete ptr;
}
In order to access ss1, you need a pointer within your abstract class to the child object. The pointer can be of either type extensions you wish to define. Since your linked list has two different type extensions, you are required to declare two child pointers within your abstract class.
module class_type
implicit none
class(*), pointer :: fnzo => null()
type, abstract :: gen
real :: ss
integer :: sdsd
class(gen), pointer :: next =>null()
type(final1), pointer :: child
end type
type, extends(gen) :: final1
real :: ss1
end type final1
type, extends(gen) :: final2
real :: x1(10)
end type final2
end module class_type
program test_class
use class_type
implicit none
class(gen), pointer :: test
type(final1), target :: test1
allocate(test, source=test1)
test1% ss1 = 20.0
test% child => test1
write(*,*) test1% ss1
write(*,*) test% child% ss1
end program test_class
When you compile and execute, you'll see:
20.00000
20.00000
I'd like to pass a keyword argument to a macro in julia. I try to parse the kwargs manually and build the Expr but got errors. Do I miss something?
macro sort(x, kv)
#show x,kv
d = []
if kv.head == :(=)
# push!(d, (kv.args[1], kv.args[2])) # fail
push!(d, (kv.args[1], esc(kv.args[2]))) # fail also
end
ex = quote
sort($x; $(d...))
end
Meta.show_sexpr(ex)
ex
end
x = collect(1:5)
rev=true
#sort x rev=rev
Output:
(x, kv) = (:x, :(rev = rev))
(:block,
(:line, 9, Symbol("REPL[36]")),
(:call, :sort, (:parameters, (:rev, :($(Expr(:escape, :rev))))), :x)
)ERROR: TypeError: non-boolean (Expr) used in boolean context
Stacktrace:
[1] (::Base.#kw##sort!)(::Array{Any,1}, ::Base.#sort!, ::Array{Int64,1}) at ./<missing>:0
[2] #sort#8(::Array{Any,1}, ::Function, ::Array{Int64,1}) at ./sort.jl:546
[3] (::Base.#kw##sort)(::Array{Any,1}, ::Base.#sort, ::Array{Int64,1}) at ./<missing>:0
[4] macro expansion at ./REPL[36]:9 [inlined]
[5] anonymous at ./<missing>:?
Edit: I am able to build keyword args using :parameters Expr. My generated Expr looks like the normal one. But strangely, I got the error "syntax: invalid syntax (parameters (kw (outerref rev) true))"
julia> macro m(f, x, ks...)
#show f x ks
ex = Expr(:call, f, x)
par = Expr(:parameters )
push!(ex.args, par)
for kv∈ks
k,v = kv.args
push!(par.args, Expr(:kw, k, v))
end
Meta.show_sexpr(ex)
ex
end
#m (macro with 1 method)
julia>
julia> Meta.show_sexpr(:(sort(1:9; rev=true)))
(:call, :sort, (:parameters, (:kw, :rev, true)), (:(:), 1, 9))
julia> #m sort 1:9 rev=true
f = :sort
x = :(1:9)
ks = (:(rev = true),)
(:call, :sort, (:(:), 1, 9), (:parameters, (:kw, :rev, true)))ERROR: syntax: invalid syntax (parameters (kw (outerref rev) true))
In the various Lisps, it's possible for me to create a sequence of functions as if they'd just been normal values:
(def ops [+ - * /])
Which I can then iterate through, again, as if they were just normal values:
(doseq [op ops] // (doseq (op ops) is like for (op <- ops) in scala
(println (op 1 2 3 4)))
Now, I've tried a few things in Scala, all of them failing:
scala> List(+, -, *, /)
<console>:1: error: illegal start of simple expression
List(+, -, *, /)
^
scala> List[Double => Double](+, -, *, /)
<console>:1: error: illegal start of simple expression
List[Double => Double](+, -, *, /)
^
scala> List[Double => Double](+_, -_, *_, /_)
<console>:8: error: not found: value *
List[Double => Double](+_, -_, *_, /_)
^
<console>:8: error: not found: value /
List[Double => Double](+_, -_, *_, /_)
^
So what's the correct procedure of defining a list of functions/operators in Scala?
The problem is that these functions are binary operators, i.e., they take two operands and return one. So you have to use:
List[(Double, Double) => Double](_ + _, _ - _, _ * _, _ / _)
In Scala, most value-level operators are actually unary instance methods (maybe all are, not sure if there are counterexmaples). So when you talk about, say, addition of two values of type Double, you're really referring to a method of the first value that takes the second value as a parameter. The familiar infix operator syntax is just sugar. In other words,
scala> 5.7 + 6.3
res0: Double = 12.0
is really just a nice shorthand for:
scala> 5.7.+(6.3)
res1: Double = 12.0
As #bluenote10 already mentioned, you can capture these operators by creating anonymous function that take both the first instance and its operand as parameters and returns the result of the + method:
scala> (lhs: Double, rhs: Double) => lhs + rhs
res2: (Double, Double) => Double = <function2>
Or as in #bluenote10's example, if the type can be inferred (like in a list with the type provided), you can use the nice underscore syntax:
scala> val doublePlus: (Double, Double) => Double = _ + _
doublePlus: (Double, Double) => Double = <function2>