Я экспериментирую с переменными аргументами в C ++, используя va_args. Идея полезна, и это действительно то, что я часто использовал в C # с помощью функции params. Одна вещь, которая меня расстраивает, это следующая выдержка, касающаяся va_args, выше:
Также обратите внимание, что va_arg не определяет, является ли извлеченный аргумент последним аргументом, переданным функции (или даже если это элемент после конца этого списка).
Мне трудно поверить, что нет никакого способа программно определить количество переменных аргументов, переданных функции изнутри самой функции. Я хотел бы выполнить что-то вроде следующего:
void fcn(int arg1 ...)
{
va_list argList;
va_start(argList, arg1);
int numRemainingParams = //function that returns number of remaining parameters
for (int i=0; i<numRemainingParams; ++i)
{
//do stuff with params
}
va_end(argList);
}
Повторим, приведенная выше документация предполагает, что va_arg не определяет, является ли найденный аргумент последним в списке. Но я чувствую, что эта информация должна быть доступна каким-то образом.
Есть ли стандартный способ достижения этого?
Мне трудно поверить, что нет никакого способа программно определить количество переменных аргументов, переданных функции изнутри самой функции.
Тем не менее, это правда. C / C ++ не ставят маркеры в конец списка аргументов, поэтому вызываемая функция действительно не знает, сколько аргументов она получает. Если вам нужно отметить конец аргументов, вы должны сделать это самостоятельно, поставив какой-то маркер в конец списка.
Вызываемая функция также не имеет представления о типах или размерах предоставленных аргументов. Вот почему printf
и друзья заставляют вас указать точный тип данных значения для интерполяции в строку формата, а также почему вы можете аварийно завершить программу, вызвав printf со строкой неверного формата.
Обратите внимание, что передача параметров определяется ABI для конкретной платформы, а не стандартами C ++ / C. Однако ABI должен обеспечивать возможность реализации стандартов C ++ / C. Например, ABI может захотеть передать параметры в регистрах для эффективности, но в этом случае может оказаться невозможным легко реализовать va_args. Таким образом, возможно, что аргументы также затенены в стеке. Практически ни в одном случае стек не помечен для отображения конца списка аргументов, хотя, поскольку стандарты C ++ / C не требуют, чтобы эта информация была доступна, и поэтому это потребовало бы ненужных накладных расходов.
Работа переменных аргументов в C и C ++ относительно проста: аргументы просто помещаются в стек, и вызывающий объект должен несколько выяснить, какие есть аргументы. В стандарте нет ничего, что позволяло бы определять количество аргументов. В результате количество аргументов определяется некоторой контекстной информацией, например количеством элементов, на которые ссылаются в строке формата.
Отдельные компиляторы могут знать, сколько элементов существует, но нет стандартного интерфейса для получения этого значения.
Однако вместо этого вы можете использовать шаблоны с переменным числом аргументов: вы можете определить очень подробную информацию об аргументах, передаваемых в функцию. Интерфейс выглядит по-другому, и может потребоваться направить аргументы в какую-то структуру данных, но, с другой стороны, он также будет работать с типами, которые нельзя передать с помощью переменных аргументов.
Нет, нет Вот почему переменные аргументы не безопасны. Они являются частью C, которой не хватает выразительности для обеспечения безопасности типов для «удобных» функций с переменными числами. Вы должны жить с фактом, что C содержит конструкции, чья правильность зависит от ценности и не только на типах. Вот почему это «небезопасный язык».
Не используйте переменные аргументы в C ++. Это гораздо более сильный язык, который позволяет вам писать одинаково удобный и безопасный код.
Нет, такого способа нет. Если у вас есть такая необходимость, вероятно, лучше упаковать эти параметры функции в std::vector
или подобная коллекция, которая может быть повторена.
Как бы вам ни хотелось, чтобы это было правдой, не существует прямого способа узнать тип или количество аргументов, передаваемых в функции с переменным числом аргументов.
Одним из вариантов является передача маркера в конце списка аргументов, например, с помощью, например, execl ():
execl (path, arg1, ... argn, NULL);
Другой способ — передать число (и тип) аргументов в явном виде, например, с помощью printf () и его строки формата.
Таким образом, функции Variadic небезопасны, и малейшая ошибка приведет к сбою кода. В C ++ есть безопасные альтернативы, такие как передача вектора. Если все аргументы не имеют одинаковый тип, вы можете заключить их в вариант контейнера, например повышение :: любой.
Переменный список аргументов — это очень старая концепция, унаследованная от истории C в C ++. Это относится ко времени, когда программисты на C обычно имели в виду сгенерированный ассемблерный код.
В то время компилятор вообще не проверял, соответствуют ли данные, которые вы передали функции при вызове, типам данных, которые ожидала получить функция. Это было обязанностью программиста сделать это правильно. Если, например, вызывающий вызвал функцию с char
и функция ожидала int
программа потерпела крах, хотя компилятор не жаловался.
Сегодняшняя проверка типов предотвращает эти ошибки, но со списком переменных аргументов вы возвращаетесь к этим старым концепциям, включая все риски. Так что не используйте его, если можете как-то избежать этого.
Тот факт, что этой концепции уже несколько десятилетий, вероятно, является причиной того, что она кажется неправильной по сравнению с современными концепциями безопасного кода.