Какие есть способы физически, на уровне машинного кода, передать переменное число параметров в подпрограмму?
1. Автобоксинг в массив. Если к тому же тип элементов может быть любым — тогда будет массив «лёгких» или обычных variant’ов, или массив объектов на «куче». В Delphi для этого используется лёгкий variant (см. SysUtils.Format), в Java и других «мусорных» языках — объекты на куче. В Си++ нет.
2. Автоматически развернуть такой вызов в кучу вызовов поменьше. См. вариативные шаблоны C++11 = variadic templates. В Си++ есть, штука тяжёлая, я с ней и сам знаком поверхностно и ничего толком рассказать не могу. Но вот кое-что набросал.
#include <iostream>
constexpr int countArgs() { return 0; }
template <class Arg, class ... Args>
constexpr int countArgs(const Arg& x, const Args& ... args)
{
return countArgs(args...) + 1;
}
int main()
{
std::cout << countArgs() << std::endl;
std::cout << countArgs(1, 2, 3) << std::endl;
return 0;
}
Может быть, и первый путь удастся завернуть в массив через вариативные шаблоны, но я не в курсе. А вот обрабатывать аргументы по одному — за милую душу!
3. Использовать особые соглашения вызова и раскручивать стек, пока не попадётся какой-то маркер «больше параметров нет» (см. работу с формами cURL), или окольным путём узнать количество параметров (см. printf). Есть даже в Си (который не что иное, как «ассемблер высокого уровня»), штука очень системная и чреватая ошибками.
4. Возможна ещё и такая фишка: физически оно устроено как printf, но обёрнуто в «лёгкий» вариативный шаблон, который защищает всё это добро от ошибок программиста.
#include <iostream>
#include <cstdarg>
constexpr int countArgs() { return 0; }
template <class ... Args>
constexpr int countArgs(int x, Args ... args)
{
return countArgs(args...) + 1;
}
void outArgsInner(int count, ...)
{
va_list ap;
va_start(ap, count);
if (count > 0) {
std::cout << va_arg(ap, int);
for (int i = 2; i <= count; ++i) {
std::cout << ' ' << va_arg(ap, int);
}
}
va_end(ap);
std::cout << std::endl;
}
template <class ... Args>
inline void outArgs(Args ... args)
{
outArgsInner(countArgs(args...), args...);
}
int main()
{
outArgs(); // пустой тоже работает
outArgs(1, 2, 3);
// outArgs("a", 2, 3); тут ошибка! — и верно, мы принимаем только int’ы
return 0;
}