Конференция "Прочее" » Раскритикуйте бред С++ника :)
 
  • guav © (27.10.08 12:05) [0]
    Тут аналогия пришла в голову.

    Разица между finally и автоматическими деструкторами примерно такая же как между конструкциями goto и repeat..until/do..while/for.

    Первые (finally и goto) проще, при этом имеют большие возможности.

    Вторые (RAII и конструкции для циклов) делают код более читабельным и навязывают правильную структуру программы.
  • Mystic © (27.10.08 13:47) [1]
    Не совсем. Не зря же в MSVC есть __finally?

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

    Автоматические деструкторы и try/finally взаимозаменяемы. Недостатки каждой конструкции очевидны: try/finally заставляет нас всякий раз повторять однотипный код в секции finally. Автоматические деструкторы заставляют городить нам всякий раз обвертку в виде класса, даже если действие уникально.
  • guav © (27.10.08 14:07) [2]

    > Не зря же в MSVC есть __finally?

    Он для SEH только. В таком виде он чтобы работал и в С без ++. Совместно с деструкторами он не работает. В реальном коде на С++ его не встречал. В более портабельных и соотвествующих стандарту компиляторах finally нет.

    С остальным, всё так, но "недостаток" finally намного меньший недостаток, чем требование писать однотипный код, требование читать однотипный код, и возможность сделать в этом однотипном коде ошибки.

    Для "разового" когда можно использовать локальные классы, тогда код освобождения будет как и с finally там где и выделение, и в отличии от finally, это освобождение будет рядом с местом выделения, а не в конце блока.
  • Mystic © (27.10.08 15:13) [3]
    Я не вижу такой уж большой разницы в этом.

    Такой вариант мне даже более приятен


    hFile := CreateFile(...);
    try
     ...
    finally
     CloseHandle(hFile);
    end;



    чем


    class win_handle_t
    {
    public:
     win_handle_t(HANDLE _handle): handle(_handle) {}

     ~win_handle_t() { if (handle != 0 && handle != INVALID_HANDLE_VALUE) CloseHandle(handle); }
    private:
     HANDLE handle;  
    };

    win_handle_t hFile = CreateFile(...);



    Потому что второй вариант не исчерпывает все возможные операции с дескрипторами. Например, есть еще копирование, присваивание и т.п. Постепенно класс win_handle_t будет разрастаться, при этом размер каждого конкретного метода будет не более чем пару строк. В мозгу у меня ограниченное чисто ячеек памяти, моя практика показывает, что забыть о внутренностях этого класса у меня не получается. Лично мне предпочтительнее когда все выполняется по месту.
  • Alkid (27.10.08 16:58) [4]

    > Mystic ©   (27.10.08 15:13) [3]

    Разница тут в головах, в стиле написания программ :)
    Кроме того, RAII даёт больше, чем отказ от finally.
    Например, возможность иметь объекты-обёртки членами классов серьёзно упрощает написание классов, "держащих" какие-то ресурсы, будь то динамически выделяемые буферы или объекты операционной системы.
    Другой пример - обращение с примитивами синхронизации.
    Это действительно удобно.

    Я, кстати, много программировал и на Дельфи и на С++, так что имею возможность сравнивать.
  • oxffff © (27.10.08 19:57) [5]
    RAII, RAII в Delphi.

    А как же способы прикрутить семантику копирования?

    1. Реализовать интерфейс и работать с ним.
      Или просто задействовать трюк.

    MyClassHelper=class helper for TInterfacedObject
    function HiddenRef:IUnknown;inline;
    end;

    { MyClassHelper }

    function MyClassHelper.HiddenRef: IUnknown;
    begin
    result:=Self;
    end;

    procedure TForm2.Button1Click(Sender: TObject);
    var a:TsomeDerivedFromTInterfacedObject;
    begin
    with TsomeDerivedFromTInterfacedObject.Create do
     begin
     HiddenRef;
     //Do something
     end;
    end;

    2. Прикрутить семантику сбоку.
       http://barrkel.blogspot.com/2008/09/smart-pointers-in-delphi.html
  • TUser © (27.10.08 20:33) [6]

    > Любую программу, с использованием goto можно переписать
    > с использованием циклов.

    Каждый овощ ... (с) Игорь Шевченко

    Вот у меня недавно получился такой код
    for ...
     case ... of
       1
    a:
       2
       3 if ... then goto a

    Смысл в том, что конечный автомат, и в ряде случаев после 3 надо сделать 1. Да, я в курсе, что можно сотворить отдельную процедуру для этого 1. Но это не улучшает читабельность (отдельная процедура на 4 строчки). Да, я знаю, что можно скопипастить код из 1 в 3. Но это не улучшает отладку ошибок, и ту же читабельность. Да, я понимаю, что можно наплодить стейтов, но тут уж про понябельность придеться забыть совсем. И я также в курсе, что любой нормальный программист сумеет разорбраться в коде из 10-15 строчек, даже если там есть один goto. нне развалится. Тем более, что разбираться кроме меня вообще никто не будет. Особенно, если он привык к break, continue, exit, raise.

    Правда, справедливости ради, сделать хотелось срочно, и на архитектуру я, к сожалению, забил. Знаю, что низя. Но надо было.
  • jack128_ (27.10.08 22:41) [7]

    > что разбираться кроме меня вообще никто не будет

    в таком случае неважно как ты пишешь.
  • Riply © (27.10.08 23:03) [8]
    > [7] jack128_   (27.10.08 22:41)
    >> что разбираться кроме меня вообще никто не будет

    > в таком случае неважно как ты пишешь.

    А себя не жалко, когда через пару месяцев будешь разбирать свои же исходники ?
    :)
  • guav © (27.10.08 23:27) [9]
    Mystic ©   (27.10.08 15:13) [3]

    > Например, есть еще копирование, присваивание и т.п

    Не хочешь - не реализуй, а запрети. Одна строчка - наследование от noncopyable - и уже некопируемое владение, которое при этом более широко применимо чем finally.
    Для владения мьютексом запрет копирования - самое разумное решение.

    Alkid   (27.10.08 16:58) [4]
    +1

    oxffff ©   (27.10.08 19:57) [5]
    Хакер :)
    Хотя вот в TXMLDocument тоже такое предлагают
    When TXMLDocument is created without an Owner, it behaves like an interfaced object. That is, when all references to its interface are released, the TXMLDocument instance is automatically freed.

    TUser ©   (27.10.08 20:33) [6]
    Всё правильно. goto иногда бывает к месту, и без него лишнее писать приходится. Именно это я имел ввиду под "большими возможностями goto". Ключевой момент здесь "иногда". Настолько редко, что случаи можно запомнить. Точно так же "иногда" finally уместнее RAII.
    Кстати, в С/С++ case допускает проваливания, но несмотря на возможную  полезность этой фичи (Duff's device :) от неё больше вреда чем пользы.
  • Германн © (28.10.08 00:23) [10]

    > Riply ©   (27.10.08 23:03) [8]
    >
    > > [7] jack128_   (27.10.08 22:41)
    > >> что разбираться кроме меня вообще никто не будет
    >
    > > в таком случае неважно как ты пишешь.
    >
    > А себя не жалко, когда через пару месяцев будешь разбирать
    > свои же исходники ?
    > :)

    +1
  • Mystic © (28.10.08 02:09) [11]
    > Не хочешь - не реализуй, а запрети. Одна строчка - наследование
    > от noncopyable - и уже некопируемое владение, которое при
    > этом более широко применимо чем finally.


    Это сужает область работу с дескриптором. Например, я могу спокойно хранить дескрипторы полях Tag, Data или внутри TList. Посылать его в wParam или lParam. Передавать как параметр в ThreadFunc. Еще могут быть интерфейсы и методы, которые принимают LPHANDLE.

    Есть еще и то соображение, что нельзя рассматривать фичи отдельно от окружения. У Delphi свой стиль, свои приемы. У C++ свои. Если в Delphi нормальная практика хранить в полях Tag, Data указатели на объекты, то в случае C++ применяют другие подходы. Т. е. затрагивая один аспект мы задеваем одну костяшку домино, которая затрагивает другие.
  • oxffff © (28.10.08 09:10) [12]

    > guav ©   (27.10.08 23:27) [9]
    > oxffff ©   (27.10.08 19:57) [5]
    > Хакер :)
    > Хотя вот в TXMLDocument тоже такое предлагают
    > When TXMLDocument is created without an Owner, it behaves
    > like an interfaced object. That is, when all references
    > to its interface are released, the TXMLDocument instance
    > is automatically freed.


    В TXMLDocument просто предлагают предлагать менять семантику копирования при создании компонента. Однако к наделению самой семантикой копирования это не имеет отношения.

    В С++ автоматический вызов декструктора приводит к тут же к необходимости

    -перегрузке оператора присваивания.
    -созданию конструктора копий.

    По причине того, что другие базовые типы "прямолинейны" (лишены семантики копирования). Это строки, интерфейсы, массивы, варианты.

    В Delphi необходимости в автоматическом десктрукторе нет, поскольку всегда предлагается деструтор(финализатор) по умолчанию.  
    И строки, интерфейсы, массивы, варианты более гибкие.
    Так что RAII в Delphi был с рождения.

    P.S. Это не камень в огород С++. Каждый подход имеет свои плюсы и минусы.
  • guav © (28.10.08 10:05) [13]
    Mystic ©   (28.10.08 02:09) [11]

    > Это сужает область работу с дескриптором. Например, я могу
    > спокойно хранить дескрипторы полях Tag, Data или внутри
    > TList. Посылать его в wParam или lParam. Передавать как
    > параметр в ThreadFunc. Еще могут быть интерфейсы и методы,
    >  которые принимают LPHANDLE.

    В текущей версии С++ для хранения внутри list и передачи в ThreadFunc прийдётся реализовать "честное" копирование или использовать враппер типа shared_ptr. В новой версии стандарта добавятся rValue ссылки и std::move - можно будет помещать в контейнеры классы, не поддеживающие копирования, досаточночно будет поддержки перемещения.

    Посылать в wParam или lParam - надо просто открыть вклассе доступ к самому дескриптору, сделав поле public, или реализовав метод get или оператор приведения к дескриптору. Помещение в Tag, Data это не путь С++, но если очень хочется, то также как и посылать в wParam или lParam.


    > Есть еще и то соображение, что нельзя рассматривать фичи
    > отдельно от окружения. У Delphi свой стиль, свои приемы.
    >  У C++ свои. Если в Delphi нормальная практика хранить в
    > полях Tag, Data указатели на объекты, то в случае C++ применяют
    > другие подходы. Т. е. затрагивая один аспект мы задеваем
    > одну костяшку домино, которая затрагивает другие.

    Да, это так. Я и не предлагаю использовать RAII в Delphi. я вообще ничего не предлагаю, просто поделился впечатлением после некоторого опыта в С++ : RAII работает лучше finally, и finally в C++ не стали делать не зря.

    oxffff ©   (28.10.08 09:10) [12]

    > Однако к наделению самой семантикой копирования это не имеет
    > отношения.

    На самом деле, в Delphi все указанные типы с автоматической финализацией наделены семантикой копирования - это подсчёт ссылок (кроме для Variant может быть и копирование по значению в зависимости от содержимого).

    Есть ещё типы с запретом копирования. Это File.


    > И строки, интерфейсы, массивы, варианты более гибкие.

    В Delphi они менее гибкие, за счёт "встраивание магии в компилятор", что делает их более удобными, но к RAII это не относится.


    > Так что RAII в Delphi был с рождения.

    Был. Но не для наследников TObject. Почему так сделано ?
  • oxffff © (28.10.08 10:46) [14]

    > guav ©   (28.10.08 10:05) [13]

    > oxffff ©   (28.10.08 09:10) [12]
    >
    > > Однако к наделению самой семантикой копирования это не
    > имеет
    > > отношения.
    >
    > На самом деле, в Delphi все указанные типы с автоматической
    > финализацией наделены семантикой копирования - это подсчёт
    > ссылок (кроме для Variant может быть и копирование по значению
    > в зависимости от содержимого).
    >

    Я тебе про Фому, а ты мне про Ерему. :)

    Твой пост.


    > Хотя вот в TXMLDocument тоже такое предлагают
    > When TXMLDocument is created without an Owner, it behaves
    > like an interfaced object. That is, when all references
    > to its interface are released, the TXMLDocument instance
    > is automatically freed.


    у меня вопрос какое отношение имеет Owner, к процессу наделения семантикой копирования? Никакого. Зато он имеет отношение к реализации смысла самой самантики копирования
    На что я тебе и указал. По твоим словам получается что owner каким то образом заставляет компилятор вставлять _НаВасЕстьСсылка, _ОтВасотказались.

    > И строки, интерфейсы, массивы, варианты более гибкие.

    > В Delphi они менее гибкие, за счёт "встраивание магии в
    > компилятор", что делает их более удобными, но к RAII это
    > не относится.


    Очень интересно. А что в С++ для class и struct автоматические вызовы деструктора, оператора присваивания, конструктора копий происходят сами сабой, без участия компилятора? Не подскажешь как?

    > > Так что RAII в Delphi был с рождения.
    >
    > Был. Но не для наследников TObject. Почему так сделано ?
    >


    А ты что не в курсе? :)
    Что в Delphi объекты размещены не стеке, а в куче(по умолчанию).
    А для копирования объеков кучи у С++ и Delphi семантика копирования идентичная. Копируется указатель. Это так называемая идентичность, но не эквивалентность.
    Все остальная семантика копирования объектов кучи , оборачивается в С++ за счет стековых классов оберток.
    Чую holywar назревает.
  • Alkid (28.10.08 10:57) [15]

    > oxffff ©   (28.10.08 09:10) [12]
    > Так что RAII в Delphi был с рождения.

    Не так. Правильно так - RAII изначально реализован в частном видел для ряда типов. Реализация его для других типов требует э-э-э-.... стратегии НЕПРЯМЫХ действий (т.е. через Ж) :)

    Это мне напоминает дизайн языка D, где они попытались для каждого частного случая внедрить прямую поддержку языка. В С++ RAII (как и многое другое) получилось как комбинация нескольких базовых механизмов языка, по сути ортогональных друг другу.

    ИМХО, попытка внедрять прямую поддержку "фитч" на все случаи жизни - тупиковый пусть развития языка. Правильнее разрабатывать мощный базис из нескольких фундаментальных механизмов с высокой степенью ортогональности и возможностью их комбинировать. Ну это так, оффтопик в сторону дизайна языков :)
  • oxffff © (28.10.08 11:13) [16]

    > Alkid   (28.10.08 10:57) [15]


    А что уже при копировании указателя на объект в С++ используется семантика копирования?
    Почему прикручивание smart pointer в C++ также не назвать через Ж?  Прошу  ответить честно. Уж не политика ли это двойных стандартов?
    :)
  • guav © (28.10.08 11:43) [17]
    > Очень интересно. А что в С++ для class и struct автоматические
    > вызовы деструктора, оператора присваивания, конструктора
    > копий происходят сами сабой, без участия компилятора? Не
    > подскажешь как?

    Я имел в виду встраивание в компилятор поддержки для IUnknown, поддержки для сообщений Windows, поддержки IDispatch при невозможности дать пользователю реализовать нужную семантику. Т.е.


    > [15] Alkid   (28.10.08 10:57)
    > попытались для каждого частного случая внедрить прямую поддержку языка.

    вместо введения

    > [15] Alkid   (28.10.08 10:57)
    > нескольких базовых механизмов языка
  • Alkid (28.10.08 12:20) [18]

    > oxffff ©   (28.10.08 11:13) [16]

    Собственно говоря, разница тут такая: в С++ изначально определялся ряд фундаментальных механизмов. В частности, в рамках нашего спора, нас интересуют следующие:
    1. Шаблоны.
    2. Пользовательские типы с возможностью создавать статические, динамические и автоматические экземпляры, а так же непосредственно включать их как поля других пользовательских типов.

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

    В Дельфи части этих механизмов нет. В частности нет возможности создавать объекты классов кроме как в куче (динамические объёкты). Так же нет шаблонов, правда в .NET-версии Дельфи появились дженерики.

    Что это означает? Та комбинация общих механизмов, которая в С++ давала возможность реализовать RAII, в дельфи не реализуема. Что бы исправить эту ситуацию, в самом языке Дельфи начинают реализовывать ПРЯМУЮ поддержку этих специальных механизмов (RAII) для некоторых избранных типов, оставляя "за бортом" пользовательские типы.

    Что остаётся делать программистам, что бы обойти эту неприятную ситуацию с пользовательским типами? Им приходится использовать ещё один специализированный механизм, предназначенный для чего-то другого. А именно - ПРЯМУЮ поддержу технологии COM языком Дельфи. Т.е. использовать УЗКОСПЕЦИАЛИЗИРОВАННЫЙ механизм за пределом его специализации. Это и есть "через Ж", если ты хочешь строгого определения :)

    Касаемо двойных стандартов, холиваров и т.п: хочу пояснить свою позицию по поводу С++ и Дельфи. Я считаю, что оба языка серьёзно устарели. С++ остался слишком низкоуровневым. Тяжкое наследие С, которое они упорно тащат за собой, делает его монстроидальным и переусложнённым, а жёсткая дисциплина модифицирования стандарта делает язык отстающим от реалий нашего дня. С другой стороны, в Дельфи проглядывается отсутствие вообще какой-либо дисциплины нововведений, что так же делает его монстроидальным и весь его дизайн прекрасно описывается простой латинской фразой - "ad hoc".

    Я считаю, что оба языка исчерпали себя. Дельфи исчерпал себя в бОльшей степени просто в силу готовности конкурирующих языков занять его место. Хорошей замены для С++ в тех нишах, где он держит свои позиции просто нет. Это печально.
  • Mystic © (28.10.08 13:41) [19]
    > guav ©   (28.10.08 10:05) [13]

    Все проблемы решаемы, но я просто хотел показать, что они есть. Ну и перечислил первое, что пришло в голову. Тем более, что надо реализовывать не get(), а release(). И это один из самых простых примеров. И новый стандарт покуда не везде реализован, правда?

    Вторым минусом является то, что для того, чтобы понять такой код, надо знать не только WinAPI, но и как минимум просмотреть реализацию класса-обвертки.  Я не думаю, что этот класс сохранится в таком минимальном виде, скорее всего в него будут добавляться алиасы для методов вроде DuplicateHandle. Что приводит к необходимости потом мучительно вспоминать, что реализовано в этом классе, а что нет. Так что небо не такое уж и безоблачное, лично я при программировании на С++ стараюсь таких обверток избегать. Если надо активно использовать WinAPI, или какую-нить C-шную библиотеку, то этот фрагмент я пишу в C-подобном стиле, а потом оборачиваю его в некоторый класс. Такой уж мой вкус.

    В крайнем случае try..finally легко реализовать в C++ и через


    try
    {
     ...
    }

    catch(...);
    {
     ...
     throw;
    }


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

  • Alkid (28.10.08 15:42) [20]

    > Mystic ©   (28.10.08 13:41) [19]
    > try
    > {
    >  ...
    > }
    > catch(...);
    > {
    >  ...
    >  throw;
    > }

    Э.... поясни, как это работать должно?
  • guav © (28.10.08 15:47) [21]

    > И новый стандарт покуда не везде реализован, правда?

    В gcc частично есть, может и rvrefs есть. В билдере тоже, но rvrefs нет.


    > В крайнем случае try..finally легко реализовать в C++ и
    > через

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

    Я бы предпочёл через локаьный класс:
    {
     HANDLE h = CreateFile();
     struct guard_t {
       ~guard_t() {
         ::CloseHandle(h);
       }

       HANDLE & h;
     } guard = {h};
     ...
    }


    Ну или по сути то же, но в красивой упаковке:
    {
     HANDLE h = CreateFile();
     BOOST_SCOPE_EXIT((h)) {
       ::CloseHandle(h);
     }
    BOOST_SCOPE_EXIT_END
     ...
    }

  • Mystic © (28.10.08 17:01) [22]
    > guav ©   (28.10.08 15:47) [21]

    Вкусы у нас разные, ну не могу я такую упаковку назвать красивой. Мне проще написать один раз finally, чем созерцать такие макросы и с ними разбираться. Когда я сам для себя начинаю мутить в таком стиле, я со временем бросаю это занятие и переписываю проект попроще. Что, что написано через try/finally на Delphi я и через пару лет превосходно пойму и в чужой код разберу.

    Была бы конструкция вида


    HANDLE h = CreateFile(..) !! if (h != INVALID_HANDLE_VALUE) CloseHandle(h);



    или


     hFile := CreateFile cleanup if hFile != INVALID_HANDLE_VALUE CloseHandle(hFile);


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

    P. S. Тем более в моей версии boost, BOOST_SCOPE_EXIT отсутствует. Новую версию качать в лом. Как написать такое самому я не знаю: надо как-то определить тип h. Очень может быть, что будут еще какие-то особенности, которые надо знать.
  • guav © (28.10.08 17:13) [23]
    > Была бы конструкция вида

    Похожее будет возможно в С++0х
    Сейчас и конкретно для HANDLE, можно воспользоваться тем, что это typedef для void* :
    HANDLE h = CreateFile(..);  boost::shared_ptr<void> s(h, ::CloseHandle);


    Если не пользоватся этим, то
    HANDLE h = CreateFile(..);  boost::shared_ptr<void> s(0, boost::bind(::CloseHandle, h));



    Что С++ навязывает помещение ресурмсов в классы, это хорошо. Есть неудобство с необходимостью врапить апи, ну это из-за того что АПИ не на С++ а не из-за того что в С++ плохо.
  • Mystic © (28.10.08 17:58) [24]
    Итого, мы получили код, который почти не отличается от кода try/finally. Главные отличия:

    1) код finally помещается в конце блока, а твой код располагается непосредственно после инициализатора

    2) вместо слово finally написана интуитивно понятная последовательность boost::shared_ptr<void> s(...);

    Само утверждение о том, что код очистки должен быть привязан к коду инициализации не бесспорно. Конечно, если предположить, что программист будет искать memory leak, то это будет, безусловно, полезно. Но если предположить, что я хочу найти логический баг, то окажется, что мне вовсе не обязательно акцентировать свое внимание на коде освобождения ресурсов. Утечки нету, значит где-то освобождается, гораздо важнее посмотреть на то, что читается/пишется в файл и как. И хорошо, что код финализации спрятан в конце и не мозолит глаза.

    По второму пункту можно определить макрос

    #define DEFERRED_CLEAN(func, arg) boost::shared_ptr<void> s(0, boost::bind((func), (arg)));



    Но если я хочу вот так:


     HANDLE h = INVALID_HANDLE_VALUE;
     // Тут нам файл может и не понадобится
     // Но если понадобился, что мы его освободим:
     if (h != INVALID_HANDLE_VALUE) CloseHandle(h); // Подразумевается finally



    Да, я могу описать функцию SafeCloseHandle, то в целом это ребусы на ровном месте. Вот из-за неудобства врапить API, если мне надо что-то написать на этом API, я как раз и не использую такие врапперы. Что касается Delphi, то одной из целей был свободный доступ к WinAPI.

    Преимущество простого рабоче-крестьянского решения в том, что ты написал try/finally и забыл. Ну однотипный код, но его одна строка. А тут мы уже уйму времени обсуждаем возможные ньюансы реализации
  • oxffff © (28.10.08 19:40) [25]

    > Alkid   (28.10.08 12:20) [18]
    >
    > > oxffff ©   (28.10.08 11:13) [16]
    >
    > Собственно говоря, разница тут такая: в С++ изначально определялся
    > ряд фундаментальных механизмов. В частности, в рамках нашего
    > спора, нас интересуют следующие:
    > 1. Шаблоны.
    > 2. Пользовательские типы с возможностью создавать статические,
    >  динамические и автоматические экземпляры, а так же непосредственно
    > включать их как поля других пользовательских типов.
    >
    > Каждый из этих встроенных в язык механизмов по отдельности
    > не даёт RAII, смартпоинтеров и т.п. Каждый из них - это
    > механизм общего назначения, но их комбинация породила ряд
    > специализированных мезанизмов, в частности RAII со смартпоинтерами.
    >
    >
    > В Дельфи части этих механизмов нет. В частности нет возможности
    > создавать объекты классов кроме как в куче (динамические
    > объёкты). Так же нет шаблонов, правда в .NET-версии Дельфи
    > появились дженерики.
    >
    > Что это означает? Та комбинация общих механизмов, которая
    > в С++ давала возможность реализовать RAII, в дельфи не реализуема.
    >  Что бы исправить эту ситуацию, в самом языке Дельфи начинают
    > реализовывать ПРЯМУЮ поддержку этих специальных механизмов
    > (RAII) для некоторых избранных типов, оставляя "за бортом"
    > пользовательские типы.


    Начнем с того, что generics и перегрузка операторов уже есть в Win32.
    см.  строку в самом конце oxffff ©   (27.10.08 19:57) [5].

    Однако интересно, что использование одних средств предназначенных для других целей для латания дыр в одном языке выдается за преимущество, а использование средств в других языках для реализации нужной фукнциональности выдается за Ж. Это двойные стандарты.

    P.S. Интересно почему вся .NET де факто слизана с идеалогии Delphi. :)
  • Alkid (28.10.08 21:34) [26]

    > oxffff ©   (28.10.08 19:40) [25]
    > Однако интересно, что использование одних средств предназначенных
    > для других целей для латания дыр в одном языке выдается
    > за преимущество, а использование средств в других языках
    > для реализации нужной фукнциональности выдается за Ж. Это
    > двойные стандарты.

    Применение конструкторов и деструкторов и автоматических объектов для обеспечения RAII - это применение их по самому прямому назначению. Так что никаких двойных стандартов :)


    > P.S. Интересно почему вся .NET де факто слизана с идеалогии
    > Delphi. :)

    Потому что Хейлсберг :) Кстати, какие версии Дельфи ещё он делал,а какие уже без него?

    Касаемо слизанности - что там такого слизано в первой версии с Дельфи, а не с Джавы? Делегаты - это да, это круто. А что ещё? И вообще .net уже своим путём идёт. Посмотри на C# 3.0 с его явным трендом в сторону ФП. И дисциплины дизайна там явно больше.  Сравнивать это с Дельфи уже просто смешно.
  • oxffff © (28.10.08 22:01) [27]

    > Alkid   (28.10.08 21:34) [26]
    >
    > > oxffff ©   (28.10.08 19:40) [25]
    > > Однако интересно, что использование одних средств предназначенных
    >
    > > для других целей для латания дыр в одном языке выдается
    >
    > > за преимущество, а использование средств в других языках
    >
    > > для реализации нужной фукнциональности выдается за Ж.
    > Это
    > > двойные стандарты.
    >
    > Применение конструкторов и деструкторов и автоматических
    > объектов для обеспечения RAII - это применение их по самому
    > прямому назначению. Так что никаких двойных стандартов :
    > )


    А двойной стандарт состоит вот в чем.
    То есть использования типа A в качестве обертки типа Б для наделения семантики входа/выхода в одном языке считается нормальным в одном языке.
    Однако использование типа А в качестве обертки типа Б в другом языке для сходной функциональности считается уже действием Ж.


    >
    >
    > > P.S. Интересно почему вся .NET де факто слизана с идеалогии
    >
    > > Delphi. :)
    >
    > Потому что Хейлсберг :) Кстати, какие версии Дельфи ещё
    > он делал,а какие уже без него?
    >
    > Касаемо слизанности - что там такого слизано в первой версии
    > с Дельфи, а не с Джавы? Делегаты - это да, это круто. А
    > что ещё? И вообще .net уже своим путём идёт. Посмотри на
    > C# 3.0 с его явным трендом в сторону ФП. И дисциплины дизайна
    > там явно больше.  Сравнивать это с Дельфи уже просто смешно.
    >


    В .NET у value type типа нет финализатора.
    В .NET у объекта управляемой кучи финализатор присутствует.
    Ввиду GC накладывать семантику копирования на сссылки нет необходимости.

    Что касаемо C# 3.0, то изменений в IL никаких не сделано начиная с NET 2.0. Сейчас сам Хейлсберг склоняется к декларативному программированию.

    Ну дык анонимные методы уже Delphi 2009 win32 появились.
    Пока нет лямбда выражений, но возможно добавят, и тогда уже аналог LINQ вполне вероятен.
  • Alkid (28.10.08 22:14) [28]

    > oxffff ©   (28.10.08 22:01) [27]
    >
    > А двойной стандарт состоит вот в чем.
    > То есть использования типа A в качестве обертки типа Б для
    > наделения семантики входа/выхода в одном языке считается
    > нормальным в одном языке.
    > Однако использование типа А в качестве обертки типа Б в
    > другом языке для сходной функциональности считается уже
    > действием Ж.

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


    > Ввиду GC накладывать семантику копирования на сссылки нет
    > необходимости.

    Однако же от идиомы RAII даже .net и C# не убежали, только реализовали её по-своему - IDisposable и 'using statement'. Да и всё равно иногда требуется реализовывать deep copy для объекта.


    > Сейчас сам Хейлсберг склоняется к декларативному программированию.

    И правильно делает.


    > Ну дык анонимные методы уже Delphi 2009 win32 появились.
    > Пока нет лямбда выражений, но возможно добавят, и тогда
    > уже аналог LINQ вполне вероятен.

    Ну это всё же не слизывание дотнета с дельфи :) Скорее наоборот.
  • guav © (28.10.08 22:23) [29]

    > Но если предположить, что я хочу найти логический баг, то
    > окажется, что мне вовсе не обязательно акцентировать свое
    > внимание на коде освобождения ресурсов. Утечки нету, значит
    > где-то освобождается, гораздо важнее посмотреть на то, что
    > читается/пишется в файл и как. И хорошо, что код финализации
    > спрятан в конце и не мозолит глаза.

    Именно. finally мозолит глаза, как и явный код удаления по месту.
    finally больше, т.к. оно добавляет ещё и лишние оступы. Для этого и есть RAII, чтобы убрать всё управление ресурсами из кода, оставив только логику, сделать шаг от boilerplate programming к literate programming.


    > Преимущество простого рабоче-крестьянского решения в том,
    >  что ты написал try/finally и забыл.

    ..написать его в другом месте :)
  • guav © (28.10.08 22:30) [30]

    > IDisposable и 'using statement'.

    это точно RAII ?

    Вот как тут пофиксить Block 2 и Block 3 :
    using System;
    using System.Text;

    namespace ConsoleApplication1
    {
       delegate void Del();

       class ExpensiveThing : IDisposable
       {
           public ExpensiveThing()
           {
               Console.WriteLine("Created");
           }


           public void DoSomething()
           {
               Console.WriteLine("Doing something");
           }


           void IDisposable.Dispose()
           {
               Console.WriteLine("Deleted");
           }

       }

       class ResouseUser
       {
           public ResouseUser()
           {
               e = new ExpensiveThing();
           }

           ExpensiveThing e;
       }

       class Program
       {
           static Del ReturnsDelegate()
           {
               using (ExpensiveThing e = new ExpensiveThing())
               {
                   return delegate() { e.DoSomething(); }
    ;
               }
           }

           static void Main(string[] args)
           {
               using (new ExpensiveThing())
               {
               }

               Console.WriteLine("Block 1 closed");
               
               {
                   new ResouseUser();
               }

               Console.WriteLine("Block 2 closed");

               
               {
                   Del d = ReturnsDelegate();
                   d();
               }

               Console.WriteLine("Block 3 closed");
           }
       }
    }

  • Alkid (28.10.08 22:39) [31]

    > guav ©   (28.10.08 22:30) [30]
    > это точно RAII ?

    Это "RAII в стиле Дельфи" :)

    Block 2 можно пофиксить путём делания ResourceuUser так же Disposable.
    Block 3 -  ну это надо что-то типа auto_ptr, который так же будет Disposable :)))))

    Короче, "те же яйца, вид сбоку" :)
  • Mystic © (29.10.08 13:56) [32]
    > ..забыл написать его в другом месте :)

    По окончании работы мне тут же скажут о наличии memory leak. И я спокойно пофикшу эту ошибку. И забуду окончательно.

    > Именно. finally мозолит глаза, как и явный код удаления
    > по месту.


    Я привык к компромиссам и далек от мысли поиска универсальных решений. Мне не сложно набрать пару дополнительных строчек и они мне не будут мозолить глаза. Особенно если это избавит меня от необходимости придумывать некий нетривиальный абстрактный механизм или изучать шаблоны boost. Автоматический вызов деструкторов это хорошо, но как только начинают происходить навороты вокруг этого, использовать этот трюк для реализации некой абстрактной фишки, то возвращаясь через пару месяцев я и сам начинаю забывать что я хотел и как оно биса работает. Спагетти намного круче goto.
  • Mystic © (29.10.08 18:37) [33]
    > Alkid   (28.10.08 15:42) [20]

    Ловим все исключения, выполняем общее действие и кидаем исключение дальше
  • Alkid (29.10.08 18:53) [34]

    > Mystic ©   (29.10.08 18:37) [33]

    А если у меня код в try-секции не произвёл исключения? Тогда catch(...) { ...; throw; } не отработает
  • Mystic © (30.10.08 00:00) [35]

    > Alkid   (29.10.08 18:53) [34]


    Ага, в детстве вбил себе эквивалентность конструкций, теперь постоянно выпускаю из вида.
  • oxffff © (30.10.08 21:58) [36]

    > Alkid   (28.10.08 22:14) [28]
    >
    > > oxffff ©   (28.10.08 22:01) [27]
    > >
    > > А двойной стандарт состоит вот в чем.
    > > То есть использования типа A в качестве обертки типа Б
    > для
    > > наделения семантики входа/выхода в одном языке считается
    >
    > > нормальным в одном языке.
    > > Однако использование типа А в качестве обертки типа Б
    > в
    > > другом языке для сходной функциональности считается уже
    >
    > > действием Ж.
    >
    > Формулируя вопрос таким образом ты упускаешь существенное
    > различие, о котором я говорил. А именно то, каким образом
    > формируется и используется  тип  А в С++ и в Дельфи. А через
    > этот пример очень хорошо видна разница в философии дизайна
    > этих языков.
    >
    >


    Разница в том что программист С++ лезет в идеалогию другого языка со своим самоваром, пытаясь мыслить привычными для него категориями.
    И больше всего, что меня поражает, пытаясь даже давать свои субъективные оценки. :)

    Допустим, если есть некоторый Тип А, к которому я бы хотел добавить некоторую семантику копирования. В С++ для этого "выкрутились" (это ключевой термин) со smart pointer. Идеалогия C++ простая перенести ответственность и семантику за копирование на Тип контейнер (struct, class).
    Того же самого я могу добиться в Delphi просто:

    1. Объявив новый класс с реализацией интерфейса. Меня честно не будет напрягать дополнительные 4 байта на VMT объекта + 4 байта на VMT таблицу интерфейса. Либо на семантике интерфейса
    2. Объявить дочерний класс от TCustomVariant. Либо на семантике variant.

    НО!!! В насколько в Delphi это выразительно. Сколько в С++
    нужно написать абракадабра.


    > > Ввиду GC накладывать семантику копирования на сссылки
    > нет
    > > необходимости.
    >
    > Однако же от идиомы RAII даже .net и C# не убежали, только
    > реализовали её по-своему - IDisposable и 'using statement'.
    >  Да и всё равно иногда требуется реализовывать deep copy
    > для объекта.


    Ну это не панацея. И задача IDisposable не в RAII, а в детерминированном освобождении ресурсов. И 'using statement' - это просто облегчение.
    Однако!!! Если есть две ссылки на объект по одной из них вызовем IDisposable, но вторая то будет висеть на него, и у нее нет способа узнать, что существующий объект уже в невалидном состоянии.

    Однако ни тот ни другой подход языков С++ и Delphi не позволяет решить проблему "зависания объектов" при зацикливания(ссылки на друг друга) объектов кучи без ссылок из корней программы. Что доступно для GC.

    Delphi, C++, C#, ASM, ABAP ... Лучше знать их все. :)
  • Alkid (31.10.08 11:14) [37]

    > oxffff ©   (30.10.08 21:58) [36]
    > Разница в том что программист С++ лезет в идеалогию другого
    > языка со своим самоваром, пытаясь мыслить привычными для
    > него категориями.

    Понимаешь, я не "программист С++". Я просто программист. Кстати, если брать по брутто-строкам кода, а Дельфи, я наверное, больше написал. А так я на Дельфи, на С++, на C# писал - это только по работе. Для души ещё много на чём. Сейчас вообще Prolog вдумчиво раскуриваю. Это я к чему - этот спор я использовал как повод начать разговор о дизайне языков, а не для того, что бы доказать, что "ваш Дельфи - г-но" :)


    > НО!!! В насколько в Delphi это выразительно. Сколько в С++
    > нужно написать абракадабра.

    А вот ЭТО и есть - субъективная оценка :-)
    Ну это так, к слову...


    > Допустим, если есть некоторый Тип А, к которому я бы хотел
    > добавить некоторую семантику копирования. В С++ для этого
    > "выкрутились" (это ключевой термин) со smart pointer. Идеалогия
    > C++ простая перенести ответственность и семантику за копирование
    > на Тип контейнер (struct, class).

    Ты не прав. Во-первых, ты зациклился на семантике копирования, хотя RAII немного об этом. Во-вторых, сматр-поинтерны - это вообще механизм контроля доступа к объетку, который помимо RAII, семантики копировани и т.п. может делать много разных интересных вещений. Например - обеспечивать синхронизацию. Вообще, кстати, для меня ещё не до конца ясно взаимоотношение понятий "RAII" и smart-pointer. Надо обдумать :)


    > И задача IDisposable не в RAII, а в детерминированном освобождении
    > ресурсов. И 'using statement' - это просто облегчение.

    Упс... А я вообще-то до этого момента искренне полагал, что RAII как раз имеет отношение к детерминироанную освобождению ресурсов
    =-o


    > Delphi, C++, C#, ASM, ABAP ... Лучше знать их все. :)

    Абсолютно согласен! :)


    > Однако ни тот ни другой подход языков С++ и Delphi не позволяет
    > решить проблему "зависания объектов" при зацикливания(ссылки
    > на друг друга) объектов кучи без ссылок из корней программы.
    >  Что доступно для GC.

    На самом деле комбинация подходов эту проблему может решить. Тут дело вот в чём -  если писать простые классы-обёртки для внешних ресурсов, то никаких циклических ссылок они порождать не будут. Иными словами, для вызова Dispose можно применить семантику подсчёта ссылок a-la boost::shared_ptr, а для освобождения памяти - трассировку ссылок от корней, т.е. GC.
  • guav © (31.10.08 11:47) [38]
    Mystic ©   (30.10.08 00:00) [35]

    > Ага, в детстве вбил себе эквивалентность конструкций, теперь
    > постоянно выпускаю из вида.

    Оно будет похоже на эквивалентность, если скопипастить после конструкции catch(...) { ..; throw; } код из catch, я так подумал что это подразумевалось.

    Alkid   (31.10.08 11:14) [37]
    Проблему зацикливания на std::shared_ptr можно решить, используя std::weak_ref :)

    Насколько хорошо можно сочетать GC с RAII и можно ли полностью вернуть RAII, например, в шарп - действительно интересно. Недавно пост про RAII+GC видел на clcm http://groups.google.com/group/comp.lang.c++.moderated/msg/f07afe67ab2488cb
    У меня такой вопрос: как в случае работающего GC+RAII будет работать  Block 3 в [30] ?
  • oxffff © (31.10.08 21:21) [39]

    > Alkid   (31.10.08 11:14) [37]
    >
    > > oxffff ©   (30.10.08 21:58) [36]


    > > Разница в том что программист С++ лезет в идеалогию другого
    >
    > > языка со своим самоваром, пытаясь мыслить привычными для
    >
    > > него категориями.
    >
    > Понимаешь, я не "программист С++". Я просто программист.
    >  Кстати, если брать по брутто-строкам кода, а Дельфи, я
    > наверное, больше написал. А так я на Дельфи, на С++, на
    > C# писал - это только по работе. Для души ещё много на чём.
    >  Сейчас вообще Prolog вдумчиво раскуриваю. Это я к чему
    > - этот спор я использовал как повод начать разговор о дизайне
    > языков, а не для того, что бы доказать, что "ваш Дельфи
    > - г-но" :)


    IBoxedType<T:Record >=interface
    function GetValue:T;
    procedure SetValue(const value:T);
    property BoxedValue:T read GetValue write SetValue;
    end;

    TBoxedType<T:Record>=class(TinterfacedObject,IBoxedType<T>)
    protected
    Value:T;
    public
    function GetValue:T;
    procedure SetValue(const value:T);
    destructor destroy;override;
    end;

    function TBoxedType<T>.GetValue: T;
    begin
    result:=Value;
    end;

    procedure TBoxedType<T>.SetValue(const value: T);
    begin
    self.Value:=value;
    end;

    Использование. :)

    procedure TForm2.Button1Click(Sender: TObject);
    var BoxedType,A:IBoxedType<integer>;
    begin
    BoxedType:=TBoxedType<integer>.create;
    BoxedType.BoxedValue:=5;
    end;


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


    Мы просто говорим о разных вещах.
  • guav © (01.11.08 22:55) [40]
    Круто. А теперь ещё интересует как выглядит:
    - слабая ссылка на это чудо
    - некопируемая неперемещаемая версия
    - другая семантика копирования, кроме "поверхностное копирование" или "потокобезопасный счёт ссылок".

    <offtop>
    Интересно, а можно ли чтобы с этим интерфейсом использовать as ?
    </offtop>
  • oxffff © (02.11.08 17:07) [41]

    > guav ©   (01.11.08 22:55) [40]


    Что за полет мысли?
  • oxffff © (02.11.08 17:12) [42]
  • guav © (02.11.08 17:21) [43]

    > Что за полет мысли?

    Это конкерные вопросы к [39].
  • oxffff © (02.11.08 17:31) [44]

    > guav ©   (02.11.08 17:21) [43]


    Вопросы следует задать понятных для всем способом. Переформулируй.
 
Конференция "Прочее" » Раскритикуйте бред С++ника :)
Есть новые Нет новых   [134446   +31][b:0][p:0.003]