C++ variadic

Variadic

Talking about variadic , before going down to any language detail , Here I have some questions :

  1. How to access the details of variadic argument ?
    1. How to know the number of variadic ?
    2. How to access each argument ?
  2. How to pass through variadic ?
    1. How to pass through the whole variadic ?
    2. How to clip and pass through part of variadic ?

OK , Let me check C/C++ first .

C / C++

variadic function

How to know the number of variadic ?

There is no language feature support . If you really need it ,
then pass it as an extra argument.

How to access each 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;
}

How to pass through the whole variadic

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;
}

How to clip and pass through part of variadic

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<

variadic template

How to know the number of variadic ?

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;
}

How to access each argument ?

Templates expanded in compile time and all of it arguments were types.

Template function use recursive to access each type

#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;
}

Template class use inherit to access each type

#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;
}

How to clip and pass through part of variadic

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 .

How to pass through the whole variadic ?

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;
}

variadic macro

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;
}

你可能感兴趣的:(C++,Variadic)