crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V  < 1 2  
Ответить в данную темуНачать новую тему
> Размышления о правильном программировании
Iron Bug
  опции профиля:
сообщение 29.3.2010, 16:58
Сообщение #11


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(Litkevich Yuriy @ 28.3.2010, 23:59) *
Цитата(Iron Bug @ 29.3.2010, 0:08) *
про возвращаемое значение:
имхо - функция ВСЕГДА должна возвращать int с кодом ошибки и точка. результаты - в параметрах.
а я вот категорически с этим не согласен.
Сам термин "функция" обязывает к математическому поведению. Как и приведено у автора статьи


все проекты, которые я видела, имели именно такие функции, возвращающие int. и я думаю, что это весьма оправданно. просто когда переходишь к реальному программированию, выясняется, что иногда вообще нет смысла возвращать значение, потому что его нет как такового, а вот зато код результата выполнения нужен абсолютно во всех случаях для проверки. я не разделяю вызовы на "функции" и "процедуры" - это устаревшее условное разделение, ничем не обоснованное на практике.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ufna
  опции профиля:
сообщение 29.3.2010, 17:22
Сообщение #12


Активный участник
***

Группа: Участник
Сообщений: 362
Регистрация: 24.5.2008
Из: Курган/СПб
Пользователь №: 182

Спасибо сказали: 29 раз(а)




Репутация:   5  


и положительные эмоции, и не очень :)

как уже было сказано - цели и практика конкретного приложения могут быть разными. Иногда "красиво" идет в ущерб быстродействию, как минимум.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 30.3.2010, 8:14
Сообщение #13


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Цитата(Iron Bug @ 29.3.2010, 0:08) *
про возвращаемое значение:
имхо - функция ВСЕГДА должна возвращать int с кодом ошибки и точка. результаты - в параметрах.

Если постоянно работаешь с чистым С, и основные апи на нём или на "С с классами" то это вполне оправданно - для единообразия. :)
В других случаях вполне разумно использовать и остальные возможности С++, например такие как возвращение экземпляра структуры/класса из функции и исключения. :)
А вот смесь этих подходов очень чревата ошибками.
Я как-то видел большой проект, где функции и возвращали код ошибки, и бросали исключения - очень неприятное спагетти получалось...

Кстати, возвращать таки лучше не int, а перечисление. :)

Цитата(Iron Bug @ 29.3.2010, 0:08) *
про геттеры и сеттеры - для крупных проектов со сложными объектами это единственный нормальный и безопасный способ обращения к данным. при этом обеспечивается полная инкапсуляция и наследование.

Зависит от назначения класса/структуры. В том же std::pair геттеры и сеттеры, как и наследование бессмысленны. :)
А наследование, в С++, обеспечивается наличием средствами языка, а не геттерами/сеттерами. :)

Ну и про рекурсию - это маразм. :)
Итеративные алгоритмы - это просто подмножество рекурсивных.
Другое дело, что встроенную в императивный язык рекурсию можно вручную нарисовать с помощью явных стеков на массиве и циклов.
Но такая эмуляция будет гораздо менее понятной и более подверженной ошибкам чем явная. :)
С другой стороны рекурсивные алгоритмы всё-таки пока требуются не так часто.
Рекурсией конечно не следует злоупотреблять, как и любой возможностью. И нужно чётко представлять во что выльется тот или иной код для выбранного языка и компилятора.

П. С. Функциональные языки, типа Haskell и LISP полностью строятся на рекурсии и циклы в них - это довольно простые функции выраженные в терминах рекурсии. :)

Ага, таки решил прочитать опус. :)

Про рекурсию у него примерно то и написано что у меня. :)

Про вложенные функции с ним не согласен.
Просто лексическая вложенность функций ничем не лучше чем объединение их в классе или пространстве имён или модулей и пакетов.
А вот для организации частичных применений/замыканий это очень удобно.
Замечательно что в новом стандарте для этого есть механизм лямбд! :)

С длинной массива тоже не всегда так однозначно. Например алгоритму бинарного поиска нужно именно отдельное количество элементов. :)

Да, свойства я тоже не люблю - из кода не видно что происходит - тривиальное действие присвоения или что-то "великое и ужасное". :)

Ну и вообще, красота кода сильно зависит от языка и парадигмы, в которой код написан - красивый код на Pascal будет уродским на Python и оба они будут корявыми для С++ и наоборот.
Код в стиле MFC|xWidget покоробит в проекте использующем в основном на Boost/Loki. :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 30.3.2010, 19:56
Сообщение #14


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(Tonal @ 30.3.2010, 11:14) *
Да, свойства я тоже не люблю - из кода не видно что происходит - тривиальное действие присвоения или что-то "великое и ужасное". :)


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

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

Сообщение отредактировал Iron Bug - 30.3.2010, 20:02
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 31.3.2010, 7:44
Сообщение #15


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Цитата(Iron Bug @ 30.3.2010, 23:56) *
Цитата(Tonal @ 30.3.2010, 11:14) *
Да, свойства я тоже не люблю - из кода не видно что происходит - тривиальное действие присвоения или что-то "великое и ужасное". :)

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

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

Цитата(Iron Bug @ 30.3.2010, 23:56) *
а про возвращение результата функции, это элементарно: что должна вернуть функция, если ей задали параметры вне её области определения? ноль? минус один? а где гарантия, что это значение не совпадёт с нормальным возможным значением?

Ну дак давай пойдём до конца: какую сигнатуру должен иметь оператор + или new?
И что они должны делать при возникновении ошибок?
Что и как должен возвращать конструктор? :)

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

Ещё раз, то что хорошо для чистого С или в стиле "С с классами" как в MFC, не обязательно хорошо/удобно/наглядно/быстро/компактно в С++. :)

Цитата(Iron Bug @ 30.3.2010, 23:56) *
а если кидать исключения, то, к примеру, есть у пользователя десять библиотек. в каждой - свои классы исключений, которые каждая функция кидает и надо отлавливать и разгребать это дерьмище.

Ежели ты работаешь с зоопарком библиотек, то кто тебе сможет гарантировать одинаковые стратегии обработки ошибок в них?
Даже если у некоторых стратегия схожа, то нюансы могут отличатся очень сильно.
Например значение кодов возврата и их структура.
Стандарты на сигнатуру - кто-то возвращает код ошибки как результат, а кто-то в параметре.
У кого-то код ошибки получается вовсе вызовом отдельной функции (привет Win32 & Posix)
А кто-то кидает исключение в случае ошибки а в код возврата отдаёт признак наличия предупреждений...

Так что тут всяко придётся каждую отдельным местом оборачивать и обрабатывать.

Цитата(Iron Bug @ 30.3.2010, 23:56) *
Думаете, мелкософт свои стандартные библиотеки зря переписали с "безопасными" вариантами вызовов, где возвращается именно результат выполенения, а указатели или ссылки для параметров передаются в параметрах?

Это про С или про С++?
Ну и думаю, таки зря. :)

Сообщение отредактировал Tonal - 31.3.2010, 7:50
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 31.3.2010, 8:03
Сообщение #16


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Цитата(Iron Bug @ 30.3.2010, 20:56) *
есть у пользователя десять библиотек. в каждой - свои классы исключений, которые каждая функция кидает и надо отлавливать и разгребать это дерьмище. это просто капец, надо заметить!

К сказанному Tonal хочу добавить свои мысли по поводу зоопарка библиотек.
Для интеграции каждой библиотеки наверное лучше написать свои врапперы, которые будут иметь однотипный интерфейс, как по вызову их функционала, так и по сообщению результата своей работы.
Тогда возможно смена низкоуровневых библиотек без изменения кода основной системы. И даже не обязательно прятать весь функционал библиотеки, достаточно описать и предоставить только используемый.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 31.3.2010, 14:30
Сообщение #17


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(Tonal @ 31.3.2010, 9:44) *
Ну и быстродействие может сильно пострадать, особенно при многопоточке. :)

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

Цитата(Tonal @ 31.3.2010, 9:44) *
Ну дак давай пойдём до конца: какую сигнатуру должен иметь оператор + или new?
И что они должны делать при возникновении ошибок?
Что и как должен возвращать конструктор? :)

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

Цитата(Tonal @ 31.3.2010, 9:44) *
Ну и с областью определений - есть функции внешнего интерфейса системы - там функция обязательно должна проверить корректность параметров и сообщить пользователю если что не так.
А есть внутренние - передача в них неверного значения это ошибка кодирования, которую должен отловить компилятор и/или система тестирования.
Для сигнализации о них вполне уместны разного рода ассерты.
Т. е. такие ошибки лучше закрывать более тщательным типизированием.

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

Цитата(Tonal @ 31.3.2010, 9:44) *
Ежели ты работаешь с зоопарком библиотек, то кто тебе сможет гарантировать одинаковые стратегии обработки ошибок в них?
...
Стандарты на сигнатуру - кто-то возвращает код ошибки как результат, а кто-то в параметре.

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

Сообщение отредактировал Iron Bug - 31.3.2010, 14:31
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 1.4.2010, 7:59
Сообщение #18


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Цитата(Iron Bug @ 31.3.2010, 18:30) *
Цитата(Tonal @ 31.3.2010, 9:44) *
Ну и быстродействие может сильно пострадать, особенно при многопоточке. :)

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

Практика - показывает что может всяко получится. :)

Например, пусть класс реализует визуальный элемент - эллипс.
При изменении размера по любой из полуосей его нужно перерисовать.
Наследник - круг, при изменении любой полуоси должен изменять синхронно другую.
Если доступ к полуосям эллипса только через геттеры и сеттеры, которые обеспечивают перерисовку элемента при изменении, то круг неизбежно будет дёргаться. :(
Что приведёт к уродскому интерфейсу.

Другой пример - многопоточный логгер. Пишет в файл, который создаётся в конструкторе и закрывается в деструкторе. Все операции защищает мутексом.
Мы хотим сделать наследника, которрый обеспечивает ротирование логов.
Если мы дадим доступ к мутексу клиентам (прямой или через геттер), то и есть прямая возможность нарушить инвариант класса и получить дедлоки...
А если доступ у мутексу получить нельзя, мы не сможем атомарно переоткрыть файл для организации ротирования.

Причём в обоих этих случаях о каких-либо инлайнах можно забыть - всё на уровне логики. :)

Цитата(Iron Bug @ 31.3.2010, 18:30) *
Цитата(Tonal @ 31.3.2010, 9:44) *
Ну дак давай пойдём до конца: какую сигнатуру должен иметь оператор + или new?
И что они должны делать при возникновении ошибок?
Что и как должен возвращать конструктор? :)

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

Демагогия - набор полемических приёмов вводящих заблуждение.
Я никого в заблуждение не вводил, а предложил довести твоё утверждение до логического конца (явного противоречия) для того, чтобы выявить границы его применимости. :)

Сигнатура операторов может изменятся в довольно широких пределах - см. например operator().
А насчёт экзотики - это опять же именно твоё личное мнение, обусловленное тем, что ты видимо мало работала в областях связанных с массовыми расчётами.

У меня есть знакомая библиотека расчётов для векторной графики написанная на С++ в 94-96гг по мотивам реализаций на Fortran и Pascal.
Где-то в 2003 была попытка перевода её на Delphi - провалилась.
Удобство использования совмещения (перегрузки) операторов оказалась критичным.

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

Так какая же сигнатура должна быть у конструкторов, функции std::strlen, std::string::size(), std::find, std::sort, и остальных? :)

Цитата(Iron Bug @ 31.3.2010, 18:30) *
что значит "сообщить пользователю" когда, к примеру, ты управляешь системой реального времени? ругнуться в свой лог? пользы от этого - ноль. там уже накроется всё оборудование, железяки покорёжит, пока ты пишешь своё "сообщение пользователю". в реальном времени нет возможности пространного ввода-вывода и ассертов. всё решается проще - возврат 0 или 1. через регистры. очень быстро и просто. а типизирование при рантайме не помогает. пришло тебе ошибочное число от аппаратуры и надо его обрабатывать. а как ты заранее предугадаешь, что может прийти?
так что далеко не везде есть возможность развёрнутых сообщений об ошибках и отлова десятков исключений с перебором, чего же там всё-таки попалось. это очень замедляет и усложняет работу софта.

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

0 и 1 через регистры это очень хорошо и быстро, а что делать, если у тебя система работает с серверами баз данных, SOAP и DCOM.
И с одним из них пропала связь или возникла какая другая критическая ошибка?
Критическая здесь - это когда код на данном уровне не может принять решение по её обработке самостоятельно.
Какую 1 или 0 ты будешь через регистры передавать?

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

Ну и "ошибочное число от аппаратуры" придти не может.
Это программа может быть написана так, что она ломается, когда приходят какие-то значения. Например браузер и ОС не должны "упать" если в ответ на запрос www.microsoft.com придёт мусор из свапфайла. :)

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

Цитата(Iron Bug @ 31.3.2010, 18:30) *
Цитата(Tonal @ 31.3.2010, 9:44) *
Ежели ты работаешь с зоопарком библиотек, то кто тебе сможет гарантировать одинаковые стратегии обработки ошибок в них?
...
Стандарты на сигнатуру - кто-то возвращает код ошибки как результат, а кто-то в параметре.

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

Хардварь - не единственная область применения С++, не говоря уже о других языках.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AD
  опции профиля:
сообщение 1.4.2010, 8:33
Сообщение #19


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Могу сказать, что крайне интересная тема получилась! Обоим участвующим в дискуссии большое спасибо. Крайне интересно.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V  < 1 2
Ответить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 27.4.2024, 9:09