С функция с неопределенным числом параметров
Функции с переменным числом параметров
Функции с переменным числом параметров
К ак уже обсуждалось ранее, по умолчанию параметры передаются функции через стек. Поэтому, технически, нет ограничения на количество передаваемых параметров – “запихать” можно сколько угодно. Проблема в том, как потом функция будет разбирать переданные параметры. Функции с переменным числом параметров объявляются как обычные функции, но вместо недостающих аргументов ставится многоточие. Пусть мы хотим сделать функцию, которая складывает переданные ей числа, чисел может быть произвольное количество. Необходимо каким-то образом передать функции число параметров. Во-первых, можно явно передать число параметров обязательным аргументом. Во-вторых, последний аргумент может иметь некоторое «терминальное» значение, наткнувшись на которое функция закончит выполнение.
Общий принцип работы следующий: внутри функции берём указатель на аргумент, далее двигаемся к следующему аргументу, увеличивая значение указателя.
OLD SCHOOL
Д елаем всё вручную. Функция, которая складывает переданные ей аргументы
Первый параметр – число аргументов. Это обязательный параметр. Второй аргумент – это первое переданное число, это тоже обязательный параметр. Получаем указатель на первое число
Далее считываем все числа и складываем их. В этой функции мы также при сложении проверяем на переполнение типа unsigned.
Можно сделать первый аргумент необязательным и «перешагнуть» аргумент unsigned char num, но тогда возникнет большая проблема: аргументы располагаются друг за другом, но не факт, что непрерывно. Например, в нашем случае первый аргумент будет сдвинут не на один байт, а на 4 относительно num. Это сделано для повышения производительности. На другой платформе или с другим компилятором, или с другими настройками компилятора могут быть другие результаты.
Поэтому лучше число параметров, если это аргумент, сделать типом int или unsigned int.
Можно сделать по-другому: в качестве «терминального» элемента передавать ноль и считать, что если мы встретили ноль, то больше аргументов нет. Пример
Но теперь уже передавать нули в качестве аргументов нельзя. Здесь также есть один обязательный аргумент – первое переданное число. Если его не передавать, то мы не сможем найти адрес, по которому размещаются переменные в стеке. Некоторые компиляторы (Borland Turbo C) позволяют получить указатель на …, но такое поведение не является стандартным и его нужно избегать.
VA_ARG
М ожно воспользоваться макросом va_arg библиотеки stdarg.h. Он делает практически то же самое, что и мы: получает указатель на первый аргумент а затем двигается по стеку. Пример, та же функция, только с va_arg
Первый аргумент – число параметров – также лучше делать типа int, иначе получим проблему со сдвигом, кратным 4.
Название | Описание |
---|---|
va_list | Тип, который используется для извлечения дополнительных параметров функции с переменным числом параметров |
void va_start(va_list ap, paramN) | Макрос инициализирует ap для извлечения дополнительных аргументов, которые идут после переменной paramN. Параметр не должен быть объявлена как register, не может иметь типа массива или указателя на функцию. |
void va_end(va_list ap) | Макрос необходим для нормального завершения работы функции, работает в паре с макросом va_start. |
void va_copy(va_list dest, va_list src) | Макрос копирует src в dest. Поддерживается начиная со стандарта C++11 |
Неправильное использование
Если передано больше аргументов, то функция выведет только те, которые ожидала встретить
Так как очистку стека производит вызывающая функция, то стек не будет повреждён. Получается, что если изменить схему вызова и сделать так, чтобы вызываемый объект сам чистил стек после себя, то в случае неправильного количества аргументов стек будет повреждён. То есть, буде функция объявлена как __stdcall, в целях безопасности она не может иметь переменного числа аргументов.
Однако, если добавить спецификатор __stdcall к нашей функции summ она будет компилироваться. Это связано с тем, что компилятор автоматически заменит __stdcall на __cdecl.
Программа завершится с ошибкой вроде The value of ESP was not properly saved across a function call.
Нужно разобраться с функциями с неопределенным числом параматеров
Здравствуйте, нужно написать функцию, которая принимает неопределенное количество параматров и выводит все параметры, которые получает. Есть такой вариант:
Функция с неопределенным числом параметров?
Коллеги, пытаюсь сделать отображение графиков и функций. С рисованием проблем нет вообще. Но.
Организовать цикл с неопределенным числом итераций
Здравствуйте, есть лист со списком имя(несколько раз подряд) и число в след стобце(неск раз подряд).
Функции с неопределенным числом фактических параметров
создать и отладить программу, которая осуществляет обработку элементов последовательности, вызывая.
Решение
Qwertyeerty, va_list, va_start, va_arg, va_end
Добавлено через 2 минуты
Qwertyeerty, только не получится узнать типы аргументов. Инфу о типах нужно передать ещё каким-то способом
Добавлено через 58 секунд
Qwertyeerty, лучше применить variadic template
Добавлено через 3 часа 56 минут
Получилось сделать вывод любого количества параметров одного (любого) типа с помощью шаблонов
#include
#include /* va_list, va_start, va_arg, va_end */
template
void print(int n, T s. )
<
int i;
T val;
std::cout 0
не-не, ты шаблон использовал чисто для указания одного единственного типа, в остальном это всё та же Си-лапша.
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Вводится строка из нескольких слов и чисел,разделенных неопределенным числом пробелов
На форме 1 кнопка,1 надпись,1 едит и 1 мемо.Вводится строка из нескольких слов и чисел,разделенных.
Визуальная среда. Вводится строка из семи слов,разделенных неопределенным числом пробелов
Может кто помочь решить задачку в визуальной среде на Паскале 🙂 не могу в ней разобраться никак.
Процедура с неопределённым числом параметров: можно ли использовать для чисел, введённых с клавиатуры?
Процедура с неопределённым числом параметров, можно ли использовать для чисел, введённых с.
Функция с неопределенным количеством параметров (без использования stdarg.h)
Мне нужно создать функцию, которая считает выражение:
4 ответа 4
Как я уже сказал выше в комментариях под вопросом, написать функцию с переменным количеством аргументов без использования средств, предоставляемых заголовочным файлом переносимым способом невозможно. Имейте это в виду, и, если нужно, покажите этот ответ своему преподователю.
А теперь смотрите, почему это не возможно с теоретической и почти всегда невозможно c практической сторон.
Теоретическая сторона
С теоретической стороны это невозможно, т. к. этого не позволяет стандарт языка C. В стандарте сказано, что размещение параметров при вызове функции неспецифицированно:
9 Each parameter has automatic storage duration; its identifier is an lvalue. 166) The layout of the storage for parameters is unspecified.
— ISO/IEC 9899:2017 N2176 §6.9.1
Это значит, что способ передачи аргументов в функцию полностью зависит от реализации и делать какие-либо предположения о том, как параметры передаются в функцию — бессмысленно.
Практическая сторона
Теперь рассмотрим практическую сторону. Как я уже сказал, способ передачи аргументов в функцию зависит от реализации. Проблема в том, что реализаций может быть много. Очень много. Под реализацией понимается, как минимум:
Количество всех возможных комбинаций очень велико. Причем для каждой такой комбинации код, в общем случае, будет разным. Придется учитывать особенности каждого ABI, и не факт, что ABI позволит получить значения аргументов таким «грязным» способом. Вынужден признать, что в некоторых обстоятельствах мы можем решить задачу, хотя говорить о корректности решения в таких случаях не приходится.
Конкретные примеры
Допустим, что мы заранее знаем, каким компилятором, под какую архитектуру и с каким ABI будет компилироваться программа. Например, это будет GCC, Intel 64 2 и System V ABI (x86_64 psABI).
Напишем код, который вызывал бы нужную нам функцию:
И проверим нужные смещения отладчиком:
Отлично! Теперь можем написать реализацию функцию sum :
1) За подробностями обращайтесь к разделу «5.1.2 Execution Environement» стандарта C17.
2) Эту ISA часто называют x64, x86_64 или x86-64. Скорее всего, процессор вашего компьютера реализует именно эту архитектуру.
3) Эту ISA также называют x32, i386 или Intel-386.
Мой подход к реализации делегатов в C++: вызов функции с неизвестными параметрами во время выполнения
Предыстория
Конечно, полная имитация делегатов слишком сложна, поэтому в этой статье будут продемонстрированы лишь общая архитектура библиотеки и решение некоторых важных проблем, возникающих, когда имеешь дело с тем, что не поддерживается языком напрямую.
Вызов функций с неопределённым количеством параметров и неизвестными во время компиляции типами
Конечно, это главная проблема с C++, которая решается не так уж и просто. Конечно, в C++ есть средство, унаследованное из C – varargs, и, скорее всего, это первое, что придёт на ум… Однако они не подходят, во-первых, из-за своей типонебезопасной природы (как и многие вещи из C), во-вторых, при использовании таких аргументов надо точно заранее знать, какие у аргументов типы. Впрочем, почти наверняка, это ещё не все проблемы с varargs. В общем, это средство нам здесь не помощник.
А теперь перечислю средства, которые помогли мне решить эту проблему.
std::any
Начиная с C++17, в языке появился замечательный-контейнер-хранилище для чего угодно – некое отдалённое подобие System.Object в CLI – это std::any. Этот контейнер действительно может хранить что угодно, да ещё как: эффективно! – стандарт рекомендует маленькие объекты хранить непосредственно в нём, большие уже можно хранить в динамической памяти (хотя такое поведение не является обязательным, корпорация Microsoft в своей реализации C++ так и сделала, что не может не радовать). А подобием лишь его можно назвать потому, что System.Object участвует в отношениях наследования («is a»), а std::any – участвует в отношениях принадлежности («has a»). Кроме данных, контейнер содержит указатель на объект std::type_info – RTTI о типе, объект которого «лежит» в контейнере.
Для контейнера выделен целый заголовочный файл .
Чтобы «вытащить» объект из контейнера, нужно использовать шаблонную функцию std::any_cast(), которая возвращает ссылку на объект.
Пример использования:
Если запрашиваемый тип не совпадает с тем, что имеет объект внутри контейнера, тогда выбрасывается исключение std::bad_any_cast.
Кроме классов std::any, std::bad_any_cast и функции std::any_cast, в заголовочном файле есть шаблонная функция std::make_any, аналогичная std::make_shared, std::make_pair и другим функциям этого рода.
Безусловно, практически нереально в C++ было бы реализовать динамический вызов функций без информации о типах во времени выполнения. Ведь надо же как-то проверять, правильные типы переданы, или нет.
Примитивная поддержка RTTI в C++ есть довольно давно. Вот только в том-то и дело, что примитивная – мы мало что можем узнать о типе, разве только декорированное и недекорированное имена. Кроме того, мы можем сравнивать типы друг с другом.
Шаблоны
Ещё одна чрезвычайно важная особенность языка, нужная нам для реализации нашей задумки – это шаблоны. Это средство – довольно мощное и исключительно непростое, по сути позволяет генерировать код во время компиляции.
Шаблоны – это очень обширная тема, и в рамках статьи раскрыть её не удастся, да и не нужно это. Будем считать, что читатель понимает, о чём речь. Какие-то неясные моменты будут раскрыты в процессе.
Упаковка аргументов с последующим вызовом
Итак, у нас есть некая функция, принимающая на вход несколько параметров.
Продемонстрирую набросок кода, который объяснит мои намерения.
Возможно, вы спросите – как это возможно? Название класса Variadic_args_binder подсказывает, что объект связывает функцию и аргументы, которые нужно ей передать при вызове. Таким образом, остаётся лишь вызвать этот связыватель как функцию без параметров!
Так это выглядит снаружи.
Если сразу же, не задумавшись, сделать предположение, как это можно реализовать, то на ум, возможно, придёт написать несколько специализаций Variadic_args_binder для разного количества параметров. Однако это невозможно в случае необходимости поддержки неограниченного числа параметров. И проблема вот ещё в чём: аргументы, к сожалению, нужно подставить в вызов функции статически, то есть в конечном итоге для компилятора код вызова должен свестись вот к такому:
Так устроен C++. И это всё сильно усложняет.
Тут справится лишь шаблонная магия!
Основная идея – создавать рекурсивные типы, хранящие на каждом уровне вложенности один из аргументов или функцию.
Итак, объявим класс _Tagged_args_binder:
Чтобы удобно «переносить» пакеты типов, создадим вспомогательный тип Type_pack_tag (зачем это понадобилось, скоро станет понятно):
Теперь создаём специализации класса _Tagged_args_binder.
Начальные специализации
Как известно, чтобы рекурсия не была бесконечной, необходимо определить граничные случаи.
Следующие специализации являются начальными. Для упрощения приведу специализации лишь для нессылочных типов и правосторонних ссылочных типов (rvalue reference).
Специализация для непосредственно параметров-значений:
Здесь хранятся первый аргумент вызова ap_arg и остальная часть рекурсивного объекта ap_caller_part. Обратите внимание, что тип T1 «переместился» из первого пакета типов в этом объекте во второй в «хвосте» рекурсивного объекта.
Специализация для rvalue-ссылок:
Шаблонные «правосторонние» ссылки – на самом деле не являются правосторонними значениями. Это так называемые «универсальные ссылки», которые, в зависимости от типа T1, становятся то T1&, то T1&&. Поэтому приходится использовать обходные пути: во-первых, так как определены специализации для обеих видов ссылок (не совсем корректно сказано, по уже озвученной причине) и для нессылочных параметров, при инстанцировании шаблона будет выбрана именно нужная специализация, даже если это правосторонняя ссылка; во-вторых – для передачи типа T1 из пакета в пакет используется исправленная версия move_ref_T1, превращённая в настоящую rvalue-ссылку.
Специализация с обычной ссылкой делается аналогично, с необходимыми исправлениями.
Конечная специализация
Эта специализация ответственна за хранение функционального объекта и, по сути, является обёрткой над ним. Она является завершающей рекурсивный тип.
Обратите внимание, как используется здесь Type_pack_tag. Все типы параметров теперь собраны в левом пакете. Это значит, что все они обработаны и упакованы.
Теперь, думаю, становится понятно, зачем нужно было использовать именно Type_pack_tag. Дело в том, язык не позволил бы использовать рядом два пакета типов, например, вот так:
поэтому приходится разделять их на два раздельных пакета внутри двух типов. Кроме того, надо как-то отделять обработанные типы от ещё не обработанных.
Промежуточные специализации
Из промежуточных специализаций напоследок приведу специализацию, опять-таки, для типов-значений, остальное по аналогии:
Эта специализация предназначена для упаковки любого аргумента, кроме первого.
Класс-связыватель
Класс _Tagged_args_binder не предназначен для непосредственного использования, что я хотел подчеркнуть одинарным подчёркиванием в начале его названия. Поэтому приведу код небольшого класса, являющегося своего рода «интерфейсом» к этому некрасивому и неудобному в использовании типу (в котором, однако, используются довольно необычные приёмы C++, что придаёт ему некоторый шарм, на мой взгляд):
Соглашение unihold – передача ссылок внутри std::any
Внимательный читатель наверняка заметил, что в коде используется функция unihold::reference_any_cast(). Эта функция, а также её аналог unihold::pointer_any_cast(), разработаны для реализации соглашения библиотеки: аргументы, которые необходимо передать по ссылке, передаются по указателю в std::any.
Функция reference_any_cast всегда возвращает ссылку на объект, хранится ли в контейнере сам объект или только указатель на него. Если std::any содержит в себе объект, то возвращается ссылка на этот объект внутри контейнера; если же содержит указатель – то возвращается ссылка на объект, на который указывает указатель.
Для каждой из функций есть варианты константного std::any и перегруженные версии для определения того, является ли контейнер std::any владельцем объекта или же содержит лишь указатель.
Функции нужно специализировать явно типом хранимого объекта, так же, как преобразования типов C++ и подобные им шаблонные функции.
Заключение
Я постарался кратко описать один из возможных подходов к решению проблемы динамического вызова функций на C++. Впоследствии это ляжет в основу библиотеки делегатов на C++ (на самом деле, я уже написал основной функционал библиотеки, а именно, полиморфные делегаты, но библиотеку ещё нужно переписать, как подобает, чтобы можно было демонстрировать код, и добавить некоторый нереализованный функционал). В ближайшем будущем я планирую закончить работу над библиотекой и рассказать, как именно я реализовал остальной функционал делегатов на C++.
Как передать в функцию заранее неизвестное число параметров?
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Как принять заранее неизвестное количество аргументов и передать их в конструктор?
Допустим есть функция которая создает объект template T* createObject(typename.
Вставить в пользовательскую форму VBA неизвестное заранее число Checkbox
Добрый, день всем! В автоматом предложенных темах не нашел точного решения того, что мне нужно.
однотипных, все равно в стеке все 32битное.
Добавлено через 28 секунд
и еще вопрос почему так нельзя
Нет не получиться, передасть ссылка на массив и все
push array
call 0x12345678
а надо
push 1
push 2
push 3
.
push n
call 0x12345678
как мне вызвать функцию ф из моей функции с теми же параметрами что была вызвана моя функция?
даже точнее будет я буду считывать параметры из памяти, где их будет хз сколько, а потом передавать в F
Добавлено через 5 минут
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Как передать в функцию указатель на двумерный массив заранее неизвестного размера?
Ребята, скажите пожалуйста, как реализовать такую идею? Необходимо передать в функцию двумерный.
функция на неизвестное число параметров
Здравствуйте, требуется создать функцию void f(), которая будет принимать от 0 до n параметров типа.
Как передать несколько параметров в функцию
Как передать несколько параметров в функцию? пытаюсь передать переменную s она объявлена глобально.
Как создать неизвестное заранее количество обьектов?
Здравствуйте! Может для кого-то глупый вопрос, но я не могу найти решение. Допустим я создаю.