-
> Mystic © (28.10.08 13:41) [19] > try > { > ... > } > catch(...); > { > ... > throw; > }
Э.... поясни, как это работать должно?
-
> И новый стандарт покуда не везде реализован, правда?
В gcc частично есть, может и rvrefs есть. В билдере тоже, но rvrefs нет. > В крайнем случае try..finally легко реализовать в C++ и > через
Нет, такое лучше не делать. Код успешной и неуспешной финализации будет дублироваться. Плюс не будет отлавливаться выбрасывание исключения из финализации. Я бы предпочёл через локаьный класс:
HANDLE & h;
} guard = ;
...
} Ну или по сути то же, но в красивой упаковке: BOOST_SCOPE_EXIT_END
...
}
-
> 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. Очень может быть, что будут еще какие-то особенности, которые надо знать.
-
> Была бы конструкция вида
Похожее будет возможно в С++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)); Что С++ навязывает помещение ресурмсов в классы, это хорошо. Есть неудобство с необходимостью врапить апи, ну это из-за того что АПИ не на С++ а не из-за того что в С++ плохо.
-
Итого, мы получили код, который почти не отличается от кода 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);
Да, я могу описать функцию SafeCloseHandle, то в целом это ребусы на ровном месте. Вот из-за неудобства врапить API, если мне надо что-то написать на этом API, я как раз и не использую такие врапперы. Что касается Delphi, то одной из целей был свободный доступ к WinAPI. Преимущество простого рабоче-крестьянского решения в том, что ты написал try/finally и забыл. Ну однотипный код, но его одна строка. А тут мы уже уйму времени обсуждаем возможные ньюансы реализации
-
> 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. :)
-
> oxffff © (28.10.08 19:40) [25] > Однако интересно, что использование одних средств предназначенных > для других целей для латания дыр в одном языке выдается > за преимущество, а использование средств в других языках > для реализации нужной фукнциональности выдается за Ж. Это > двойные стандарты.
Применение конструкторов и деструкторов и автоматических объектов для обеспечения RAII - это применение их по самому прямому назначению. Так что никаких двойных стандартов :)
> P.S. Интересно почему вся .NET де факто слизана с идеалогии > Delphi. :)
Потому что Хейлсберг :) Кстати, какие версии Дельфи ещё он делал,а какие уже без него?
Касаемо слизанности - что там такого слизано в первой версии с Дельфи, а не с Джавы? Делегаты - это да, это круто. А что ещё? И вообще .net уже своим путём идёт. Посмотри на C# 3.0 с его явным трендом в сторону ФП. И дисциплины дизайна там явно больше. Сравнивать это с Дельфи уже просто смешно.
-
> 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 вполне вероятен.
-
> oxffff © (28.10.08 22:01) [27] > > А двойной стандарт состоит вот в чем. > То есть использования типа A в качестве обертки типа Б для > наделения семантики входа/выхода в одном языке считается > нормальным в одном языке. > Однако использование типа А в качестве обертки типа Б в > другом языке для сходной функциональности считается уже > действием Ж.
Формулируя вопрос таким образом ты упускаешь существенное различие, о котором я говорил. А именно то, каким образом формируется и используется тип А в С++ и в Дельфи. А через этот пример очень хорошо видна разница в философии дизайна этих языков.
> Ввиду GC накладывать семантику копирования на сссылки нет > необходимости.
Однако же от идиомы RAII даже .net и C# не убежали, только реализовали её по-своему - IDisposable и 'using statement'. Да и всё равно иногда требуется реализовывать deep copy для объекта.
> Сейчас сам Хейлсберг склоняется к декларативному программированию.
И правильно делает.
> Ну дык анонимные методы уже Delphi 2009 win32 появились. > Пока нет лямбда выражений, но возможно добавят, и тогда > уже аналог LINQ вполне вероятен.
Ну это всё же не слизывание дотнета с дельфи :) Скорее наоборот.
-
> Но если предположить, что я хочу найти логический баг, то > окажется, что мне вовсе не обязательно акцентировать свое > внимание на коде освобождения ресурсов. Утечки нету, значит > где-то освобождается, гораздо важнее посмотреть на то, что > читается/пишется в файл и как. И хорошо, что код финализации > спрятан в конце и не мозолит глаза.
Именно. finally мозолит глаза, как и явный код удаления по месту. finally больше, т.к. оно добавляет ещё и лишние оступы. Для этого и есть RAII, чтобы убрать всё управление ресурсами из кода, оставив только логику, сделать шаг от boilerplate programming к literate programming.
> Преимущество простого рабоче-крестьянского решения в том, > что ты написал try/finally и забыл.
..написать его в другом месте :)
-
> IDisposable и 'using statement'.
это точно RAII ? Вот как тут пофиксить Block 2 и Block 3 : using System;
using System.Text;
namespace ConsoleApplication1
public void DoSomething()
void IDisposable.Dispose()
}
class ResouseUser
ExpensiveThing e;
}
class Program
;
}
}
static void Main(string[] args)
Console.WriteLine("Block 1 closed");
Console.WriteLine("Block 2 closed");
Console.WriteLine("Block 3 closed");
}
}
}
-
> guav © (28.10.08 22:30) [30] > это точно RAII ?
Это "RAII в стиле Дельфи" :)
Block 2 можно пофиксить путём делания ResourceuUser так же Disposable. Block 3 - ну это надо что-то типа auto_ptr, который так же будет Disposable :)))))
Короче, "те же яйца, вид сбоку" :)
-
> ..забыл написать его в другом месте :)
По окончании работы мне тут же скажут о наличии memory leak. И я спокойно пофикшу эту ошибку. И забуду окончательно.
> Именно. finally мозолит глаза, как и явный код удаления > по месту.
Я привык к компромиссам и далек от мысли поиска универсальных решений. Мне не сложно набрать пару дополнительных строчек и они мне не будут мозолить глаза. Особенно если это избавит меня от необходимости придумывать некий нетривиальный абстрактный механизм или изучать шаблоны boost. Автоматический вызов деструкторов это хорошо, но как только начинают происходить навороты вокруг этого, использовать этот трюк для реализации некой абстрактной фишки, то возвращаясь через пару месяцев я и сам начинаю забывать что я хотел и как оно биса работает. Спагетти намного круче goto.
-
> Alkid (28.10.08 15:42) [20]
Ловим все исключения, выполняем общее действие и кидаем исключение дальше
-
> Mystic © (29.10.08 18:37) [33]
А если у меня код в try-секции не произвёл исключения? Тогда catch(...) { ...; throw; } не отработает
-
> Alkid (29.10.08 18:53) [34]
Ага, в детстве вбил себе эквивалентность конструкций, теперь постоянно выпускаю из вида.
-
> 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 ... Лучше знать их все. :)
-
> 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.
-
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] ?
-
> 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, семантики копировани и т.п. может делать много > разных интересных вещений
Мы просто говорим о разных вещах.
|