Разное количество аргументов функции в C/C++

lev-rechin

есть функция (ну пусть для примера среднее которая вызывается с разным числом параметров, как их получать я знаю, вопрос в том как определить внутри функции сколько аргументов она получила?
avg(int num_of_args, ...) не катит, чтобы не было случаев avg(9999).
делать последний аргумент каким-то magic_number'ом - тоже нельзя.
для чего нужно va_end (в msdn'е написано не внятно)

k_a_m

>в msdn'е написано не внятно
А где тогда внятно? "C/C++ Illustrated"?

lev-rechin

C/C++ Illustrated

vln2

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

spiritmc

Ответ: никак.
Только передав явно или неявно, как в printf, самому.
Посмотри, как сделан va_end в заголовочнике.
Почти наверняка, это макрос.
---
...Я работаю антинаучным аферистом...

komarov

Стандартный путь определения количества переданных аргументов описан в MSDN. Используем макросы va_start, va_arg и va_end. Дальше цитата

int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
{
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}


То есть надо пробежаться по списку аргументов, инкрементируя счетчик, пока va_arg не вернёт -1. Я так понял Вам не понравилось, что нет способа получить число аргументов одним вызовом? Ну и пускай нет. Всё равно, как правило, переданные аргументы нужны. Вот и тащите их в цикле, как выше, подсчитывая, сколько вытащили.
Для чего нужен макрос va_end. В MSDN написано:
After all arguments have been retrieved, va_end resets the pointer to NULL.
. То есть это делается только для того, чтобы показать, что все, мы закончили работать со списком аргументов. Если Вы потом, "ниже по течению" кода, забудете про это и попытаетесь чего-то делать с va_list, программа Вам тут же освежит память. То есть va_end можно в конце работы со списком параметров не вызывать. Будет работать. Можно залезть в файл VC98\CRT\SRC\stdarg.h и посмотреть, как определены макросы va_start, va_arg и va_end для разных платформ. Например для платформы intel x86 будет:


#ifdef _M_IX86
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *ap += _INTSIZEOF(t - _INTSIZEOF(t )
#define va_end(ap) ( ap = (va_list)0 ) /* <= вот он, красавчик, ничего особенного не делает */

ocean

> надо пробежаться по списку аргументов, инкрементируя счетчик, пока va_arg не вернёт -1
> #define va_arg(ap,t) ( *(t *ap += _INTSIZEOF(t - _INTSIZEOF(t )
чё-та не вижу связи
1) сомневаюсь, что компилер кладёт (int)-1 в конец списка аргументов
2) как быть, если мне нужно -1 передать?

antcatt77

> То есть надо пробежаться по списку аргументов, инкрементируя счетчик, пока va_arg не вернёт -1
Это уже домыслы, -1 будет только для данного примера. В других случаях будет что-то свое.

antcatt77

-1 это только для приведенного примера.
В приведенном примере, функция average всегда должна вызыватся следующим образом:
average (10, 20, 3, 465, -1) , т.е. последовательность чисел всегда должна заканчиватся -1.

nik66104248

Дааа." Поспешишь, людей насмешишь" (С). Неправду сказал. Облажался. Стыжусь. Полностью согласен с и DarkGrey.

komarov

Предыдущий пост от меня.

lev-rechin

Стандартный путь определения количества переданных аргументов описан в MSDN

я же говорил что magic number (-1 в данном примере) не катит

lev-rechin

Только передав явно или неявно, как в printf, самому.

пусть первый аргумент - число параметров, и я вызову avg(9999). все пройдет нормально или, будет access violation (или как там его)?

vln2

В общем случае - нет. Если аргументов не слишком много, иначе ты дойдешь до начала стека и тогда с высокой вероятностью будет ошибка. Неужели ты никогда не путал у printf'а количество аргументов?

spiritmc

/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\
/* stdarg.h
Definitions for accessing parameters in functions that accept
a variable number of arguments.
Copyright (c) Borland International 1987,1988
All Rights Reserved.
*/
#if __STDC__
#define _Cdecl
#else
#define _Cdecl cdecl
#endif
#if !defined(__STDARG)
#define __STDARG
typedef void *va_list;
#define va_start(ap, parmN) (ap = ...)
#define va_arg(ap, type) (*type *ap++)
#define va_end(ap)
#define _va_ptr (...)
#endif
/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\
Отсюда видно, как осуществлять доступ к параметрам.
Примерно то же, но чуть другое в GNU LibC.
Различия не помню, но помню, что есть и очень маленькое (латал гнутые исходники).
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."
Karel Capek

lev-rechin

>#define va_arg(ap, type) (*type *ap++)
вот это меня и пугает

spiritmc

А ты не бойся.
Гнусь выглядит СТРАШНЕЕ! Ж: ( )
---
...Если ты не параноик, это ещё не значит, что за тобой не следят...

aqvamen

зачем это может быть нужно, коль уж ты слово С++ пишешь? наверняка есть более "правильный" способ, а то и не один

kliM

наверняка... например, перегрузка запятой... что-то типа такого:
Average<int> a;
a=1, 2, 3, 10, 20;
cout << a << endl;

FieryRush

Запятую нельзя перегружать
Но можно сделать типа
List<int> nill;
f(nill << 10 << 20 << 30 << 40);
Только зачем так извращаться?

svistunov

>Запятую нельзя перегружать
"А у тебя мозгов нет самому в стандарте прочитать? Тогда просвещайся" (с) гыгыгы
Поботай п. 13.5
Вам устное замечание. Личные оскорбления посредством цитирования

aqvamen

>Запятую нельзя перегружать
>
>Но можно сделать типа
А потом кричат какое дерьмо плюсы и сколько в них недостатков...
>Только зачем так извращаться?
точно, так не стоит...

FieryRush

А кто кричит? Впрочем, это разговоры в пользу бедных; партия (Микрософт) уже все решила и обсуждать собственно нечего.

kliM

для тех кто не читал стандарт и имеет кривые руки, может проверить такой примерчик:


#include <iostream>
using namespace std;
template<class T>
class Average
{
typedef Average<T> A;
int n;
T sum;
public:
Average:n(0 sum{}
A& operator=(const T& v)
{
n=1;
sum=v;
return *this;
}
A& operator, (const T& v)
{
n++;
sum+=v;
return *this;
}
operator T
{
return sum/n;
}
};
void main
{
Average<float> a;
a=1, 2, 3, 10, 20;
cout << a << endl;
}


kliM

или лучше так (поэффектнее):


#include <iostream>
using namespace std;
template<class T>
class Average
{
typedef Average<T> A;
int n;
T sum;
public:
Average(const T& v):n(1 sum(v){}
A& operator, (const T& v)
{
n++;
sum+=v;
return *this;
}
operator T
{
return sum/n;
}
};
class StartAveraging
{
public:
template<class T>
Average<T> operator=(const T& v)
{
return Average<T>(v);
}
} mean_value_of;
void main
{
cout << (mean_value_of=1.0, 2, 3, 10, 20) << endl;
cin.get;
}

Оставить комментарий
Имя или ник:
Комментарий: