LINUX.ORG.RU

Субботний C++ (как правильно сообщать о не успехе)

 


0

2

Продолжаю осваивать С++. И возник вопрос как сделать правильно следующую вещь…

Есть некий метод некоего объекта. Метод в случае успеха создает некий другой объект. Ну и естественно хочется возвращать его через return.

Но возможен другой ход развития событий, когда успеха не было. Это штатная ситуация. В си я бы похожую задачу решал бы посредством возвращения указателя на структуру. Либо указатель на созданную, либо null. Снаружи типа анализируем, и решаем что делать дальше.

А вот как такое правильно делать in cpp-way?

Все классы и методы мои, могу написать что захочу. А вот какие варианты идейно верные?

★★★

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

И для особо одаренных - запускать примеры на локалхосте НЕ СТОИТ. В продакшене можно, там бакапы.

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

и как тебе поможет optional в этом случае?

В этом случае тебе поможет std::variant, про который писали выше.

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

может и на ноль поделить уже нельзя???

Чувак, прикинь!

C99 6.5. 5p5 - The result of the / operator is the quotient from the division of the first operand by the second; the result of the % operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined.

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

Компилятор генерит код в расчёте, что у тебя при разыменовании указателя в нём никогда не будет NULL. Всё. Точка.

так я это и написал постом выше!

…да мало ли как трактует стандарт разыменование нула. физически это будет обращение по адресу со значением этой константы. и если защиты памяти нет, то оно пройдет прозрачно…ну с получением того что там записано.

да и причем тут разыменование, если разговор был про вполне себе полезное значение - nullptr.

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

the behavior is undefined.

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

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

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

да мало ли как трактует стандарт разыменование нула

Если вы их с компилятором трактуете по-разному, то у тебя баг в коде :D

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

Если вы их с компилятором трактуете по-разному, то у тебя баг в коде :D

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

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

это мое дело его определять

Не твое, а конпелятора. Ты примеры-то смотрел? Я для кого их сляпал, для пушкина штоле?

уб - означает что стандарт не определяет характер поведения программы на языке с++, это не мое дело, говорит стандарт

Тут ты путаешь undefined с unspecified или implementation-defined. Undefined он окончательный и бесповоротный, «так нельзя и в коде такого быть не может эвар».

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

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

Ох щщи! Чувак, у тебя, кажется, проблема: ты читать не умеешь. Undefined behaviour и implementation-defined behaviour – это, в рамках стандарта C, ООООООЧЕНЬ разные вещи.

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

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

Чувак, прикинь!

В Си всё время этот маразм с UB. В Обероне можно делить на ноль с предсказуемым результатом (обрабатываемое исключение).

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

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

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

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

В Си всё время этот маразм с UB. В Обероне можно делить на ноль с предсказуемым результатом (обрабатываемое исключение).

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

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

Кто из нас более прав?

У кого штаны меньше воняют.

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

В Обероне можно делить на ноль с предсказуемым результатом (обрабатываемое исключение).

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

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

короче, не надо делать из плюсов культа, это всего лишь средство.

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

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

порождает его рантайм, перехватывая или сигнал ОС или непосредственно прерывание.

В MSVC C++ и SEH так и сделано.

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

Если в вашей уютной команде есть аж 1 разработчик на С/C++ - то возвращай объекты как вздумается (и наверно не нужно советовать остальным с высоты своего обширного опыта аж из 1-го языка).

Городить огороды, возвращать указатель на объект, а ошибку интом по ссылке, нужно тогда, когда у вас в команде, или в зависимостях от вашей жизнедеятельности на любимом C/C++ заводятся питонисты, сишарписты, «дружные рубисты» итд, которым надо биндинги к ихним любимым языкам.

Примерно такие требования стоят обычно перед пацанами из хроноса, которомы надо сделать «равномерно херово» для множества язычков-клиентов.

Ты ведь наверно даже не задумался о том, как удобно будет тебе, и твоим возможным друзьям с их сишарпом, если ты объектик будешь рожать по ссылке?

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

Мне вот родители сказали в детстве, что в штаны срать – это плохо

Жалко вас, что вам самому до этого не дошло и пришлось объяснять.

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

Жалко вас, что вам самому до этого не дошло и пришлось объяснять.

Не все готовы к экспериментам. Многим хватает предупреждения.

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

Ты ведь наверно даже не задумался о том, как удобно будет тебе, и твоим возможным друзьям с их сишарпом, если ты объектик будешь рожать по ссылке?

Нормально им будет, привычно. Ознакомьтесь как работает Int32.TryParse в C#, например. Именно так, как я советую.

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

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

Тебя тоже к горшку приучали, просто мы замещаем это воспоминание в подростковом возрасте, мол и так всегда было ясно. Древние вообще срали в штаны до 30, а потом страдали от болезней, т.к. не было медицины.

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

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

Всем стилям и соглашениям невозможно угодить одновременно.

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

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

Чувак, ты не понимаешь. UB в том числе влияет на code flow. У тебя тупо ветки кода могут выкидываться.

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

Вот чтобы так не делать, изобрели std::optional

Ога, *opt намного лучше чем *ptr

Имеет, только его лучше выпилить и забыть о его существовании

Почитай core guidelines какие-нибудь.

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

Ога, *opt намного лучше чем *ptr

Да. Как минимум, отсутствием UB. Почему это хорошо, читай тред выше.

Почитай core guidelines какие-нибудь.

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

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

Чувак, ты не понимаешь. UB в том числе влияет на code flow. У тебя тупо ветки кода могут выкидываться.

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

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

отсутствием UB

https://en.cppreference.com/w/cpp/utility/optional/operator*

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

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

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

код может быть(это implementation defined) выкинут, если он статически недостижим, то есть нет ни одного способа передать на него управление.

Ох господи…

Вот:

[code] int foo(int x) { int a; if (x) return a; return 0; } [/code]

Код достижим, да ещё как. На -O2 компилятор выкинет ветку из-за UB (a не определен).

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

Ох господи…

Вот:

int foo(int x)
{
    int a;

    if (x)
        return a;

    return 0;
}

Код достижим, да ещё как. На -O2 компилятор выкинет ветку из-за UB (a не определен).

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

А может статически недостижимый код быть вызванным UB?

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

ну а какой нить clang 15 увидит наверное. по крайней мере нет никаких варнингов, хотя и стоит -wall. ну может у него еще какие флаги есть, чтобы он увидел.


void ff(){
	int *ptr = nullptr;
	*ptr=0; //обращение по null

	int xx = 0;
	xx = 10/xx; //деление на нуль
}

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

Код достижим, да ещё как. На -O2 компилятор выкинет ветку из-за UB (a не определен).

какую ветку он выкинет-то? он прежде всего даст варнинг о неинициализированной переменной. этот код можно трактовать и как легальный(чувак просто хочет прочитать, что там на стеке лежит на данном адресе) и как нелегальный - чувак забыл туда хоть что-то записать… а какую «ветку» тут «выкидывать» вообще непонятно. давай вот скажи, какую ветку он тут выкинул, и на каком основании.

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

какую ветку он выкинет-то? он прежде всего даст варнинг о неинициализированной переменной. этот код можно трактовать и как легальный(чувак просто хочет прочитать, что там на стеке лежит на данном адресе) и как нелегальный - чувак забыл туда хоть что-то записать… а какую «ветку» тут «выкидывать» вообще непонятно. давай вот скажи, какую ветку он тут выкинул, и на каком основании.

Что тебе рассказать? Делаешь objdump -St и наслаждаешься:

0000000000001160 <foo>:
    1160:       31 c0                   xor    %eax,%eax
    1162:       c3                      ret
    1163:       66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    116a:       00 00 00
    116d:       0f 1f 00                nopl   (%rax)
anonymous
()
Ответ на: комментарий от anonymous

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

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

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

А ты можешь предвидеть, какой код будет вызван при UB, чтобы этим пользоваться на уровне «ну уб, ну и чо»?

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

ты на вопрос то ответь. зачем из функции что была ВОЗМОЖНО некорректной, он слепил код который некорректен в принципе. потому, что там никоим образом не написан безусловный возврат нуля. то есть он думает, что повыкидывав ветки с потенциально опасным UB кодом, он породил код безупречный? может и ты так думаешь?

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

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

Когда ты оптимизируешь систему линейных неравенств каким-нибудь хитрым методом, промежуточные матрицы могут вообще никакого смысла не иметь, но на выходе у тебя будет решенная линейная проблема, because science, bitches. При этом алгоритм может накладывать свои условия, например что система не underconstrained, т.е. ее переменные не «болтаются», как например в [(-1x <= 0), (1x - 10 <= 0)]. Иначе он выплюнет то, что получилось, может 0, может 10, может 3.14, в зависимости от колва неравенств или рандом сида, и никакого понимания об этом в него не заложено. То же самое в компиляторах - у них нет и никогда не будет понимания кода и для чего он, есть только очень эффективный способ решать задачу так, как диктуется стандартом и исходником, ему удовлетворяющим. Зачем стандарт так диктует - а затем что невалидные программы все равно не валидны, и корректно сконпелять их нельзя в принципе. Терять полезные оптимизации ради того, что кто-то решил поделить на ноль, никто не хочет.

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

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

The behavior is undefined if *this does not contain a value.

Охренеть! Они и тут обосрались. Просто потрясающие чуваки.

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

К счастью, я сырые указатели в optional никогда не заворачивал. Как и не использовал operator* у него.

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

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

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

Кто стреляет в пятку, а попадает в нос, клоун.

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

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

Я что-то не уверен, что добавить if в operator* и кидать исключение – это дорого. С учётом предсказателя в процессоре, это скорее всего вообще бесплатно или почти бесплатно. Хотя бенчмарк увидеть было бы интересно.

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

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

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

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

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

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

Не, так не годится, у тебя изначальный тезис был иным

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

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

if (ptr)   *ptr;

подтверждающего примера ты не показал.

Что касаем предоставленного примера - ну да, шланговская оптимизация. Вот только происходит она лишь потому, что значение в указатель приходит не во время рантайма и компилятор его может отследить. Реальный код ты сначала 100% скомпилишь с -O0 если хоть немного адекватный (плюс еще набор отладочных опций) и этот дереференс будет мигом отловлен ибо -O0. Если же значение указателя зависит от каких-то рантайм условий, то никакой подобной оптимизации не будет https://godbolt.org/z/cKaj564n8.

В общем, игрушечный пример, встреча в реальной жизни с которым - близка к нулю.

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

А единственная причина того, что это undefined вместо unspecified - какие-то МК, которые не могут вообще что-то конкретное в этом случае прогарантировать. Думаю так.

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

подтверждающего примера ты не показал

Да блин, все кто в теме это видели https://lwn.net/Articles/342330/

И тут ты такой учишь компиляторы как оптимизировать, а что такое UB не понимаешь

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

Ну так просто идиоматичный C++ - zero cost как религия. Краткие формы быстрые, длинные - безопасные. Так же как с operator[]/at() у vector.

А так да, где-то выше *opt уже должна была быть проверка на ноль, и повторная вполне может быть оптимизирована.

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

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

Потому что компилятору так захотелось. Неопределенное поведение. Ты читать-то умеешь, родное сердце?

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

Ну так он заоптимизурует, если там действительно данные по ptr есть (либо сам подставит).

Я ошибался в том, что зависимость от рантайм условий отключит оптимизацию. ГЦЦ норм, а шланг творит лютую дичь (не -O0):

#include <stdio.h>

const char *f() {
    char i = getchar();
	if (i == '1')
		return NULL;
	return "qjfkdjf";
}

int main(void) {
    printf("formatting drive %c\n", *f());
}

Ему вообще пофиг, что возврат из f() зависит от рантайм условий и всегда возвращает «qjfkdjf». Если не могут написать даже unspecified, то нужно хотя бы обязать делать реальное чтение из памяти, а не оставлять полностью на откуп оптимизатору. В общем да - лучше бы с этим что-то сделать. Вдруг какой-то редкий кейс и в -O0 дело до него не дошло, так пусть релизе лучше вообще все упадет чем вот так как в шланге.

pavlick ★★
()
Последнее исправление: pavlick (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.