LINUX.ORG.RU

[C++] Переменное количество параметров в аргументах функции

 


0

0

Как сделать сабж? Во всех примерах, которые мне попадались, мухлюют: в качестве первого параметра передают общее количество параметров, либо последним передают "-1" либо «0».

Можно ли в функции с переменным количеством параметров определить общее их число без подобных хаков?


template<typename T1>
void Func(T1 *t1);

template<typename T1, typename T2>
void Func(T1 *t1,T2 *t2);

template<typename T1, typename T2, typename T3>
void Func(T1 *t1,T2 *t2,T3 *t3);

c++way

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

Он же вручную это распишет. (:

Автор, смотри Boost.Preprocessor.

l5k
()
Ответ на: комментарий от Elverion

> А что в C++?

А в C++ - жёсткие сигнатуры. Хочется гибкости - юзайте Коммон Лисп :)

mv ★★★★★
()

Ещё можно посмотреть на printf, как он работает с переменным числом параметров. Посмотреть, и никогда так не делать :)

А вообще С++ и переменное число параметров - вещи плохо совместимые. Лучше всего вообще не писать такие функции, или писать в стиле printf. Например объекты нельзя передавать в эти самые функции. Если и другие ограничения, подробнее в стандарте.

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

А что в C++?

вариадические шаблоны в C++0x. в текущей версии стандарта - ничего

jtootf ★★★★★
()

Можно попробовать.

Агрументы функции передаются через стек (для вызова типа C и STD_CALL). Можно пройтись по стеку в поисках адреса возврата для предыдущей функции.

Или это тоже хак?

Liosha_Syrnikov
()
Ответ на: комментарий от Elverion

>>va_list - это для Си.

А что в C++?

классы же!
вашстрауструп

ЗЫ:
можно заюзать какой-нибудь контейнер.
или сделать по принципу манипуляторов ввода/вывода в потоки.

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

>Или это тоже хак?
хак, причем не только очень грязный, но и жутко вонючий!

возникают проблемы:
1) как определить, является ли число адресом?
2) как определить, является ли число адресом функции?
3) как определить, является ли число адресом вызванной функции, а не каким-нибудь коллбэком (хотя на ЦПП колбеки вроде не нужны особо)?
4) как определить, является ли число-не-адрес параметром функции или локальной переменной?

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

можно заюзать какой-нибудь контейнер.

а в какой контейнер можно положить одновременно int, double, void *, и MyObject?

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

boost::any?

тогда уж std::vector<boost::any>, но это добавляет очевидный оверхед на распаковку

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

P.S. Тут есть доля шутки, если что...

существенная, я бы сказал

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

>а в какой контейнер можно положить одновременно int, double, void *, и MyObject?

А что union нынче не кошерен? Только не надо в контейнер ложить MyObject лучше MyObject*. Делаем обычный массив из union и передаем. В большинстве случаев этого хватит. Без всяких мерзких stl::vector и ещё более мерзких шаблонов.

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

А вообще по осторожнее надо в C/C++ c функциями с переменным числом параметров. Будет либо небезопасно, либо жутко тормозззно, либо недостаточно гибко. Чем-то прийдется пожертвовать.

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

> А что union нынче не кошерен?

а в какой контейнер можно положить одновременно int, double, void *, и MyObject?

а в какой контейнер можно положить одновременно


положить одновременно


одновременно</i>

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

> одновременно</i>

очевидно имелся ввиду массив юнионов, хотя к каждому юниону надо еще и тип указывать, да и предложение использовать везде MyObject* - несерьезно

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

> одновременно</i>

[censored] всё разжевывать. LamerOk (ник как бы намекает) наверное будет удивлен, но массив из union может хранить любые элементы указанные в типе union. Для определения типа делаем либо структуру с union и идентификатором типа. Либо информацию о типе данных передаём как-то ещё, можно это сделать как в printf через строку.

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

Как я уже говорил универсального варианта не существует. Каждый способ решения этой задачи имеет свои серьезные недостатки.

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

либо структуру с union и идентификатором типа. Либо информацию о типе данных передаём как-то ещё

иными словами, ты предлагаешь два костыля, ограниченно работающих с типами данных C++, и ещё удивлешься - чем же union не кошерен?

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

либо небезопасно, либо жутко тормозззно, либо недостаточно гибко. Чем-то прийдется пожертвовать.

и как именно будет в случае использования вариадических шаблонов? небезопасно? тормозно? негибко?

jtootf ★★★★★
()

Необходимость использования функций с переменным числом аргументов
в C++ говорит о проблемах с дизайном.

blackice
()
Ответ на: комментарий от jtootf

> и как именно будет в случае использования вариадических шаблонов? небезопасно? тормозно? негибко?

Учитывая, что без рекурсии обойти по порядку все параметры вариадического шаблона будет проблематично, можно сказать, что «негибко».

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

Учитывая, что без рекурсии обойти по порядку все параметры вариадического шаблона будет проблематично, можно сказать, что «негибко».

а как рекурсия ухудшает эту самую гибкость? на каком случае, скажем так, этот подход перестаёт работать?

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

> а как рекурсия ухудшает эту самую гибкость? на каком случае, скажем так, этот подход перестаёт работать?

Если говорить все-таки о гибкости, а не о строгой применимости, то очевидно, что проблемы с реализацией будут у функций, семантика параметров которых сильно зависит от их порядка в последовательности относительно других параметров.

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

очевидно, что проблемы с реализацией будут у функций, семантика параметров которых сильно зависит от их порядка в последовательности относительно других параметров

мне неочевидно. поясни, откуда возникнут проблемы с реализацией

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

Хорошо, простейший случай: хочу написать функцию, которая для каждого переданного ей объекта выведет на консоль результат некой выполненной над ним операции. При этом результат этой операции зависит от типа аргумента, будь то int, double или MyObject и общего кол-ва аргументов. Решая подобную задачу с помощью вариадических шаблонов, придется опять же прибегнуть к временному хранилищу, в худшем случае - к пресловутому std::list<boost::any>.

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

функцию, которая для каждого переданного ей объекта выведет на консоль результат некой выполненной над ним операции

template<typename T>
void print_comma_separated_list(T value)
{
    std::cout<<value<<std::endl;
}

template<typename First,typename ... Rest>
void print_comma_separated_list(First first,Rest ... rest)
{
    std::cout<<first<<",";
    print_comma_separated_list(rest...);
}

первый шаблон можно специализировать по T, и задать какую-то особую обработку перед выводом для конкретных типов данных. в чём подвох?

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

первый шаблон можно специализировать по T

оба шаблона, конечно же

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

Это понятно. Подвох в том, что общее кол-во этих аргументов, участвующее по условию в операции над ними, ты не узнаешь пока не обойдешь все элементы.

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

общее кол-во этих аргументов, участвующее по условию в операции над ними, ты не узнаешь пока не обойдешь все элементы

имеется в виду вот это:

template <typename T, typename... Args>
struct count
{
  static const int value = 1 + count<Args...>::value;
};
const int args_size = count<int, char, bool, std::string>::value; // args_size == 4

или я, опять же, чего-то не понимаю? ну то есть да, для подсчёта количества элементов надо рекурсивно их обойти, но что в этом такого страшного? O(n), можно вызвать подсчёт элементов перед вызовом обработчика (если в этом действительно есть необходимость)

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

Таким образом, остаются только проблемы с реализацией функций, результат которых зависит от положения аргументов относительно друг-друга. С другой стороны, если бы возможность произвольного доступа к параметрам вариадического шаблона была бы доступна, то и без того несладкий синтаксис шаблонов усложнился бы кардинально.

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

остаются только проблемы с реализацией функций, результат которых зависит от положения аргументов относительно друг-друга

результат выполнения printf, очевидно, зависит от порядка следования аргументов. проблем с реализацией на вариадических шаблонах я не заметил

но вообще было бы интересно посомтреть на примеры таких функций - хотя бы парочку. есть у меня подозрение, что это слишком частная задача, чтобы её в принципе имело смысл решать на уровне базовых конструкций языка

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

Примером такой функции, например, является любая функция для которой нужна специализация по типу последнего аргумента. Не то что бы важная возможность, но и не «слишком частная задача».

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

функция для которой нужна специализация по типу последнего аргумента

почему этот аргумент нельзя поставить первым/перед typename...?

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

почему этот аргумент нельзя поставить первым/перед typename...?

Можно. Но ты не находишь, что отсутствие возможности поставить этот аргумент после typename... - это проявление «негибкости»?

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

#include <list>
#include <boost/any.hpp>

typedef std::list<boost::any> AnyList;

template<typename T>
void helper (AnyList &list, const T &last)
{
    // generic...
}

template<>
void helper<> (AnyList &list, const long long &last)
{
    // specialized...
}

template<typename T, typename... Rest>
void helper (AnyList &list, const T &next, Rest... rest)
{
    list.push_back(next);
    helper(list, rest...);
}

template<typename... Args>
void function (Args... args)
{
    AnyList list;
    helper(list, args...);
}

int main (int argc, char* argv[])
{
    function(1, "hello", 2.2, 333LL);
}

Что-то подобное придется делать, если взаимосвязь между взаимным положением аргументов функции и операциями над ними сложнее, чем у printf.

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

Интересно, у одного меня SyntaxHighlighter выдает какую-то вырвиглазную хрень?

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

Но ты не находишь, что отсутствие возможности поставить этот аргумент после typename... - это проявление «негибкости»?

нет. было бы в C++ каррирование - я бы ещё понял, но так - нет. аналогично можно было бы сетовать на то, что аргументы со значениями по умолчанию также должны распологаться строго в конце списка аргументов

Что-то подобное придется делать

только в случае специальной обработки последнего элемента; но даже в этом случае - что тут настолько страшного? хотя применительно к данной задаче я не понимаю, зачем вообще может понадобится специализировать helper по последнему аргументу. в чём профит?

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

> только в случае специальной обработки последнего элемента

В случаях, когда для обработки аргумента понадобятся значения аргументов, следующих до или после текущего. Иными словами, ограничения рекурсивного обхода параметров аналогичны ограничениям строго последовательного доступа в сравнении с произвольным.

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


Меня, в принципе, все устраивает: уже сейчас можно делать гораздо более интересные и практичные API. «Негибкость» в данном случае - это так... демагогия :)

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

В случаях, когда для обработки аргумента понадобятся значения аргументов, следующих до или после текущего

ты примеров функций так и не привёл, кстати

уже сейчас можно делать гораздо более интересные и практичные API

boost, массово переписанный на вариадические шаблоны, должен уменьшиться в размерах разов в пять-десять

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

> ты примеров функций так и не привёл, кстати

Oh shi~, а пример со специализацией по последнему аргументу не прокатил что ли? Вполне себе зависимость обработки аргументов от значения последнего/предпоследнего/следующего-через-3-после-текущего.

разов в пять-десять


Ну это, конечно, очень смелая оценка. Мне в целом импонирует то, что c++0x позволит существенно сократить использование препроцессора в больших проектах.

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

Oh shi~, а пример со специализацией по последнему аргументу не прокатил что ли?

это был общий пример конструкции; я хочу пример её применения - ситуации, когда без такого не обойтись

Ну это, конечно, очень смелая оценка

про нутро boost.lambda в этом смысле готов спорить на ящик пива :)

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

> я хочу пример её применения - ситуации, когда без такого не обойтись

Хм, ты требуешь невозможного. Любое ограничение C++, включая ограничения 0x, можно обойти множеством способов. В реальных условиях я не стал бы использовать подход, аналогичный приведенному выше. Это искусственная ситуация, случай, когда необходимость рефакторинга очевидна в момент написания кода.

Таким образом, предлагаю подождать, пока под c++0x со всеми его вариадическими шаблонами начнут перепиливать boost, чтобы посмотреть, какие костыли и для чего там придумают на этот раз :)

mannaz
()
Ответ на: комментарий от jtootf

а в какой контейнер можно положить одновременно int, double, void *, и MyObject

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

вот пример статического списка без виртуальных функций

/// g++ -Wall multi-arg3.cxx && ./a.out
/// выхлоп:
/// int=2 char*=asdf double=1.5 char*=qqq


template<class L, class R>
struct Pair
{
  Pair(L l, R r): l(l),r(r) { }
  L l;
  R r;
  template<class RR> Pair<const Pair<const L&,const R&>&, const RR&> operator, (const RR& r) const
  {
    return pair(*this,r);
  }
};
template<class L, class R>
Pair<const L&, const R&> pair(const L& l, const R& r) { return Pair<const L&,const R&>(l,r); }

struct Nothing
{
  template<class R>  Pair<const Nothing&, const R&> operator, (const R& r) const /// <!!!> без const не пашет
  {
    return pair(*this,r);
  }
};
static const Nothing& NOTHING = Nothing();

template<class L, class R>
void do_something_with_all(const Pair<const L&, const R&>& arg)
{
  do_something_with(arg.l);
  do_something_with(arg.r);
}
template<class L, class LL, class R>
void do_something_with_all(const Pair<const Pair<const LL&, const L&>&, const R&> arg)
{
  do_something_with_all(arg.l);
  do_something_with    (arg.r);
}

/// /////////////////////////////////////////////////////////////////////////////// usage:

#include <iostream>

void do_something_with(const Nothing&) { }
void do_something_with(const int i)    { std::cout << " int=" << i; }
void do_something_with(const char* s)  { std::cout << " char*=" << s; }
void do_something_with(const double x) { std::cout << " double=" << x; }

int main()
{
  do_something_with_all(( NOTHING, 2, "asdf", 1.5, "qqq" ));
  std::cout << '\n';
  return 0;
}
www_linux_org_ru ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.