LINUX.ORG.RU

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

 , , чистый код


0

5

Серьезно. В той же OpenCV ехал выходной параметр через выходной параметр. В итоге: непонятно, что же именно функция возвращает, надо объявлять все «возвращаемые значения» заранее. Есть риск при этом ошибиться с типом, ибо функция при компиляции может молча проглотить предполагаемый тип, а уже на этапе выполнения сказать «Нет, хозяин, не могу». Впрочем, такой подход еще много где видел. Зачем и почему?

Ответ на: комментарий от Ygor

Точно, у меня даже справка есть.

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

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

В теме обсуждается возврат нескольких значений в плюсах по ссылкам и тут у штатного клоуна @FishHook возникает такой риторический вопрос… чо то ржу.

Возврат кортежа можно трактовать как возврат нескольких значений, это бессмысленный терминологический спор. Вопрос не в том когда надо несколько значений возвращать, вопрос в том как это хорошо делать;-)

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

вот ты и скатился до оскорблений, а такого интеллего строил из себя.

Возврат кортежа можно трактовать как возврат нескольких значений

трактовать можно все что угодно и как угодно, это от фантазии зависит.

Вот я такой код написал


def foo() -> tuple[int, int]:
    return 1, 1

профессор академических наук трактует(!!) - функция возвращает два значения. Ну хорошо, уважаемый академик, а мы сделаем вот так

f = foo()

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

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

ну не нано тут «непойнемат»

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

в питоне не вектор даж а достаточно(>80%) итератора

вот будь хасскель сразу с удобствами питона - все бы(остальным бы нашлось чем заняться) щаб хаскеляли

вот заживём!

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

так сразу фортран тада на входе всё состояние машины

на выходе всё состояние машины

чё мелочится

форт хорош тем что стек возвратов отдельно поэтому можно флексовать свои управляющие структуры

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

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

Схерали, пардонте?! Возврат нескольких значений из функции это стандартная ситуация, а вариант возврата только по ссылкам/одной структурой просто костыль. Не надо оправдывать слоупочность разработчиков новых фич в плюсах некой гипотетической заботой о пользователях и чистоте кода которые эти пользователи пишут.

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

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

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

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

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

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

Профессор зельеваренья не утруждает себя практическими вопросами, он трактует. У квадратного уравнения два корня? Ну и все, идите в жопу, значит и значения возвращается два, я натрактовал. А будешь спорить, зачет не поставлю.

FishHook
()
Последнее исправление: FishHook (всего исправлений: 1)
Ответ на: комментарий от FishHook

мне интересно, а в каких ЯП из функции можно вернуть более одного значения?

Golang. Но там эти ребята придумали, что исключения не нужны, а ошибку нужно возвращать отдельным значением (в итоге они переизобрели исключения, но это уже другая история…).

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

Кутешный QString::toInt() с параметром bool* посмотри, например. Это выходной по сути параметр, при том, что у функции и результат есть, который число возвращает.

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

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

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

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

возврат результата из функции бьет все прочие способы только в случае ВОЗВРАТА ЧЕРЕЗ РЕГИСТРЫ!!!. это в принципе возможно даже если возвращать туплы, но тут вопрос к оптимизации кода. но если результаты не лезут в свободные регистры - то будет возврат через стек, и никакие мувы не спасут. они лишь несколько сократят лишние потери.

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от vromanov

Также прототип покажет IDE. Название типов параметров типа InputArray или OutputArray ничего не намекает?

Намекает. На то, что это какой-то входной и какой-то выходной массивы. А в реальности типы надо подставлять совсем другие типы, как я написал выше.

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

А в реальности типы надо подставлять совсем другие типы, как я написал выше.

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

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

Кутешный QString::toInt() с параметром bool* посмотри, например. Это выходной по сути параметр, при том, что у функции и результат есть, который число возвращает.

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

Невозможность выполнить функцией прямую задачу (в данном случае конверсию в число) точно является исключительной ситуацией, то есть должна триггерить exceptional flow; ошибка это или нет - это вопрос больше логики конкретной программы, чем синтаксиса языка. В данном случае это точно должно быть исключением.

P. S. На самом деле есть один сценарий, где «не всё так однозначно» (С) - warning. Например, QString::toInt() могло бы быть реализовать так, чтобы, например, «64kb» превращалось бы в 64, но дополнительно возвращался бы warning о том, что на самом деле в параметре было не только число; далее вышестоящая логика могла бы принять решение, это ожидаемо или нет. То есть нужно возвращать и результат, и warning - два значения. Типового паттерна я на это не знаю; в Python есть отдельные Warning исключения, но как по мне это неудобно. В своих имплементациях я в функции добавляю параметры вида allow*. Например, в случае QString::toInt() можно было бы передать allowNonStrictConversion, и если он false (то есть с точки зрения логики не ожидается что в параметре не что-то кроме числа), то оно бы бросало exception, а если true, то нет. То есть это ветвление передаётся в функцию. Это делает код более читаемым.

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от Kroz

Например, QString::toInt() могло бы быть реализовать так, чтобы, например, «64kb» превращалось бы в 64, но дополнительно возвращался бы warning о том, что на самом деле в параметре было не только число

не надо этих нечетких логик. а kb64 - это число? а x00x - число? возникнет лишний набор правил, совершенно ненужных.

есть число и нечисло. а недочисел, получисел и проч. быть не должно.

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

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

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

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

Очевидно я знаю как писать HPC коды лучше, чем любой идеальный сферический программист в вакууме который их не писал, как бы идеален он ни был. Хотя бы потому что пишу эти коды не один десяток лет.

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

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

Первый - это какой?

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

Есть мнение, что там тоже люди тоже с опытом… Мягко скажем…

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

Первый - это какой?

Это который (-b-sqrt(D)/2a, он меньше или равен второму который (-b+sqrt(D)/2a если корни не комплексные. В некоторых физических задачах интересен только один из корней, причем заведомо известно какой.

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

Есть мнение, что там тоже люди тоже с опытом… Мягко скажем…

Не спорю. Но опыт опыту рознь - специфика разработки HPC кодов сильно отличается от специфики разработки скажем веб-браузеров. Че там в веб-браузерах я ХЗ, но что и как нужно в HPC я представляю достаточно хорошо;-)

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

Тогда exception

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

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

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

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

Если код функции полностью доступен в точке вызова, оптимизирующий компилятор сделает всё, что сможет.

Если функция дергается из статических объектника или библиотеки, то можно попробовать собрать статическую либу с -flto[=thin], и после ликовать с -flto[=thin].

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

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

Первый - это какой?

Это который (-b-sqrt(D)/2a, он меньше или равен второму который (-b+sqrt(D)/2a если корни не комплексные. В некоторых физических задачах интересен только один из корней, причем заведомо известно какой.

Всё еще не вижу преимуществ двух отдельных переменных перед roots[0].

Ситуация с корнями простая, что делать если значения имеют разные типы?

Без практических примеров обсуждение улетит далеко в розовые теоретические облака.

Как я писал выше, я на практике сталкивался только с одной ситуацией, которая «в серой зоне» - warning’и. И то там есть приемлемые решения позволяющие обойтись без множественного возврата. В остальных случаях, когда такое требовалось, достаточно было уделить чуть больше времени на осмысление алгоритма, и понимаешь, что либо функция перегружена логикой, либо имеет смысл завернуть значения в объект, что позволит не только вернуть одно значение, но и повысит читаемость кода, ти добавить «предохранители», чтобы гарантировать консистентность данных.

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

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

бить по пальцам за геттеры/сеттеры/виртуальные функции/ехсепшены и т.д. и т.п;-)

О, так вы из религиозных…

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от Kroz

Без практических примеров обсуждение улетит далеко в розовые теоретические облака.

Вот Вам целых два последних:

  1. функция доступа к соседней ячейке в AMR сетке должна возвращать указатель на пользовательский тип const T* и ранг разбиения ячейки (целое число).

  2. функция расчета смещения к соседней ячейке регулярной сетки должна возвращать смещение (uint64_t) и принадлежность ячейки к границе (енум или битовую маску).

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

Не практикую подход «вокруг все идиоты, только я знаю как правильно»

Я тоже не практикую, я просто делюсь опытом.

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

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

Проблемы начинаются, когда используются функции из разных единиц трансляции, библиотек. Тут приходится думать, что делать с (не такими как мне надо) API, ABI.

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

Нет, это не религия, это образность изложения и констатация фактов. Вы не согласны с утверждением, что в разных областях разные стандарты разработки?

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от alysnix

если для out параметров синтаксис допускает объявление внешней(принимающей на стороне вызывающего) переменой в выражении вызова - то удобство увеличится

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

Вот Вам целых два последних:

функция доступа к соседней ячейке в AMR сетке должна возвращать указатель на пользовательский тип const T* и ранг разбиения ячейки (целое число).

Информации маловато, но попробую ванговать. Ранг разбиения ячейки - это аттрибут ячейки или указателя? Если ячейки то (предполагая что у ячейки есть ещё как минимум значение, а может и другие аттрибуты) должен быть класс ячейки; тогда функция возвращает указатель на ячейку, а уж там можно взять ранг. Это будет намного понятней и читабельней.

функция расчета смещения к соседней ячейке регулярной сетки должна возвращать смещение (uint64_t) и принадлежность ячейки к границе (енум или битовую маску).

Выглядит как две функции. Особенно учитывая что «возврат второго значения опционален».

Вообще, без примеров кода, понимания контекста (например, насколько ресурсоёмкие это операции) всё это угадывание.

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

Нет, это не религия, это образность изложения и констатация фактов. Вы не согласны с утверждением, что в разных областях разные стандарты разработки?

Разные стандарты - пожалуйста. Но должна быть аргументация. Например, я могу понять почему исключения нельзя использовать при разработке ядра и подобных компонент. Но вы пока не привели ни одного аргумента почему в вашем конкретном случае их нельзя использовать. «Бить по рукам» без объяснения, а тем более «переучивать» С++-шников - извините, но это религия.

Кстати, если вас не устраивают исключения, вам не нужны виртуальные функции - вам точно С++ нужен? Может вы не тот инструментарий выбрали?

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от Ygor

И? эти 8 да даже 28 байт в рантайме срать на них. Вот когда у тебя в цикле объекты создаются, тогда не срать (и то зависит от количества объектов - я софтину тут писал там объектов не более 1000 и срать мне не то что на 8 байт оверхеда но и на 8 килобайт на объект тоже срать, потому что 8 мегабайт там вообще не проблема - проблема чтение с архивов и их распаковка/запаковка в реалтайме, а как распакованные объекты хранить это вообще копейки меньше 1%). А так чистой воды преждевременные оптимизации.

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

Словно это проблема. Во всяких шарпах/джавах и прочих высокоуровневых ЯП в таком случае объектами срут, да даже в си можно структурами гадить.

Братан, ты понимаешь что такое объект, и что такое пара каких-то интов от балды связанных в структуру? Там гражданин по сути лепит объект, а срёт внутренним содержанием объекта. Надо что-то одно: или крестик или трусы, как по мне.

Другое дело, что да, функция никак не должна изменять входящие параметры. Но всем на это плевать потому что так удобнее.

Ну я даже не знаю зачем её сейчас экономить.

Тогда надо брать питон, java, c# и не мучить себя с++.

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

Надо что-то одно: или крестик или трусы, как по мне.

Надо. Ты же понимаешь что те же сеттеры и геттеры во всяких шарпах занесли ради инкапсуляции чтоб не гадить внутренним состоянием. Но по факту оно не для этого используется, а для проверки/перезагрузки операции присваивания/считывания. Мы много можем говорить что это неправильно и идея была в другом, но тут реальность у теоретиков с книжками и практиков расходится. А так да, в плюсы сеттеры геттеры с сахарком и не через жопу надо заносить чтоб таких проблем не было.

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

«Бить по рукам» без объяснения, а тем более «переучивать» С++-шников - извините, но это религия.

Вы хотите что бы я вам сейчас здесь в одном сообщении озвучил стандарты разработки HPC кодов и аргументацию почему эти стандарты именно такие?!

вам точно С++ нужен?

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

Кстати перегрузка операций где то считается нежелательной, а нам без неё никуда. Вы сейчас наверное затребуете объяснений почему:-)

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

Не надо ванговать, надо внимательно читать ТЗ, вся необходимая информация там есть:-)

Ранг разбиения ячейки - это аттрибут ячейки или указателя?

Ни то ни то. Это информация которая вычисляется (бесплатно) вместе с указателем, и иногда она может быть необходима. Причём ради неё делать повторные вычисления это непозволительная роскошь.

Выглядит как две функции.

Тем не менее под капотом должна быть одна функция, и вычисление должно быть единственным.

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

AntonI ★★★★★
()