Sometimes, we are supposed to accept uncertain number of arguments in C++.
In this post, I am going to introduce 3 ways to implement it, so that you can pick up one of them which is most fit for you in current situation.
1. va_list
va_list is coming from C lang. Had you ever considered how to implement the function “printf”? Of course, they are using va_list.
Here is a tiny example
/* output:
:23 args_copy[0]=3
:23 args_copy[1]=5
sum of: 1, 2, 3 = 9
*/
#include
#include
int sum(int count, ...) {
int sum = 0;
va_list args;
va_start(args, count);
va_list args_copy;
for (int j = 0; j < count; j++) {
sum += va_arg(args, int);
if (j == 0) {
// args_copy will copy args to args_copy from current
va_copy(args_copy, args);
}
}
va_end(args);
for (int j = 0; j < (count - 1); j++) {
printf("%s:%d args_copy[%d]=%d\n", __FILE__, __LINE__, j,
va_arg(args_copy, int));
}
va_end(args_copy);
return sum;
}
int main() {
printf("sum of: 1, 2, 3 = %d\n", sum(3, 1, 3, 5));
}
Please refer to this link and try yourself. You can also refer to this link for its documentation.
You will use va_start, va_copy, va_arg, va_end to initialize, copy, scan and finalize the args easily.
The positive is you can use it either in C or C++, but the negative is you can’t get the count and type of args.
2. initializer_list
initializer_list is a lightweight solution to pass a set of args to some function.
Here is a simple example for initializer_list
/* output
got: 1
got: 2
got: 3
got: 4
total size: 4
sum of them is 10
*/
#include
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::initializer_list;
using std::string;
int sum(initializer_list il) {
int sum = 0;
for (auto i : il) {
cout << "got: " << i << endl;
}
cout << "total size: " << il.size() << endl;
for (auto it = il.begin(); it != il.end(); it++) {
sum += *it;
}
return sum;
}
int main() {
cout << "sum of them is " << sum({1, 2, 3, 4}) << endl;
}
Please refer to this link and try yourself.
The positive is you can use it easily in construction function, and gather results like a vector. The negative is it is pretty hard to pass a set of args in different type.
3. template
Template is a pretty useful feature in C++.
In this way, we can create a few functions, and compiler will automatically create related functions in compile time.
Here is a small example using template parameters:
/* output:
accept: add type int, value1
accept: add type double, value2
accept: add type string, value abc=>3
sum of them is 6
*/
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::string;
class S {
public:
S() : sum_(0) {}
template
S(const Args &... args) : sum_(0) {
Add(args...);
}
S &Add(int i) {
sum_ += static_cast(i);
cout << "accept: add type int, value" << i << endl;
return *this;
}
S &Add(double i) {
sum_ += i;
cout << "accept: add type double, value" << i << endl;
return *this;
}
S &Add(const string &s) {
sum_ += s.length();
cout << "accept: add type string, value "
<< s <" << s.length() << endl;
return *this;
}
template
S &Add(const First &first, const Rest &... rest) {
return Add(first).Add(rest...);
}
double val() { return sum_; }
private:
double sum_;
};
int main() {
S s(1, 2.0, string("abc"));
cout << "sum of them is " << s.val() << endl;
}
Please refer to this link and try yourself.
The positive is you can handle a lot of certain situation, and will quickly failed in compile time, the negative is it really spend a lot of time in coding.
4. template + initializer_list
If we wish to implement a API, which will accept a set of similar args. we can also use template as the frontend, and use initializer_list as the backend.
For example, if we need to accept a set of string args (whatever the input is string, const string, or const char *), the implement may seems as follow:
/* output:
got: a
got: bc
got: def
the result is: abcdef
*/
#include
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::initializer_list;
std::string StrCatImpl(initializer_list sl) {
std::string base;
for (auto s : sl) {
cout << "got: " << s << endl;
base += s;
}
return base;
}
template
string StrCat(const T &... args) noexcept {
return StrCatImpl({args...});
}
int main() {
std::string s = StrCat("a", "bc", string("def"));
cout << "the result is: " << s << endl;
}
Please refer to this link and try yourself.
2 Comments
xqiushi · February 15, 2018 at 03:39
新年快乐
Yu · April 4, 2019 at 12:18
好久不见。靴靴