LINUX.ORG.RU

Деструкторы не нужны (?)

 


0

5

Тут в соседней теме один анон сказанул следующее:

Дело не в том. Сишка, она про написание руками + можно навесит абстракций, аки глиб/гобджект. Кресты же — изначально нагромождение абстракций со строками, срущими в кучу, функциональными объектами, методами, вызывающимися неявно (например, деструкторы, на которые жаловался кармак) и проч

Собственно, хотелось бы поговорить о выделенном.

Антон прикрылся ссылкой, по которой про деструкторы я так ничего и не нашёл. Более того, в твиттере Кармака всё выглядит с точностью до наоборот — https://twitter.com/id_aa_carmack/status/172340532419375104

RAII constructor / destructor pairing and templates for type safe collections are pretty high on my list of C++ benefits over C

Кто прав? Кто виноват? И нужны ли в итоге C++ деструкторы?

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

допустимо ли складывать B*x и С

man bind function

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

А в чем у тебя проблема?

У меня нет проблем, я использую исключения.

Обернул в монады и вперед.

Вперед, покажите код.

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

допустимо ли выполнять B*x, что будет результатом B*x, допустимо ли складывать B*x и С, и т.д.

Это на уровне типов решается.

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

Это на уровне типов решается.

Ну решите задачку сложения матриц, размерность которых определяется в runtime, на уровне типов. В C++.

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

Вперед, покажите код.

(A+B)*x

let open Result in
Ok (A,B) 
>>= (fun (A,B) -> if (Mat.size A) = (Mat.size B) 
                  then Ok (Mat.sum A B)
                  else Error "some error")
>>= (fun C -> Ok (Mat.mul_int C x));;
anonymous
()
Ответ на: комментарий от anonymous

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

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

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

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

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

О, шлангист зашланговал.

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

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

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

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

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

О, шлангист зашланговал.

А аргументы-то становятся все весомее и весомее.

По делу что сказать имеете? Где вменяемый код на C++?

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

А аргументы-то становятся все весомее и весомее.

Какой вопрос — такой ответ. Какой ты ждал аргумент на великолепную критику: «Ну просто выдающаяся демонстрация выразительности. Ради этого, определенно, стоит запретить и исключения, и деструкторы.». Такой-то осмысленный комментарий.

Где вменяемый код на C++?

Тебе вменяемый или на С++?

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

Какой вопрос — такой ответ.

Это точно. Было:

A=B*x + C;
Стало:
let open Result in
Ok (A,B) 
>>= (fun (A,B) -> if (Mat.size A) = (Mat.size B) 
                  then Ok (Mat.sum A B)
                  else Error "some error")
>>= (fun C -> Ok (Mat.mul_int C x));;
Тут уже нечего комментировать, критиковать или спрашивать.

Тебе вменяемый или на С++?

Это топик про C++ вообще-то.

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

Мало того, важен порядок описания объектов в заголовке. Это для меня было культурным шоком.

Справедливости ради: этот порядок ведь должен где-то задаваться. Учитывая, что объявление класса можно сделать только один раз, то это вполне подходящее место.

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

в нормальных языках исключения быстрые

Распространённый миф. Просто «нормальные» языки уже настолько тормозные, что бросанием исключений хуже уже не сделаешь.

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

Потому что при работе с ffi оно всё надо почти всегда.

Ок, но всё-таки: эти функции что-то опасное сделать могут?

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

а в том, допустимо ли выполнять B*x, что будет результатом B*x, допустимо ли складывать B*x и С, и т.д.

B*x = матрица-или-ошибка.

То есть операторы могут применяться как к матрицам, так и к значениям матрица-или-ошибка. Если аргумент имеет значение «ошибка», результат тоже «ошибка».

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

Альзо на хаскиле оно бы выглядело няшно и красиво

Вот на такой вариант хотелось бы взглянуть и не важно на каком языке.

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

Стало:

Не совсем. У тебя в коде выше + — перегруженный метод. Никто не мешает написать. Окамловский код же с лямбдами, никто не мешает объявить монадические операции и писать:

let open Result in
Ok (A,B) 
>>= (fun (A,B) -> if (Mat.size A) = (Mat.size B) 
                  then Ok (Mat.sum A B)
                  else Error "some error")
>>= (fun C -> Ok (Mat.mul_int C x));;

как

let open Result in
Ok (A,B) 
>>= MMatrix.sum
>>= (MMatrix.mul_int x)

или просто перегрузить операторы, например |+| и .* в каком-нибудь модуле ResultMatrix (достаточно написать простой функтор для модуля Matix) и писать

let open ResultMatrix in
(A |*| B) .* x

Где |*| и .* — обычные моноиды.

Но такую запись ты бы банально не понял. Потому расписал тебе, что было попроще и понятнее.

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

эти функции что-то опасное сделать могут?

Что понимать под «опасное»?

Если в финализаторе сделать ссылку на само значение (register-finalize s (lambda (x) (close-input-port s))) вместо (register-finalize s (lambda (x) (close-input-port x))), то значение никогда не будет собрано сборщиком мусора.

Можно сделать вечный цикл и заблокировать очередь финализации.

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

Очевидно, вы предлагаете программировать в стиле:

Either<Matrix,Error> operator*(const Either<Matrix,Error> & m, const Number n) {
  ... // сперва проверим m на error.
  ... // затем уже попробуем сделать перемножение.
}
...
Either<Matrix,Error> operator*(const Either<Matrix,Error> & a, const Either<Matrix,Error> & b) {
  ... // сперва проверим a на error.
  ... // сперва проверим b на error.
  ... // потом проверим размерности a и b.
  ... // затем уже попробуем сделать перемножение.
}

Вы все это серьезно говорите ли это троллинг такой? Попытка чувство юмора продемонстрировать?

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

Вы не мои умственные способности обсуждайте. Вы код покажите. Грубо говоря, у вас есть цепочка из N операций. Каждая из них может завершиться неудачей. Ваш подход может прервать цепочку на i-й операции, если там обнаружили ошибку? Или же вы будете делать еще (N-i-1) проверку в цепочке? Пока выходит, что проверки у вас будут.

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

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

Попытка чувство юмора продемонстрировать?

сперва проверим a на error.

потом проверим размерности a и b.

А ты предлагаешь что? Как ты выделишь память под матрицу-результат, не зная размерности операндов?

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

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

А ты предлагаешь что?

Предлагаю, как минимум, читать, что пишут. Желательно было бы еще и мозги включить.

Ну и проверять на error не надо в языках с монадками

Искренне рад за языки с монадками. C++ давно в их число попал?

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

Желательно было бы еще и мозги включить.

Заметил, что по ходу треда весь набор твоих аргументов ограничивается узким кругом вида: («ты — дебил», «я ничего не понял, значит не работает», «покажите на крестах, я не знаю других языков», «ты — дебил»). Так что я, пожалуй, сольюсь с дискуссии, все равно что с ослом разговаривать.

C++ давно в их число попал?

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

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

Вопрос только был не про арзаичные языки, а про выразительность и возврат значений.

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

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

Что понимать под «опасное»?

Да меня просто смутило/заинтересовало название «unsafe». Пожалуй, ответ получил.

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

там

Там никто и не пытался написать монадку. Там кто-то пытался написать какой-то странный прокси-объект. Кажется, ты не совсем понимаешь, что такое монада, моноид и.т.д.

Вы-то уж наверняка бы справились

Что там писать-то, тебе нужно всего две функции

https://habrahabr.ru/post/205026/

Одного только не пойму, как в вопросе

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

Аргументом стала убогость крестов?

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

Там кто-то пытался написать какой-то странный прокси-объект. Кажется, ты не совсем понимаешь, что такое монада, моноид и.т.д.

Кажется, вы просто не можете изложить свои мысли в коде на C++. На что я вам намекаю уже не первый раз. Как там в безопасных языках с GC — это дело десятое. Покажите, как записать A=B*x+C в C++ без исключений, но чтобы это было и эффективно, и выразительно.

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

Будет выглядеть как код на Rust. За исключением того момента, что в Rust есть matching, которого нет в C++. Поэтому match из Rust преобразуются в if(result.is_ok()).

Option и Result из Rust можно реализовать на C++ с нужными методами. И писать как-нибудь так:

Result<T> r = do1()
    .and_then(do2)
    .and_then(do3);

Эти and_then – вариант bind, о котором талдычат анонимусы.

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

Как там в безопасных языках с GC

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

Теперь скажи мне, при чем здесь, мать его, гц, при чем здесь тот потешный прокси-объект с send.

Кресты убоги, в них это эффективно можно сделать только на уровне шаблонов

https://github.com/beark/ftl

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

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

Поздно. Херб Саттер уже плохому норот научил: собрал все грязные хаки в одной статье :)

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

Блин, да неужели так сложно A=B*x+C записать в and_then-нотации самостоятельно? Запишите и посмотрите, что получится.

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

2012

У тебя устаревшая инфа, ныне он переписывает код с крестов на схему и пишет прошивки для звездолетов на haskell)

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

Бинд принимает монаду, функцию над содержимым монады и возвращаем монаду. В случае монады error при условии ошибки, цепочка биндов просто делает неявный готу в конец цепочки, пропуская все вызовы функций.

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197
Result<Matrix> A = matrixMul(B, x).and_then(matrixAdd, C);

Вот так, навскидку. Можно ещё поколупаться, думаю, и сделать так, чтобы было:

Result<Matrix> A = B * x + C;
Тут operator* возвращает Result, и определён operator+, принимающий слева Result lhs и возвращающий lhs.and_then(operator+, C).

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

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

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

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

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

там спрашивали про отношения с С++. Алсо, движки игорей он на схему не переписывает, не надо ля-ля: «Джон Кармак предлагает использовать Scheme в качестве скриптового языка Gear VR/Oculus Rift» (с) :)

«предлагает» — это не переписывает

С тем же успехом можно сказать «у Джона Кармака баттхерт от андроида и он не смог в Java»: http://hi-news.ru/technology/dzhon-karmak-proklyal-vsyo-poka-adaptiroval-virt...

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

Result<Matrix> A = matrixMul(B, x).and_then(matrixAdd, C);

Ну это определенно лучше, чем A=B*x+C с исключениями. На 146%.

Тут operator* возвращает Result, и определён operator+, принимающий слева Result lhs и возвращающий lhs.and_then(operator+, C).

Прекрасно. Вы переизобретаете expression templates со всеми их недостатками. Плюс получаете чейнинг, на каждой фазе которого будет проверяться результат предыдущей операции.

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

Каких только приключений люди себе не понапридумывают.

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

Цепочку биндов реализовать можно, а вот оптимально — никак (ну разве что через шаблоны).

Это вы мне сейчас пытаетесь доказать то, о чем я вам намекал давным давно.

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

Да, пример с хабра был прям в тему, в тему. Особенно порадовали те фичи, которые в язык еще не включены.

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

Прекрасно. Вы переизобретаете expression templates со всеми их недостатками.

Это не они, если ориентироваться на описание в Википедии.

Было:

Matrix operator*(Matrix m, int x)
{
 // ...
}

Станет:

Result<Matrix> operator*(Result<Matrix> m, Result<int> x)
{
  // ...
}

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

Ну это определенно лучше, чем A=B*x+C с исключениями. На 146%.

Спор об использовании и неиспользовании исключений - это уже из разряда «остроконечников» и «тупоконечников» из Гулливера. НА МОЙ ВКУС, лучше. Объясняю почему.

С++ с исключениями:

void f()
{
 // Я тут что-то делаю, готовлю данные, чтобы ниже вычислить
 // выражение, содержащее матрицы. Поскольку это коротенькое
 // выражение может взорваться эксепшеном и начать мне тут 
 // раскручивать стек и всё такое, я должен постоянно держать 
 // это в голове для этого участка кода и всего кода, который
 // находится выше f() по стеку. Писать т.н. exception-safe код,
 // то есть параноить, что в любой момент из любого места на меня
 // выпрыгнет беда и начнёт всё крушить.

 try {
   Matrix A = B * x + C;
 } catch(matrix_error) {
   // В версии libMatrix 1.2.3 написано, что операции над матрицами
   // кидают matrix_error. Окей, ловлю ситуацию, что матрицы не
   // посчитались, пишу в лог.
 }

 // А в вот в версии 1.2.4 автор решил, что matrix_error мало и
 // добавил ещё один тип: compute_error. У меня огород большой,
 // проглядел этот момент. И по коду интерфейса этого не видать,
 // так как типы эксепшенов в заголовке не прописываются для ф-ций. 
 // 99% времени матрицы у меня ладные да складные, так что я и не
 // замечаю проблемы. Пока в продакшене у меня код не звезданётся с
 // невкусным сообщением о невыловленном эксепшене в ситуации, когда
 // я обработку ошибки вычисления вообще-то написал.
}

И гипотетический C++ без исключений, но с Result:

void f()
{
  // Я спокойно готовлю данные, мне не нужно париться о том,
  // что на меня выпрыгнет из-за угла эксепшен. Всё, что может
  // обломиться, возвращает не int на Result<int>, который
  // я честно проверяю и реагирую соответственно.

  // Использую libMatrix 1.2.3
  Result<Matrix> A = B * x + C;
  if (A.is_error()) {
     // Матрица не посчиталась, пишу в лог A.get_error().
  }

  // А в версии libMatrix 1.2.4 автор решает добавить ещё один
  // тип ошибки и... нихрена не меняется для моего кода. Он как
  // работал, так и работает. А если автор либы решает вместо 
  // Result сделать Option а то и вовсе изобретает способ, как 
  // гарантированно посчитать результат, это сразу ведёт к изменению
  // прототипа функции, и компилятор мне такой: чувак, хедер
  // libMatrix поменялся, вот тебе номера строк, глянь, я старый
  // твой код не могу скомпилить.
}

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

И чего? Сишники живут с явным чейнингом, пифоманс не страдает. А порой это даже помогает переписать кусок кода по-другому, так чтобы точек отказа стало меньше. А в C++ есть возможность этот чейнинг с глаз долой припрятать.

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

Это не они, если ориентироваться на описание в Википедии.

Надеюсь, что кто-нибудь таки начнет читать то, на что отвечает:

Тут operator* возвращает Result, и определён operator+, принимающий слева Result lhs и возвращающий lhs.and_then(operator+, C).

Прекрасно. Вы переизобретаете expression templates со всеми их недостатками.

Речь не про один operator*, а про чейнинг нескольких операций. Вы либо вообще все операции переводите в разряд операндов типа Either (о чем я говорил еще monk-у), либо извращаетесь с expression templates в попытке изобразить монады (lhs.and_then(operator+,C) — это как раз из этой оперы).

Все, что вы написали про исключения и про ваши фобии — это все уже неоднократно описывалось, нечего ходить по N-ному разу. Раз для вас exception safe код — это параноя, то дальнейший разговор смысла не имеет.

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

Да меня просто смутило/заинтересовало название «unsafe».

Там просто собрано всё, что относится к ffi. Например, https://docs.racket-lang.org/foreign/ctype.html точно ничего опасного сделать не может. Но лежит там же.

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

Окей, ловлю ситуацию, что матрицы не посчитались, пишу в лог.

Не в защиту исключений, но если надо «просто писать в лог», то и ловить достаточно std::exception.

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

Раз для вас exception safe код — это параноя, то дальнейший разговор смысла не имеет.

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

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

Это высокая цена, которую обязан платить любой программист на C++

Во-первых, цена совсем не высокая.

Во-вторых, этот стиль программирования присущ не только C++, но и ряду других языков: D, Rust и даже Go (через Go-шный defer).

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

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

Во-первых, цена совсем не высокая.

Ты этого просто не замечаешь :-) Для тебя и C++ не сложный :-) Так бывает, когда много лет посвящаешь себя какому-то узкому направлению :-)

Во-вторых, этот стиль программирования присущ не только C++, но и ряду других языков: D, Rust и даже Go (через Go-шный defer).

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

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

exception-safe код писать не просто :-) Поэтому, много ли ты видел библиотек, которые в API тщательно документировали бы предоставляемую гарантию безопасности исключений? :-)

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.