C++ variadic

21334 ワード

Variadic
Talking about variadic , before going down to any language detail , Here I have some questions :
  • How to access the details of variadic argument ?
  • How to know the number of variadic ?
  • How to access each argument ?

  • How to pass through variadic ?
  • How to pass through the whole variadic ?
  • 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;
    }