Talking about variadic , before going down to any language detail , Here I have some questions :
OK , Let me check C/C++ first .
There is no language feature support . If you really need it ,
then pass it as an extra argument.
By some maro ( or maybe function) named va_list
, va_start
, va_args
and va_end
.
#include <iostream>
#include <cstdarg>
////////////////////////////////////////////////
//
// Access variadic argument one by one .
//
void print_args_all_int( int num , ... ) {
va_list va;
va_start(va,num);
std::cout<<" Total "
<<num
<<" variadic int argument : ";
for( int i = 0; i < num ; i ++ ) {
std::cout<<va_arg(va,int)<<" ";
}
std::cout<<std::endl;
va_end(va);
return ;
}
int main() {
print_args_all_int(5,1,2,3,4,5);
return 0;
}
Pass it by va_list variable . However , if you do this , you actually let other function access your function stack .
#include <iostream>
#include <cstdarg>
////////////////////////////////////////////////
//
// Use va_list to pass through variadic
//
void print_va_list( int num , va_list va ) {
std::cout<<" Total "
<<num
<<" variadic int argument : ";
for( int i = 0; i < num ; i ++ ) {
std::cout<<va_arg(va,int)<<" ";
}
std::cout<<std::endl;
}
void print_args_all_int( int num , ... ) {
va_list va ;
va_start(va,num);
print_va_list(num,va);
va_end(va);
}
int main() {
print_args_all_int(5,1,2,3,4,5);
return 0;
}
Auctually when I use va_arg
, it change va_list variable to point next arguement ( which I don’t have language standard support ! ) , so I can clip va_list from head and pass the left part to other function !
“`C++
////////////////////////////////////////////////
//
// Clip variadic and pass left to other function
//
void print_first_arg_recursive( int num , va_list va ) {
if( num < 1 ) {
return ;
}
std::cout<
Use sizeof
operator ,
////////////////////////////////////////////////
//
// Use sizeof to count the variadic size
//
#include <iostream>
template<class ...arg>
size_t get_argument_size(arg...va) {
return sizeof...(va);
}
int main(){
std::cout<<get_argument_size(1,2,3,4)<<std::endl;
return 0;
}
Templates expanded in compile time and all of it arguments were types.
#include <iostream>
////////////////////////////////////////////////
//
// recursive access each argument in template fuction
//
template<class T , class ...Arg>
void pass_arg_by_recursive(T head , Arg ... va ){
std::cout<<"One arg is "<<head<<std::endl;
pass_arg_by_recursive( va...);
}
// Last argument is int
template<>
void pass_arg_by_recursive(int head ){
std::cout<<"Last one arg is i "<<head<<std::endl;
}
// Last argument is float
template<>
void pass_arg_by_recursive(float head ){
std::cout<<"Last one arg is f"<<head<<std::endl;
}
int main() {
pass_arg_by_recursive(1,2,34,4,5,67);
pass_arg_by_recursive(1.3f,67.2f );
return 0;
}
The problem here is : for function template , it only support specialization and partial specialization is not supported ,I have to know the type of last argument to end the recursion.
There is a solution : add a extra explicit type argument as first argument and use it to end the recursion . Then I actually can use any type in variadic.
#include <iostream>
////////////////////////////////////////////////
//
// Use const char * s to end recursion .
//
void printf1(const char *s) { // Use const char * s to end recursion .
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cerr<<"invalid format string: missing arguments"<<std::endl;
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf1(const char *s, T value, Args... args) {// Here add extra explicit type argument const char * s . It can be used to end recursion.
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
s += 2; // this only works on 2 characters format strings ( %d, %f, etc ). Fails miserably with %5.4f
printf(s, args...); // call even when *s == 0 to detect extra arguments
return;
}
}
std::cout << *s++;
}
}
int main(){
print1("%d , %s ",1,"test");
return 0;
}
#include <iostream>
#include <string>
////////////////////////////////////////////////
//
// Use inherit to access each argument
//
template <class T , class ... arg>
class Test : public Test<arg...>{
public:
Test(T t, arg ...va) : t_(t) , Test<arg...>(va...) {}
void PrintT() {
std::cout<<t_<<std::endl;
Test<arg...>::PrintT();
}
private :
T t_;
};
// Use below to end the recursion
template<>
class Test<std::string>{
public:
Test(std::string t) :t_(t) {}
void PrintT() {
std::cout<<t_<<std::endl;
}
private :
std::string t_;
};
int main() {
Test<int,float,std::string> m{1,1.1f,std::string("test")};
m.PrintT();
return 0;
}
Also , If I don’t want to let the last argument fixed , use an extra explicit argument so that I can use partial specialization to deal the last argument.
#include <iostream>
#include <string>
////////////////////////////////////////////////
//
// Use extra explicit argument .
//
template <class size_t , class T , class ... arg>
class Test1 : public Test1<size_t, arg...>{
public:
Test1(T t, arg ...va) : t_(t) , Test1<size_t,arg...>(va...) {}
void PrintT() {
std::cout<<t_<<std::endl;
Test1<size_t,arg...>::PrintT();
}
private :
T t_;
};
template<class size_t ,class T>
class Test1<size_t,T>{
public:
Test1(T t) : t_(t) {}
void PrintT() {
std::cout<<t_<<std::endl;
}
private :
T t_;
};
int main() {
Test1<size_t,int,float,std::string> m{1,1.1f,std::string("test")};
m.PrintT();
return 0;
}
As the example above . Arg...va
can be passed into T head, Arg...va
, which actually clip cout first argument and pass the left as a standalone argument packet .
use argument packet arg...
to pass variadic throught.
////////////////////////////////////////////////
//
// Pass argument packet.
//
#include <iostream>
template<class ...arg>
size_t get_argument_size(arg...va) {
return sizeof...(va);
}
template<class ... arg>
void pass_arguments(arg ...va){
std::cout<<get_argument_size(va...)<<std::endl;
}
int main(){
pass_arguments(1,2,3,4,5);
return 0;
}
The only support of marco is use __VA_ARGS__
to expand ...
. However , macro was expanded at per-compile period , so it is easy to combine variadic marco and others ( like variadic template ) .
////////////////////////////////////////////////
//
// Use __VA_ARGS__ to expand all varadic into variadic function
//
#define CALL_F(...) f(__VA_ARGS__)
template<class ...Arg>
void f(Arg...va) {
std::cout<<sizeof...(va)<<std::endl;
}