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