Обновить
2

Закон мурра

2
Подписчики
Отправить сообщение
Евгений, вы присмирели, благодарю вас. Таким вы мне нравитесь. :)

Вы мне только что написали:
Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет.


Я вам объяснил идею словами и привел иллюстративный пример кода. Кто знает? Может у вас реально проблемы с пониманием русского языка, действительно, бывают такие люди. Не проблема, я пошел вам навстречу набросав простейший пример в качестве иллюстрации… эээ… «архитектурным решением» называть это слишком громко, пусть будет «рефакторинга».

На это вы выдали в эфир совершенно пустое рассуждение (по смыслу никак не опровергающее и не подтверждающее мой исходный тезис):
Ну не подходит предложенный вами вариант checked_malloc для ряда случаев.
Ну очевидно не подходит, а что должен? Это как-то отменяет саму идею, сказанное в самом первом сообщении, как-то ПРИНЦИПИАЛЬНО запрещает заменять malloc на собственную обертку, или что? В чем проблема-то? Вы это никак не прояснили. И вот вы пишете:
Так как прикажете воспринимать приведенный вами здесь код? Как демонстрацию ваших рассуждений о слоях абстракции? Или как не имеющую отношения к теме иллюстрацию, на которую не стоит обращать внимания?

Хахахаха. Я после этой фразы пытаюсь понять, чего вы добиваетесь. Реально, уже думаю о нехорошем. :)
Если это всего лишь иллюстрация, то когда будет хоть какая-то конкретика, которую можно обсуждать предметно? Или обсуждать останется только вашу тонкую душевную организацию? Я вас уже который комментарий прошу: давайте ближе к теме.

Конкретика?
А какая вам «конкретика» тут нужна?
Ну вот вам написали, не используйте malloc, заменяйте враппером, не замусоривайте код проверками, это решение несколько получше, вон даже на *nix ах без проблем существует аналогичный xmalloc. Даже продемонстрировали примерный код. Куда уж конкретней, Евгений? Приведите пример своей «конкретики»?

Ну и давайте все-таки вернемся к исходной посылке, ибо вы не продемонстрировали какого-либо суждения по существу вопроса.
Исходное предложение до абсурдного простое, я и другие люди не видим что там ВООБЩЕ можно обсуждать. Но вы, видимо, усматриваете какие-то концептуальные проблемы. Вот только рассказать не успели, всё больше требуя от меня. Я ваши требования выполнил.

Жду теперь от вас наконец какого-то разумного опровержения или возражения по существу сказанного в исходном сообщении Подчеркиваю, жалобы на якобы «не конкретику» я уже слышал. Код привел, объяснения дал. На xmalloc сослался. Давайте теперь Евгений вашу здравую аргументацию в ответ на исходный пост.

И у меня к Вам будет предложение. А давайте с вашей помощью поставим на хабре рекорд абсурда и войдем в историю? Ну например, я напишу слова «оператор сравнения», а вы возьмете на себя функцию раз десять написать «давайте конкретику»? Я вам уже и код
if (x) {}
приведу, а вы всё знай будете требовать «конкретики», да «конкретики»? Давайте, а? Чего уж там, гулять так гулять абсурд так абсурд! :)
А давайте я взамен тонны вот этого запредельного школьного хамства:
eao197 Это мощно.

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

eao197 вы не понимаете сложности и широты темы, о которой идет речь

eao197 подозрение о том, что человек не является действующим разработчиком

eao197 покажите, что с вами есть о чем говорить. Что вы не теоретик и архитектор-астронавт.


Отпишу доступным вам языком? Ну-ка брысь курить xmalloc!
Потому что весь этот школьный бред просто не мог бы возникнуть, если бы eao197 хотя бы подозревал о его существовании.

eao197 Ну не подходит предложенный вами вариант checked_malloc для ряда случаев.

Жгите ещще красавчик, я польщен глубиной беседы и вашими «выводами» о иллюстративном коде, набитом за пару минут.
© Ге-ни-аль-но!
Приведите фрагмент моего текста, в котором я «спорю» со своим же предложением вынести все malloc'и, заменив их оберткой, и заявляя что это — неравноценное решение.
Мне-то это очевидно, почему я свободно и высказался.

Но, видимо, это ясно далеко не всем моим оппонентам.

Я тоже искренне недоумеваю, что тут можно не понимать, и о чем тут можно спорить. Но, вот поди ж ты… Можно :-)
то разговаривать, как правило, можно только о личностях, которые до такого додумываются.


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

Евгений, вы не правы во всем вашем выступлении.

Моя тривиальнейшая мысль заключалась в том что в коде желательно писать не напрямую библиотечный malloc() с повсеместной проверкой возвращаемого значения, но его собственную замену/обертку — совершенно верна. Это азы. Это тривиальщина.

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

Вы никак мои слова не опровергли. Вот именно от вас ПО СУЩЕСТВУ никто тут ничего интересного не услышал, впустую потеряв время на вычитку ваших бессмысленных текстов.

И вместо этого, будучи изначально неправым вы:
а) Совершаете атаку на мою личность, занимаясь на Habrahabr выяснением кто якобы © «лучше знает XYZ а кто не знает».
— это очень нехорошо.

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

ORLLY? Тривиальный рефакторинг будет иметь проблемы? Чего? Обертка malloc()'а доставит какие-то практические проблемы? Конечно же это не так, и это ложь, бред, потеря реальности.

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

Вынужден разорвать бессмысленную дискуссию.
Простите, а вы точно к разработке на C или C++ имеете отношение?

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

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


Волшебным?
Сошлитесь на мой фрагмент текста, который для вас непонятен и объясните что там для вас «не предметно» и «волшебно».

Тогда можно будет поговорить о решениях. Пока же есть ощущение


С «ощущениями» и «чувствами» это не ко мне. Я только по технической и архитектурной части.

Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет.


Вы троллите? Что тут может быть «где эта обертка будет работать, а где нет»?

//  Call it "System level"
void* checked_malloc(size_t sz) {
    void* ptr = malloc(sz);
    if (!ptr) {
        exit(YOUR_ERROR_CODE);
    }
    return ptr;
}

// Call it "application level"
int main() {
    void* mymem1 = checked_malloc(YOUR_SIZE1); 
    // Here I fully trust mymem1, no need for checking
    
    void* mymem2 = checked_malloc(YOUR_SIZE2); 
    // Here I fully trust mymem2, no need for checking

    free(mymem2);
    free(mymem1);
    return 0;
}


— Концепция ясна? Это же сверх-тривиально, как тут что-то может быть неясно, что тут требуется иллюстрировать?

Но вместо этого вы говорите про какие-то system-level и application-level.


Это простейшее разбиение на логические слои. Почему вы не можете мне просто написать: «Простите, я не совсем понимаю что вы имеете в виду. Можете мне объяснить?»

А действительно важные вещи, которые влияют и на то, и на другое, это у вас: «уже детали».


Вы не правы.
Решение принимаемое в каждой конкретной программе в случае нехватки памяти не имеет значения (выше это тривиальный вызов exit() ). Мы обсуждаем только узкий контекст необходимости проверки аллокаций памяти. Всё что вне этого контекста — не информативно.
Хочу вас разочаровать. Когда разработчик имеет дело с исключениями вместо кодов возврата, необходимость думать и принимать решение никуда не исчезает. Там есть свои проблемы, например, позаботиться об exception safety.


Вы не правы, исчезает.
В легко достижимом идеале пишется код реализующий только свою задачу, не отвлекаясь на посторонние детали, такие как проверки выделения памяти, реализация exception safety, мультипоточности и проч. Мы не должны смешивать код с посторонними «костылями», выполняющими вспомогательные функции. И этот идеал достижим, именно к нему и следует стремиться, приближаясь по мере возможности. В простейшем случае создается библиотечный набор exception-safe классов, берущий на себя соотв. задачи

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


Зачем вы хамите, переходя на личности? Если у вас отсутствуют аргументы — это не повод к Ad hominem.

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


Вы не правы.
Построить обертку и не использовать malloc() повсеместно (т.е. на application-level уровне) — это именно то, что следует сделать. В чем тут проблема и какие тут «овраги»?

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


Если вас удивляет тезис того что программисты постоянно стремятся снизить себе трудоемкость работы — тогда ответьте (самому себе, мне не надо) на вопрос — зачем создаются библиотеки? Зачем добавляется syntactic shugar?

А вам ведь не составит труда на примерах кода показать, как это оно?


А ведь составит. Мне это затратно, тратить время на вас лично.
Изучайте существующие кодовые базы, а не пользуйтесь уловкой «а вот покажите мне пальцем». В простейшем случае изучите поведение/реализацию operator new в языке c++ (на предмет проверки и exeption) или что происходит в managed-языках.
Там все происходит именно так, как это вам описано — пользователь языка генерально доверяет распределению памяти, нет проверок по месту на то что аллокация произошла/не произошла.

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

Предлагается выполнить эту проверку в одном месте, сразу после вызова malloc(), оформить это как функцию/класс/макро, назначив этому функционалу обязанности «системного слоя» и далее уже писать свой application код в надежде на то что в application-level уровне абстракции память валидна всегда, не может быть ситуации когда память не возвращена, и, соответственно, UB не может возникнуть в принципе.

Вопрос который остался за кадром — это что делать на системном слое сразу после вызова *alloc, если память не вернулась? Все поведение malloc() в оригинале предназначалось для GE-645 с которым работали в Bell Labs и который являлся мультизадачным mainframe с виртуальной памятью. Не исключено что в оригинале можно было подождать :) пока память появится. В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
Это мощно.


Каков должен быть ответ на «мощно» — я не знаю.
Уместно ли делать такие комментарии в технической дискуссии?

Выше я привел логические обоснования, см. деление «app»-level уровень абстракции и «системный», закладываемый при проектировании. Я описал как эта проблема должна быть решена наиболее универсальным и наиболее разумным способом.

Тут нужно думать, работа программиста в этом и состоит.


Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
То что я предложил находится на чуть более высоком уровне, архитектурном. Возможно просто «программист» этой проблемы не видит/не понимает, думая что выходом является поставить везде copy-paste проверки…

Или сейчас уже принято писать программы на C или C++ без всей этой отвлекающей настоящего мастера рутины?


Возможно вас это удивит, но — да. К этому всегда стремятся…
Я думаю стоит начать с этого:
Разыменовывание нулевого указателя в надежде, что
1. Получится сегфолт
2. Компилятор раскроет UB именно так как вы планируете.
Уже является закладыванием и на платформу


В упомянутом выше «application-level» слое абстракции никогда не может возникнуть такой ситуации из-за того что нижележащий «system-level» слой не должен допускать ситуацию с возвратом не выделенной памяти. (Грубо говоря, null в application-level не может вернуться в принципе, на это можно рассчитывать).

Соответственно, начать надо с того, как у вас вообще тогда может возникнуть разыменование нулевого указателя? Никак, возвращаемая вам память всегда валидна. Поэтому развиваемая вами дальше гипотетическая ситуация хоть и имеет ценность, никогда не должна возникнуть на application level уровне абстракций при правильном построении программной системы.

Все остальное в вашем сообщении верно и имеет самостоятельную ценность. Вы совершенно верно сообщаете что рассчитывать на segfault нельзя и это в целом system dependent.
Еще раз уточню, во избежание. Деление «application/user-level» и «system-level» не имеет отношения к уровням защиты и os-терминологии. Это лишь указания на распределение ответственности в нашем, доступном коде, это символическое название слоев абстракции, которые вводятся при проектировании.
Просто если не писать проверку — ваше решение приводит к результату вида «Ну на вот этих платформах упадёт, на вот этих упадёт


Вы не поняли смысл моих слов, видимо воспринимая слова «системные» или «системный код» буквально, system call. А также, можно выдвинуть гипотезу, вы недопонимаете зачем архитектурно выполняется деление между высокоуровневым application-level слоем, и «системным», более низкоуровневым слоем.

Объясняю этот материал «на пальцах».
Высокоуровневый user-level слой ничего не знает ни о каких «платформах». Следовательно, ничего там не может «упасть» и пишется уже «не думая» об этом, не предусматривая «падения». За падения, за ошибки выделения памяти и «платформы» ответственнен именно system-level слой, который и должен по моей прямой рекомендации включать *alloc() функции. Это то, что я порекомендовал выполнить как «обертку».

Соответственно, ваш комментарий хоть и имеет собственную ценность, имеет слабое отношение к сказанному мной и/или не опровергает мой тезис.
Это можно обосновать другими словами тем, что хорошо построенные системы разбиваются на слои. Системному слою абстракции, которому принадлежат функции *alloc() — системную логику и системную «ответственность». Пользовательскому слою абстракции — соответственно, свою упрощенную ответственность.
Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.
Обоснование. Одной только проверки недостаточно, если уж проверять, кроме проверки придется еще и в каждом случае решать что делать дальше, а это означает что придется писать дополнительный код, принимать решения, которые не всегда могут оказаться удачными или безошибочными. Это отвлекает, это время и деньги и дополнительные баги/проблемы. Кроме того, этот код будет кодом решающим системные задачи и по смыслу не имеющим отношения к непосредственному алгоритму/ решаемой задаче, вот почему его быть не должно.
Проблема тут в самих системных *nix функциях *alloc(). Они слишком старые, слишком «системные», и предназначались для системных реалий 70х-80х годов.

Как выглядит более корректное решение?
На мой взгляд лучше не использовать эти системные вызовы напрямую. Использовать или другие библиотечные функции, или написать свои обертки и использовать их. Основная задача — оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.
Заполнять буфер с конца, также как и писать другие алгоритмы «трогающие» адреса памяти с конца на современных архитектурах не следует, потому что современное hardware в основном работает по принципу look-ahead, т.е. кэширует то, что впереди.
Прекрасная работа!
Почему-то после просмотра пришла мысль. Насколько в мире велик рынок сбыта маленьких микро-пылесосиков для программистов и вообще, IT-люда.
Вы совершенно правы в своем замечании. Вероятно, я не вполне ясно выразился. У автора прослеживается следующая мысль: «современные системы содержат очень много транзисторов, очень быстры, но всё равно имеют большой лаг по сравнению с 8-bit компьютером». Моя мысль: «современные системы быстры, всё это верно. Но в них существует пара-тройка буферов связанных с видео-выводом и задерживающих картинку минимум на 2-3 кадра, что даёт встроенный лаг, который портит впечатление при подобном тестировании этих систем по сравнению с 8-bit машиной». Также я хотел сообщить, что это лишь отчасти недоинжиниринг и разрастание функциональности, эти системы обеспечивают по-умолчанию luxury (прозрачность, отсутствие tearing итд). При соответствующей настройке software и hardware можно получать лаг меньше.
Юмор-то еще вот в чем. ОО-системы можно построить во-первых, без всякой прямой ссылки классов друг на друга (или без включения). И во-вторых, построить без явного упоминания имен других классов и их методов. Вообще, в крупных системах к этому и стремятся, но это к несчастью, имеет свою цену…
К этому следует добавить, что наследование вообще не является существенной чертой объектного программирования, вопреки известной попсовой формуле “инкапсуляция+наследование+полиморфизм”. Поэтому объяснять сущность ООП через наследование – это как объяснять назначение самолёта через принцип работы двигателя внутреннего сгорания.

Абсолютно верно! Я это выразил в одном из своих дальнейших опусов под этой статьей.

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

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

Стоит умственно осуществить такую подмену — и все таинственные откровения, aka принципы, например LSP (Liskov substitution principle) становятся ясны по-умолчанию. Кстати, у Гослинга наследование в Java обозначается как extends…

И есть еще одно.
Вообще говоря, реальный ООП-дизайн без полиморфных подстановок aka программирования на уровне интерфейсов aka «без конкретных типов» — это не очень хороший дизайн, поскольку он не расширяем без переписывания (не следует open-closed). Так вот, если бы механизм наследования назывался «версии», а не «наследники» как сейчас, то тогда по-умолчанию было бы понятно, что не надо трогать и постоянно переписывать первую (и обычно единственную и последнюю) версию. И вообще, возникали бы вопросы, для чего-то же нужна вся эта версионность?

Вот так, я считаю, один неудачный термин «наследование» породил непонимание что это такое, и как следствие привел ко всей этой перманетной трагедии…
Я примерно так и мыслил ООП при изучении этой парадигмы.

Есть подозрение, что принцип этот вы поняли, но, как это часто бывает, применить его сумели не вполне…
Только вот меня всегда сбивала мысль о том что в обычной программе множество параллельно взаимодействующих объектов не возможно… всегда существует некая главная процедура которая которая приводит в действие иерархию объектов, причем последовательно.
Это «инерция мышления», идущая, вероятно, от процедурной парадигмы программирования. ОО реализация предполагает что может быть вызван любой публичный метод. Некая сущность извне может вызвать публичные, т.е. заранее выставленные наружу методы этого мира, образуя event-driven систему. А вот вопрос о параллельном вызове сразу двух и более методов объектного мира — это вопрос уже не из области ОО, а из области параллелизма (конкурентного исполнения). ОО не имеет к нему отношения, вопросов этих не решает и их не касается.
А так же я не могу понять как не запутаться меж связей объектов в реально работающей программе и связях классов от которых объекты произведены, от этой двойственности идет кругом голова.
Это получается от того, что вы сейчас представляете полезную программу как размытую по методам различных классов, то есть игнорируете тот факт, что класс должен быть цельной «программой».
ОО-программу надо представлять так. Надо сфокусироваться только на методах «своего» класса. Все остальные классы и их методы вас интересовать не должны. Они выполняют свою работу, и пусть, а вам должно быть достаточно только информации о их названиях, передаваемых параметрах и возвращаемых значениях, пусть это будут «черные ящики», выполняющие свою работу. Самое тяжелое — это научиться психологически «доверять» используемым типам, игнорируя код их методов.
Этот же подход освободит (или по крайней мере релаксирует) от попытки анализа всех связей в программе. ОО-программы часто используют полиморфную подстановку типов, так что какой там будет конкретный тип и какой выполнится код заранее может быть неизвестно.

Поэтому, надо победить самого себя, победить попытку контролировать всё и вся, насильно бить себя по рукам и не лезть вообще всюду. Это особая техника ОО-программистов, нарабатывается не сразу.

Информация

В рейтинге
Не участвует
Откуда
Россия
Зарегистрирован
Активность