Home > OS >  Variadic functions to take any number of objects
Variadic functions to take any number of objects

Time:09-28

I have a class called parser with a member function madd_arguments(const argument args, ...). argument is a separate class and has a member variable arg_name to differentiate one from another. I want the function madd_arguments to accept any number of argument objects. To achieve this, I have the following code:

class argument{
private:
    std::string arg_name;                        
public:                             
    argument(const std::string& ARG_NAME) : arg_name(ARG_NAME){}     
    friend class parser;
};
#define add_arguments(...) madd_arguments(__VA_ARGS__, argument("END_HERE"))

#include "arg.h"

class parser {
private:
    std::vector<argument> known_arguments;                      //holds all arguments
public:
    void madd_arguments(const argument args, ...);
};


void parser::madd_arguments(const argument args, ...) {
    va_list vargs;
    for (va_start(vargs, args); args.arg_name != "END_HERE"; args = va_arg(vargs, const argument)){
        known_arguments.push_back(args);
    }

    va_end(vargs);
}

and my main:


int main() {

    argument a("name");
    a.set_flags("-v", "-verbose", "bobby", "jones");

    argument b("string argument");

    parser p;
    p.add_arguments(a,b);
}

but I get the following errors: error: passing ‘const argument’ as ‘this’ argument discards qualifiers [-fpermissive] _start(vargs, args); args.arg_name != "END_HERE"; args = va_arg(vargs, const argument)){ and error: passing ‘const std::vector<argument>’ as ‘this’ argument discards qualifiers [-fpermissive]known_arguments.push_back(args);

After looking at other posts on here I figured that I would have to make the member function madd_arguments a const function but I can't because I am going to make changes to the variable known_arguments which is a member of the parser object.

I used macro and the va_list based on this post: How do I write functions that accept unlimited arguments?. Anybody know how I can get around this or should I just go back to using variadic templates instead?

CodePudding user response:

This is C . You should use variadic templates. some thing like what follows

class argument {
  private:
    std::string arg_name;

  public:
    argument(const std::string& ARG_NAME) : arg_name(ARG_NAME) {}
    friend class parser;
};

class parser {
  private:
    std::vector<argument> known_arguments;

  public:
    template <class... T>
    requires(std::is_convertible_v<std::common_type_t<T...>,
                                   argument>) void add_arguments(T&&... args) {
        known_arguments.reserve(sizeof...(args)   known_arguments.size());
        (known_arguments.push_back(std::forward<T&&>(args)), ...);
    }
};

int main() {

    argument a("name");
    argument b("string argument");

    parser p;
    p.add_arguments(a, b);
}

CodePudding user response:

As already stated before, the cause of the problem is this:

void parser::madd_arguments(const argument args, ...) {
    va_list vargs;
    for (va_start(vargs, args); args.arg_name != "END_HERE"; args = va_arg(vargs, const argument)){
        known_arguments.push_back(args);
    }

    va_end(vargs);
}

args is const so it is immutable so the assignment args = va_arg(vargs, const argument) is invalid. In order to make it work, remove the const keyword and it will run just fine:

void parser::madd_arguments(argument args, ...) {
    va_list vargs;
    for (va_start(vargs, args); args.arg_name != "END_HERE"; args = va_arg(vargs, argument)){
        known_arguments.push_back(args);
    }

    va_end(vargs);
}

But this isn't exactly something that you'd do in C 11 because of the presence of Variadic templates and parameter packs which can prevent you from having to explicitly use macros and from having to use an upper bound like "END_HERE".

Basically, the compiler will do all the hard work for you by instantiating/inlining templates based on the number of parameters passed.

The following is C 11 and seems to work:

#include <vector>
#include <string>
#include <utility>

class argument {
private:
    std::string arg_name;
public:
    argument(const std::string& ARG_NAME) : arg_name(ARG_NAME){}
    friend class parser;
};

class parser {
private:
    std::vector<argument> known_arguments;
public:
    template <typename Arg>
    void add_arguments(Arg&& arg) {
        known_arguments.push_back(std::forward<Arg>(arg));
    }
    template <typename Arg, typename ...Args>
    void add_arguments(Arg&& arg, Args&& ...args) {
        known_arguments.push_back(std::forward<Arg>(arg));
        add_arguments(std::forward<Args>(args)...);
    }
    // ...
};

int main() {
    argument a("name");
    // a.set_flags("-v", "-verbose", "bobby", "jones");

    argument b("string argument");

    parser p;
    p.add_arguments(a,b);
}
  • Related