LINUX.ORG.RU

[странно?][c++]Вызов вариадической функции без обязательного указания количества аргументов

 


0

1

Обычно вызвать такие функции можно f(int amount, ...), где первый аргумент - кол-во следующих за ним. Можно ли вызывать такие функции иначе и как тогда сконструировать обработку аргументов? Насколько я понимаю, аргументы ложатся в стек так, чтобы вызываться в обратном порядке, причем подряд. Можно ли как-то установить место, где следует остановиться и перестать доставать следующие содержимые стека? Или я вообще неправильно понимаю работу это штуки? Разжуйте, пожалуйста.

★★★★★

Можно ли как-то установить место, где следует остановиться и перестать доставать следующие содержимые стека?

Только на основе уже заданных аргументов, насколько я понимаю. Вообще то man va_arg - уже есть готовые средства.

ЗЫ вариадическая функция О_О ... минут пять втыкал о чем речь.

AIv ★★★★★
()

А почему массив не передать? Эллипсисы не вписываются в плюсовую typesafe-парадигму.

Absurd ★★★
()

Можно доставать, пока не встретишь заранее оговоренный терминатор, например NULL. Можно доставать согласно шаблону, как в *printf(). Можно оговорить способ вызова как-то еще, например суешь тип значения int > 0, потом собственно значение соответсвующего размера, ..., заканчиваешь типом значения 0. Все обсуждаемо.

f1("ls", "-l", "-F", NULL);
f2(T_INT, 3, T_FLT, 3.14, T_STR, "str", 0);
printf("%d %g %s\n", 3, 3.14, "str");
arturpub ★★
()

variadic functions - это пережиток С, исключающий контроль типов и ограничивающий типы аргументов, передаваемых через `...'. В С++ следует использовать перегрузку операторов (см. boost::format) или variadic templates.

Begemoth ★★★★★
()
Ответ на: комментарий от AIv

вариадическая функция О_О ... минут пять втыкал о чем речь.

А по-русски получается слишком длинно (функция с переменным числом аргументов) :-( Как бы ты по русски назвал VVTT (variadic variadic template templates)?

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

Я бы вообще не стал его никак называть. Хотя сам и люблю поизвращаться с шаблонами, но всему есть предел;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

Или уж назвал по англицки так, что бы гуголь мог выдать что нить разумное по названию.

AIv ★★★★★
()
Ответ на: комментарий от cdshines

va_arg требует va_start(vl, amount), где второй аргумент - то же к-во аргументов.

Ты ман вообще читал?

void va_start(va_list ap, last);

The va_start() macro initializes ap for subsequent use by va_arg() and va_end(), and must be called first.

The argument last is the name of the last argument before the variable argument list, that is, the last argument of which the calling function knows the type.

Begemoth ★★★★★
()

Можно - man va_arg

Но этому не место в C++.

Deleted
()
Ответ на: комментарий от Begemoth

VT я нагуглил, но что насчет перегрузки? представим, что я использую эту функцию в случае с конструктором с параметрами. Перегружать ()? http://stackoverflow.com/questions/1436968/variadic-function-without-specifie...

Вот отсюда я вообще ничего не понял)

cdshines ★★★★★
() автор топика
Ответ на: комментарий от cdshines

В любом случае, как мне пройтись тогда по всему va_list?

Гарантированно, именно по всему тому списку, что был передан - никак. Тебе нужен либо параметр, указывающий количество остальных параметров (как форматная строка в printf) или маркер конца (если ты передаёшь указатели, что можно использовать 0).

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

Да, только что попробовал, работает. Спасибо. А почему такому не место в С++? Спрашиваю у тебя, потому что автор утверждения ляпнул и молчит, как будто от балшой авторитет и ему нужно априорно верить.

cdshines ★★★★★
() автор топика
Ответ на: комментарий от cdshines

Пример с перегрузкой:

#include <iostream>
#include <string>
#include <vector>

class PrintStrings
{
  std::vector<std::string> strings;
public:
  // собирает параметры, которые передавались бы через ...
  PrintStrings& operator<< (const std::string& s)
  {
    strings.push_back(s);
    return *this;
  }

  // собственно выполняем то что должна делать функция
  void operator() () const
  {
    for (std::size_t i = 0; i < strings.size(); i++)
      std::cout << strings[i] << ' ';
  }
};

int main()
{
  (PrintStrings() << "123" << "abc" << "qwe")();
  std::cout << std::endl;
}

Begemoth ★★★★★
()
Ответ на: комментарий от cdshines

А почему такому не место в С++?

1. Типы параметров передаваемых через `...' никак не проверяются.

2. Эти типы не должны иметь нетривиальный конструктор копирования или деструктор (т.е. нельзя передавать все контейнерные типы стандартной библиотеки, умные указатели и все классы которые содержат поля этих типов).

Спрашиваю у тебя, потому что автор утверждения ляпнул и молчит, как будто от балшой авторитет и ему нужно априорно верить.

Вообще-то в треде об этом говорили я и Absurd и оба обосновали.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

А что это нам дало? Прости за глупые вопросы, но разве твой код не аналогичен более разборчивому варианту ниже?

PrintStrings a;
a << "123" << "abc" << "qwe";
a();
Разве в этом случае мы получили возможность вызывать функцию с нным к-вом параметров? Или как раз в этом и суть? Тогда конструктор с произвольным к-вом параметров вызвать в привычном формате constructor(1, 2, 3), к примеру, нельзя?

cdshines ★★★★★
() автор топика
Ответ на: комментарий от Begemoth

Все, снимаю свой вопрос выше) Спасибо.

cdshines ★★★★★
() автор топика
Ответ на: комментарий от cdshines

Разве в этом случае мы получили возможность вызывать функцию с нным к-вом параметров?

Мы сымитировали возможность.

Тогда конструктор с произвольным к-вом параметров вызвать в привычном формате constructor(1, 2, 3), к примеру, нельзя?

Да, но можно сделать вспомогательный тип, экземпляр которого будет передаваться конструктору. Полноценные функциии с переменным числом параметров дают только variadic template или списки инициализации (std::initializer_list, это ещё одна черта С++11):

#include <iostream>
#include <string>
#include <vector>

void print_strings(const std::vector<std::string>& strings)
{
  for (std::size_t i = 0; i < strings.size(); i++)
    std::cout << strings[i] << ' ';
}


int main()
{
  print_strings({"123", "aaa", "qwe"});
  std::cout << std::endl;
}
Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

О, то, что нужно. Держи плюсик в карму. А мне надо больше читать.

cdshines ★★★★★
() автор топика
Ответ на: комментарий от Begemoth

это выглядит как говно, поэтому если типы заранее определены и я знаю, что данным кодом никто кроме меня пользоваться не будет, то я выберу va_arg

Reset ★★★★★
()
Ответ на: комментарий от Reset

это выглядит как говно, поэтому если типы заранее определены и я знаю, что данным кодом никто кроме меня пользоваться не будет, то я выберу va_arg

А почему парой аргументов массив+длина не передать?

Absurd ★★★
()
Ответ на: комментарий от Reset

потому что массив надо дополнительно создавать

Если это локальный Сишный массив, то он же все равно будет создан на стеке. Синтаксического же сахара в va_arg я не вижу никакого.

Absurd ★★★
()
Ответ на: комментарий от Reset

За такую хренотень я С++ и ненавижу. За корявые списки инициализации, за разную каку из Си и прочее.

Absurd ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.