LINUX.ORG.RU

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

 , , чистый код


0

5

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

Это семантически принципиально разные понятия. Выходные параметры — это модификация существующего состояния, возвращаемые значения — это создание новых объектов и возвращение их из функции (copy elision и пр. оптимизации — не играет, детали реализации).

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

Нет, там это как замена return val1, val2, val_n идет. То есть возвращаем через return bool, типа удачно/неудачно сработало, остальное в указателях или ссылках в числе параметров. Типичный пример void cv::cvtColor(cv::Mat src, cv::Mat& dst, ColorScheme).

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

Если в современном С++, то std::make_tuple(val1, val2, val_n) с распаковкой через auto [val1, val2, val_n] = myFunc(). Если в С, сделать структуру с нужными полями и возвращать ее.

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

так-то в императивщине это костыль, который призван убрать проверку на приёме выщлопа работы функции, в ООП не нужон. Вот и в более древних подходах применяется if (myfunc(blah) > 0)

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

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

C++ проблемы, как ни странно. Многим функциям все равно, что получить на вход: cv::что-то, std::vector<что-то>, std::vector<std::vector<что-то>>. Про проблемы иногда можно узнать только в рантайме.

LongLiveUbuntu ★★★★★
() автор топика
Ответ на: комментарий от Ygor
typedef struct {int a; float b;} foo_ret;
foo_ret foo (int a, float b) {
	return {a*a, b*b};
}

#include <iostream>
int main(int argc, char **argv)
{
	auto [i,f] = foo(2,1.5);
	
	std::cout<<i<<" "<<f<<"\n";
	return 0;
}
thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

Если в современном С++

сделать структуру с нужными полями и возвращать ее.

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

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

Если в С, сделать структуру с нужными полями и возвращать ее.

Фу так делать. Даже если компилятор поддерживает return разрядности большей чем машинное слово/максимальный примитивный тип, это не повод данный изврат внедрять в свой код.

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

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

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

C++
Многим функциям все равно, что получить на вход: cv::что-то, std::vector<что-то>, std::vector<std::vector<что-то>>.
Про проблемы иногда можно узнать только в рантайме.

Давай конкретный пример конкретной функции с конкретным что-то и конкретной проблемой в рантайме.

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

Догадайся

Зачем? Ты что, полумная бабка, гадающая тип аргументов функции на кофейной гуще?

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

Согласно прототипа, вот такие:

double cv::calibrateCamera	(	InputArrayOfArrays 	objectPoints,
InputArrayOfArrays 	imagePoints,
Size 	imageSize,
InputOutputArray 	cameraMatrix,
InputOutputArray 	distCoeffs,
OutputArrayOfArrays 	rvecs,
OutputArrayOfArrays 	tvecs,
int 	flags = 0,
TermCriteria 	criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) 
)		

И да, я просил проблемы в рантайме из-за неправильных параметров.

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

Так что должно быть вместо InputOutputArrays? Что это? Какой-то массив? vector, array, list, какой-то свой тип?

А проблемы в рантайме - это сегфолт с ассертом, например. Хорошо, хоть ассерт догадались в код добавить.

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

Ты не поверишь, но LSP тут бессилен, а это IntelliSence C++ от Майкрософт, на минуточку. Он справился бы в случае вывода типа компилятором, но не в этом случае.

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

Так что должно быть вместо InputOutputArrays?

В каком смысле «вместо»? Ты норкоман, сука, чтоле? Функция ожидает InputOutputArrays, так что будь добр ей именно его и передать.

А проблемы в рантайме - это сегфолт с ассертом, например.

А-а-а-а-а… Ну, тогда-то всё понятно. Закрываем тему.

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

Раз LSP не видит определение, может оно создается кодогенератором при сборке, так часто делают. Попробуй собрать проект и перезапустить IDE. Если не получится, то берешь grep или что то покруче, и вперед, искать где определяется этот символ.

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

Если в С, сделать структуру с нужными полями и возвращать ее.

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

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 2)

В той же OpenCV ехал выходной параметр через выходной параметр.

Управление памятью.

В computer vision очень жесткие требования по производительности могут быть в soft real time задачах при высоких нагрузках.

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

Помню когда только учился работать с OpenCV, первую итерацию пайплайна оптимизировал по памяти и получил +30% скорости.

Norgat ★★★★★
()

Так делают по тем же причинам, что и вот такое void(struct { ... }*) - типа опасаются за «производительность». В основном связано с древностью.

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

OpenCV 4 все еще поддерживало C++ 99 (потому что хотели иметь поддержку компиляторов под Android, а там новее не было). А теперь никто не хочет переписывать сигнатуры, потому что это потянет за собой потерю обратной совместимости.

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

Вот подтверждение того, о чём я говорил. Какие-то легенды из 80-х. Ну и выше там такие же истории:

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

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

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

Какие еще легенды? Я вот профайлил код с использованием OpenCV через интеловские тулзы и видел раскладку на что тратится время. Левое выделение памяти в цикле обработка кадра с видео - сразу заметная просадка по производительности в 5% и более получалась. А когда тебе нужно уложиться в 24-30 fps, то это все очень заметно становится.

Norgat ★★★★★
()
Последнее исправление: Norgat (всего исправлений: 1)

Туплами в C++ тяжело пользоваться. Иногда проще и красивее (для C++) сделать возврат результата через ссылку или указатель в стиле голого С. А вот если бы туплы в С++ были бы обычными и такими, как они выглядят везде в других языках, то, это, наверное, уже был бы не тот C++, который мы все любим - только каждый по своему

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

Да, «я сам видел» это прям аргумент аргументов. Какое-то выделение памяти, какой-то цикл. Максимум, что у тебя там могло произойти - это ты увидел разницу и не понял, чем она обусловлена. Потому как out-параметры vs return никакой разницы в рантайме не дают.

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

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

Размер этих буфферов легко может быть и килобайты и мегабайты. Оборачивать все это в структуры - это ужас, учитывая сколько там типов в OpenCV и так.

Там сколько макарон получилось бы, что их просто не дописали бы.

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

В том, что у тебя входные параметры и выходные нужны для управления памятью. Если не можешь сообразить как они связаны - сходи напиши пару обработок потока входных кадров из видео на OpenCV с применение 3-5 алгоритмов в каждому кадру и все станет ясно.

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

Не, меня не интересуют съезды в стиле «ну сходи туда-то и сделай то-то». Есть разница out vs return - показываешь. Не можешь показать - значит разницы нет и твои знания основаны на легендах из 80-х.

И да, где ответ на предыдущий вопрос? Чего ты так резко начал рассказывать «код будет некрасивый», позабыв про память/интеловские тулзы/30%?

anonymous
()

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

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

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

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

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

Это неверно. Про изменение в теме речи не шло. Выходные/out-параметры - это не входные-выходные/in-out-параметры. Если на входе должен быть параметр - он будет в любом случае, здесь нет выбора означенного в ОП.

возвращаемых через стек например

В фантазиях например. Максимум на какой-то лапше компилятор затупить может, но не более.

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

cv::calibrateCamera(objectPoints, imagePoints, frameSize, cameraMatrix, distCoeffs, rvecs, tvecs); Догадайся какой тип должен иметь каждый параметр.

Да. Это недостаток синтаксиса. В C#, например, надо явно указать ref или out для объектов, которые будут возвращены по ссылке. Будет что-то типа cv::calibrateCamera(objectPoints, imagePoints, frameSize, cameraMatrix, distCoeffs, out rvecs, out tvecs);

Возвращать через pair несколько сложных данных (матриц, например) - очень не эффективно. Оно бы могло решаться через сборщик мусора, но в C++ его нет. Есть move-семантика, но она не так давно появилась и люди путаются ещё.

Я делал свой язычок, в котором вообще не было возвращаемых значений, а все параметры передавались по ссылкам, но входные по неизменяемым, а выходные по изменяемым. Там бы функция выглядела примерно как: rvecs, tvecs cv::calibrateCamera(objectPoints, imagePoints). Тут будет небольшая потеря при передаче простых данных, но это копейки. Зато синтаксис лаконичнее.

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

Возвращать через pair несколько сложных данных (матриц, например) - очень не эффективно

Очередные поверья 40-летней давности. Всё как я и говорил.

Я делал свой язычок, в котором вообще не было возвращаемых значений, а все параметры передавались по ссылкам, но входные по неизменяемым, а выходные по изменяемым

Это называется не осилил иное, кроме самого примитивного.

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

Очередные поверья 40-летней давности.

Ну справедливости ради вот такой код:

struct MyStruct
{
    MyStruct(){ cout << "MyStruct constructor" << endl;}
    MyStruct(const MyStruct &other){ cout << "MyStruct copy constructor" << endl;}
    MyStruct(const MyStruct &&other){ cout << "MyStruct move constructor" << endl;}
    MyStruct& operator=(const MyStruct& other){ cout << "MyStruct = constructor" << endl;}
    MyStruct& operator=(const MyStruct&& other){ cout << "MyStruct = constructor" << endl;}

};

MyStruct func()
{
    MyStruct a;
    return a;
}

int main()
{
    MyStruct b = func();
    return 0;
}

у меня выводит только «MyStruct constructor». Но я не оборачиваю в pair. Как там будет мне лень проверять.

не осилил иное, кроме самого примитивного

А что не примитивное?

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

Да. Это недостаток синтаксиса

Недостаток извилин, скорее. Ты волен написать что угодно, любую обертку ..., out(rvecs), out(tvecs), in_out(something));, любой интерфейс, который будет достаточно выразителен

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

Но я не оборачиваю в pair

Будет тоже самое. Да и причём здесь pair? Наличие разницы не зависит от конкретного типа.

А что не примитивное?

Как минимум функции с возвратами. Как ещё менее примитивное - инлайн.

anonymous
()