Я, как правило, избегаю varargs, за исключением одного конкретного случая, когда это очень полезно. Переменные аргументы на самом деле не дают такой большой выгоды сверх того, что может быть сделано отдельными вызовами функций, особенно в вашем случае.
С точки зрения читабельности (и это обычно то, что я предпочитаю необработанной скорости, за исключением очень специфических случаев), между этими двумя опциями нет реальной разницы (я добавил счетчик к версиям varargs, так как вам нужен либо счетчик или дозорный, чтобы определить конец данных):
update(2, FIELD_NAME1, 10, FIELD_NAME2, 20);
update(3, FIELD_NAME3, 10, FIELD_NAME4, 20, FIELD_NAME5, 30);
/* ========== */
update(FIELD_NAME1, 10);
update(FIELD_NAME2, 20);
update(FIELD_NAME3, 10);
update(FIELD_NAME4, 20);
update(FIELD_NAME5, 30);
На самом деле, поскольку версия varargs становится длиннее, вам все равно придется разделить ее для форматирования:
update(5,
FIELD_NAME1, 10,
FIELD_NAME2, 20,
FIELD_NAME3, 10,
FIELD_NAME4, 20,
FIELD_NAME5, 30);
Выполнение этого способа «один вызов на имя поля» приводит к упрощению кода в самой функции и не ухудшает читаемость вызовов. Кроме того, он позволяет компилятору правильно обнаруживать определенные ошибки, которые он не может сделать для переменных, например, неправильные типы или несоответствие между предоставленным пользователем количеством и фактическим числом.
Если вы действительно должны быть в состоянии вызвать одну функцию, чтобы сделать это, я бы выбрал:
void update (char *k1, int v1) {
...
}
void update2 (char *k1, int v1, char *k2, int v2) {
update (k1, v1);
update (k2, v2);
}
void update3 (char *k1, int v1, char *k2, int v2, char *k3, int v3) {
update (k1, v1); /* or these two could be a single */
update (k2, v2); /* update2 (k1, v1, k2, v2); */
update (k3, v3);
}
/* and so on. */
Вы можете даже выполнять высокоуровневые функции как макросы, если хотите, без потери безопасности типов.
Единственное место, где я склонен использовать функции varargs, - это предоставление тех же функций, что и printf()
- например, мне иногда приходилось писать библиотеки журналов с такими функциями, как logPrintf()
, обеспечивающими такие же функции. Я не могу думать о любом другом времени в моем долгом (а я имею в виду, долгом :-) времени на угольном заборе, которое мне нужно было использовать.
Кроме того, если вы решите использовать varargs, я склоняюсь к дозорным, а не к подсчетам, поскольку это предотвращает несоответствия при добавлении полей. Вы можете легко забыть настроить счет и в итоге:
update (2, k1, v1, k2, v2, k3, v3);
при добавлении, что коварно, потому что оно молча пропускает k3 / v3, или:
update (3, k1, v1, k2, v2);
при удалении, что почти наверняка фатально для успешного запуска вашей программы.
Наличие стража предотвращает это (если, конечно, вы не забудете стража):
update (k1, v1, k2, v2, k3, v3, NULL);