Re[2]: Выйти из двух циклов сразу
От: Whisperer  
Дата: 04.06.02 10:45
Оценка:
Здравствуйте Хитрик Денис, Вы писали:

ХД>Ребята, давайте завершать беседы об отвлечённых материях. По теме в последнее время достаточно мало было сказано

ХД>Если кто не успел высказаться, вперёд! Заводите новую тему и публикуйте в соответствующем форуме.

ХД>P.S. Хорошо, что темы пока нельзя закрывать А то закрыл бы и не случилось бы такого бурного обсуждения в почти чистом форуме С++


Я обсолютно с тобой согласен. Надо ввести опцию — убить тему — если определенное количество
пользователей высказалось убить — значить надо ее снести. Какое количество надо решить
методом голосования.
Re[9]: Выйти из двух циклов сразу
От: DNS Россия  
Дата: 04.06.02 11:08
Оценка:
Здравствуйте VladD2, Вы писали:

VD>Здравствуйте WolfHound, Вы писали:


VD>Вообще-то моя задача была дать общую идею. Ну, да ладно, перепишим эквивалентно:


for(SDirectoryRecord* pRecord3 = pRecord1->m_pFirstRecord; pRecord3;)
{
  for(;pRecord3 != NULL; pRecord3 = pRecord3->m_pNextDirectory) 
  {

[skip]


VD>Да в твоем случае дейсвтительно лишние извраты.


Сижу, читаю нитку...
Влад, что-то вы уже перемудрили, как раз эти 2 for-а и норовят запутать читающего, т.к.
не являются стандартным подходом к организации циклов. В первом отсутствует модификация,
во втором инициализация....

Не думаю, что я бы воспользовался подобной идей для облегчения чтения кода.

Успехов!
Д.Н.С.
Re[22]: Выйти из двух циклов сразу
От: Аноним  
Дата: 04.06.02 11:20
Оценка:
Во понаписали!!!
Re[13]: Выйти из двух циклов сразу
От: Sergey Россия  
Дата: 04.06.02 11:23
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

AS>>>Во-первых, мне не совсем понятно, почему для противники goto постоянно щеголяют приёмами из арсенала C++. Если goto противоречит принципам структурного программирования, давайте оперировать средствами языка C. То есть никаких исключений, объектных обёрток и пр.


S>>Не, это не честно. Неиспользование goto в C — это уже совсем другой вопрос. Я думаю, экстремистов, утверждающих, что goto не нужен в С куда меньше, чем протестующих против его использования в C++.


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


Ну дык так и есть — goto противоречит принципам структурного программирования, нарушает их. И даже в какой-то мере плох из-за этого. Только из этого вовсе не следует, что его нельзя использовать. Чисто структурный подход достаточно беден сам по себе, а потому иногда, вместо того, чтобы привести к созданию более читаемых программ, приводит к прямо противоположному результату. Классический пример — очистка ресурсов. В принципе, обычно нет никаких проблем в том, чтобы реализовывать ее всегда без goto, только if'ами. Вот только если ресурсов более-менее много, лесенка if'ов не влезет даже на 22" монитор. И более читабельной/исправлябельной от отсутствия goto такая функция, естественно, не станет.

S>>Угу. Но break в стиле java был бы красивше. В общем-то, есть большая между return/break и goto — goto умеет прыгать назад.


AS>Ох, а ведь есть ещё setjmp/longjmp... ;)


А это сладкая парочка, AFAIK, считается с точки зрения структурного программирования еще более вредной, чем goto.

AS>>>В-четвёртых, по поводу квалификации программиста и зависимости количества goto от неё. Смеха ради я открыл папку VC6 и сделал по ней глобальный поиск. Что же я вижу? MFC. goto используются во многих местах. Ну, я всегда подозревал, что MFC писали ламеры. Смотрим дальше. CRT. Что это? И здесь goto? Список кандидатов на увольнение из Микрософт разрастается... Но ведь есть же ATL. Её писали настоящие ООП-пацаны, они не могут нас подвести. Как, и здесь goto? :???: Поистине, мы живём в обществе потребления, без веры, без идеалов... ;) Список можно продолжать. Загляните в исходники ядра Linux. Там вы тоже найдёте goto. Почему Linux-комьюнити до сих пор не переписала этот позорный код? Ы? Вот и я о том же.


S>>Не, это не слишком удачные примеры. Linux вроде как на C писана, там goto нужнее. CRT — тоже. Что касается ATL, то в atlbase.h можно было легко обойтись без goto, не потеряв читабельности.

AS>[skip]

AS>Здесь речь не о том. Дело в том, что создатели Linux и библиотек Microsoft таки используют goto. По логике Влада это даёт нам право зачислить их в ламеры. Сделать это несложно, но таким образом можно ненароком прописать в ламеры и самого себя. ;)


Про Linux ничего не скажу, я ейные исходники не разглядывал. А насчет ATL — я ж говорю, там в основном либо C, либо легаси код, перенесенный из того же С, который просто обломались переписать. Либо все-таки bad habits/недоделанность. В С допустимый, с моей точки зрения, набор ситуаций, когда можно использовать goto, гораздо шире и включает в себя не только выход из вложенных циклов, но и очистку ресурсов. А тяготеющая к goto очистка ресурсов нужна как минимум в сто раз чаще, чем выход из вложенных циклов. А насчет ламеризма Влад, как обычно, перегибает палку. Использование goto часто обусловленно не ламеризмом как таковым, а ленью (делать объектные обертки), отсутствием необходимости (переписывать legacy код) или недостатком времени (переписывать и отлаживать заново неструктурный унаследованный код, если в него все-таки приходиться вносить изменения). Скорее уж можно назвать ламеризмом использование флагов там, где проще написать goto :-\ Я уж не говорю про использование исключений для выхода из цикла :)
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[14]: Выйти из двух циклов сразу
От: Alexander Shargin Россия RSDN.ru
Дата: 04.06.02 11:51
Оценка:
Здравствуйте Sergey, Вы писали:

AS>>Ох, а ведь есть ещё setjmp/longjmp...


S>А это сладкая парочка, AFAIK, считается с точки зрения структурного программирования еще более вредной, чем goto.


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


AS>>Здесь речь не о том. Дело в том, что создатели Linux и библиотек Microsoft таки используют goto. По логике Влада это даёт нам право зачислить их в ламеры. Сделать это несложно, но таким образом можно ненароком прописать в ламеры и самого себя.


S>Про Linux ничего не скажу, я ейные исходники не разглядывал.


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


S>А насчет ATL — я ж говорю, там в основном либо C, либо легаси код, перенесенный из того же С, который просто обломались переписать. Либо все-таки bad habits/недоделанность.


Скорее не из C, а из "старого C++", который мало что умел и поддерживал. Тем не менее, и у MS есть разные ситуации. Так, в MFC goto зачастую используется для выхода из вложенных циклов, которые бегают по картам сообщений (CWnd::OnWndMsg, CWinThread::DispatchThreadMessageEx и т. д.).
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[3]: Выйти из двух циклов сразу
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 04.06.02 12:32
Оценка:
Здравствуйте Whisperer, Вы писали:

ХД>>Ребята, давайте завершать беседы об отвлечённых материях. По теме в последнее время достаточно мало было сказано

ХД>>Если кто не успел высказаться, вперёд! Заводите новую тему и публикуйте в соответствующем форуме.

ХД>>P.S. Хорошо, что темы пока нельзя закрывать А то закрыл бы и не случилось бы такого бурного обсуждения в почти чистом форуме С++


W>Я обсолютно с тобой согласен. Надо ввести опцию — убить тему — если определенное количество

W>пользователей высказалось убить — значить надо ее снести. Какое количество надо решить
W>методом голосования.

Я с вами не согласен, это один из немногих топиков, ради которых стоит читать не только ответы на свои вопросы.
Подробнее я высказался здесь
Автор: DarkGray
Дата: 04.06.02
Re[4]: Выйти из двух циклов сразу
От: Whisperer  
Дата: 04.06.02 12:42
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте Whisperer, Вы писали:


ХД>>>Ребята, давайте завершать беседы об отвлечённых материях. По теме в последнее время достаточно мало было сказано

ХД>>>Если кто не успел высказаться, вперёд! Заводите новую тему и публикуйте в соответствующем форуме.

ХД>>>P.S. Хорошо, что темы пока нельзя закрывать А то закрыл бы и не случилось бы такого бурного обсуждения в почти чистом форуме С++


W>>Я обсолютно с тобой согласен. Надо ввести опцию — убить тему — если определенное количество

W>>пользователей высказалось убить — значить надо ее снести. Какое количество надо решить
W>>методом голосования.

DG>Я с вами не согласен, это один из немногих топиков, ради которых стоит читать не только ответы на свои вопросы.

DG>Подробнее я высказался здесь
Автор: DarkGray
Дата: 04.06.02


Тогда надо создать тему ТРЕП или как еще ее там (а вобщето вы меня убедили)

Re[15]: Выйти из двух циклов сразу
От: Sergey Россия  
Дата: 04.06.02 13:00
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

AS>>>Ох, а ведь есть ещё setjmp/longjmp... ;)


S>>А это сладкая парочка, AFAIK, считается с точки зрения структурного программирования еще более вредной, чем goto.


AS>Однако используется же. В основном как раз для эмуляции исключений и корректного освобождения ресурсов в глобальном масштабе. А это не семечки, такая система (написанная хорошо) не снижает, а повышает поддерживаемость/расширяемость.


Ага. Глобальных ресурсов — а это уже не структурно. Просто концепция структурного программирования, как и бритва Оккама, слишком острая и иногда (нечасто) отрезает некоторые существенные детали.

AS>>>Здесь речь не о том. Дело в том, что создатели Linux и библиотек Microsoft таки используют goto. По логике Влада это даёт нам право зачислить их в ламеры. Сделать это несложно, но таким образом можно ненароком прописать в ламеры и самого себя. ;)


S>>Про Linux ничего не скажу, я ейные исходники не разглядывал.


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


Только из этого не следует, что то, что при этом получится — структурное и легко модифицируемое. Да и ядро, IMHO, не сто человек пишут, и новичками их не назовешь. Наверняка ребята и в автокодах при необходимости программировать могут, что им какие-то goto :))

S>>А насчет ATL — я ж говорю, там в основном либо C, либо легаси код, перенесенный из того же С, который просто обломались переписать. Либо все-таки bad habits/недоделанность.


AS>Скорее не из C, а из "старого C++", который мало что умел и поддерживал. Тем не менее, и у MS есть разные ситуации. Так, в MFC goto зачастую используется для выхода из вложенных циклов, которые бегают по картам сообщений (CWnd::OnWndMsg, CWinThread::DispatchThreadMessageEx и т. д.).


Я ничего не имею против использования goto для выхода из вложенных циклов в C++ (и для выхода из вложенных циклов и очистки ресурсов в C). AFAIK, Страуструп тоже :) Не стоит быть святее папы римского. Но у тех же MS в том же ATL полно ошибочного кода или кода, написанного не плюсовым (с моей точки зрения), а значит, плохим для программирования на C++ стилем. И goto встречаются в основном в этом коде :)
Кстати, о стиле — вот взять dinkumware'вскую реализацию STL, написанную, если верить комментариям в исходниках, неким P.J. Plauger. Все ее ругают, говорят стиль плохой, не понятно нифига. А ентот самый P.J. Plauger, оказывается, в соавторстве с небезызвестным Пайком книгу написал о хорошем стиле кодирования. И вся "плохость" его стиля на поверку является всего лишь нетрадиционной расстановкой отступов и слишком короткими названиями переменных. Хотя если немного привыкнуть, оказывается, что именование переменных и приватных функций подчиняется там довольно жестким правилам, и код становится вполне понятным. Так что что такое хорошо, а что такое плохо применительно к программированию вопрос не столь уж простой (это адресовано тем, кто тему собирается грохнуть :) ).
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[5]: Выйти из двух циклов сразу
От: flyker Россия  
Дата: 04.06.02 13:02
Оценка:
Здравствуйте Whisperer, Вы писали:

W>Здравствуйте DarkGray, Вы писали:


DG>>Здравствуйте Whisperer, Вы писали:


ХД>>>>Ребята, давайте завершать беседы об отвлечённых материях. По теме в последнее время достаточно мало было сказано

ХД>>>>Если кто не успел высказаться, вперёд! Заводите новую тему и публикуйте в соответствующем форуме.

ХД>>>>P.S. Хорошо, что темы пока нельзя закрывать А то закрыл бы и не случилось бы такого бурного обсуждения в почти чистом форуме С++


W>>>Я обсолютно с тобой согласен. Надо ввести опцию — убить тему — если определенное количество

W>>>пользователей высказалось убить — значить надо ее снести. Какое количество надо решить
W>>>методом голосования.

DG>>Я с вами не согласен, это один из немногих топиков, ради которых стоит читать не только ответы на свои вопросы.

DG>>Подробнее я высказался здесь
Автор: DarkGray
Дата: 04.06.02


W>Тогда надо создать тему ТРЕП или как еще ее там (а вобщето вы меня убедили)


W>


Да какой там треп ? Это же одна из важнейших дилемм в программировании!
Очень интересная тема (хотя и избитая), с удовольствием читаю все сообщения.
Все гениальное — просто
Re[7]: Выйти из двух циклов сразу
От: IT Россия linq2db.com
Дата: 04.06.02 13:42
Оценка: 123 (11)
Здравствуйте Patalog, Вы писали:

IT>>Главная оценка качества кода — maintenance...


P>Т.е. производство ради производства?


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

P>А тебе не приходило в голову, что частенько пишутся программы, которые и должны быть монолитными? Которые и не нужно изменять?


Не приходило. Более того, пока ты пишешь программу ты её сам и изменяшь. Или ты никогда не возвращаешься к уже написанному коду? Типа открыл тетрадку начал писать, закончилась тетрадка пошёл сдал результат заказчику? Ну-ну.

P>Ты всегда "офисный" софт пишешь? Тогда, конечно, узеру всегда приятно видеть пару лишних фенечек. А как насчет, что софт иногда пишется под конкртное железо (или ето уже не модно)?


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

P>А maintenance, как ты выразился, в большинстве софтверных контор вырождается в убирание багов уже после релиза.


Правильно. Полтора года назад сидел я одном доткоме и правил баги. Первое с чего всё начиналось — это с преведения исходного кода в божеский вид, расстановки отступов, добавления пробельных строк между процедурами и т.д. На это уходило больше времени чем на исправление самих багов. Этот дотком платил за меня моему работодателю 80 баксов в час, $640/день, $3200/неделю, $14000/месяц. Половиной этих денег оплачивалось форматирование исходных кодов. После того как пара таких самородков, за которыми всё время приходилось убирать ушли, было принято Code Convention и дело пошло на лад. Правда самому доткому уже не долго оставалось жить.

IT>>Это всё важно, но вторично.


P>Еще раз повторюсь, есть вещи в которых _ЭТО_ первично. А вот модное слово maintenance вторично, либо совсем не нужно.


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

ЗЫ. Знаешь почему на RSDN'е так медленно появляются новые фичи? В том числе и потому, что если мне нужно что-то добавить в исходнике, с которым мне не приходилось ещё работать, то для начала я должен его переписать по новой. При этом я уважаю Диму, который всё это делал, как талантливого программиста, но была бы моя воля я бы отшлёпал это юное дарование за то, что он видимо как и ты считает maintenance просто модным словом и оно для него "вторично, либо совсем не нужно".
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: Выйти из двух циклов сразу
От: flyker Россия  
Дата: 04.06.02 14:31
Оценка: -1
Здравствуйте Patalog, Вы писали:

P>Здравствуйте VladD2, Вы писали:


VD>>Здравствуйте Patalog, Вы писали:


P>>>Никак не могу понять почему народ так боится goto? Для этого есть какие-то причины помимо эстетических?


VD>>С давних пор луди стали замечать, что если придерживаться некоторой стратегии, то дела идут лучше, а удача чаще приходит в их дом. В программировании одной из таких стратегий является структурное программирование.


P>>>Ежели есть просветите плиз. А то я иногда пользуюсь, особенно в подобных случаях, и мне кажется это более красиво, чем с переменной...


VD>>goto оператор неструктурного программирования. Стоит применить его один раз как захочется еще и еще... Читать такой код значительно сложнее. Научивщись же обходится без оного по прошествии некоторого времени начинаешь задаваться другим вопросом... Зачем нужен этот оператор?!


P>Sorry читаю в нитевом режиме по этому сначало ответил на 29.05.02 03:21:39, а теперь только добрался до этого...


P>Хотя смысла это не меняет. Мне эта gotoфобия в плане структурного программирования сильно напоминает желание ООПшников все и вся превратить в объекты.

P>Мне кажется ето дело вкуса либо каких-то корпоративных правил\соображений. В первом случае — "De gustibus et coloribus non est disputandum" (с), во втором — "Против танка не попрешь" (с).

P>

P>equal:
P>for(SDirectoryRecord* pRecord3 = pRecord1->m_pFirstRecord; pRecord3 != NULL; pRecord3 = pRecord3->m_pNextDirectory) {
P>   if(pRecord3 != pRecord2 && pRecord2->m_nLevel == pRecord3->m_nLevel) {
P>       nRes = CompareRecord(pRecord2, pRecord3, nType) == 0 ? CCB_ERR_EQUAL_ID : CCB_ERR_NOERROR;
P>       if(nRes != CCB_ERR_NOERROR) {
P>            pCheckControl->m_data1 = reinterpret_cast<long>(pRecord2);
P>            pCheckControl->m_data2 = reinterpret_cast<long>(pRecord3);
P>            nCallBackRes = pCheckControl->DoControl(nRes); //Передается юзеру, который должен как-то отреагировать на ошибку
P>            nErrorCount++;
P>            
P>            if(nCallBackRes == CCB_RES_MAKEAUTO || nCallBackRes == CCB_RES_IGNORE) continue; //OK, продолжаем дальше
P>            else if(nCallBackRes == CCB_RES_ABORT) return nErrorCount; //Failed, отмена
P>            else goto equal; //Юзер попытался исправить ошибку, надо проверить заново
P>       }
P>   }
P>}

P>


P>IMHO, ежели у кого-то возникают проблемы с чтением этого кода, енто личные проблемы этого кого-то. У меня это единственный случай, когда я использовал goto (к вопросу о "Стоит применить его один раз как захочется еще и еще"), и _мне_ это показалось более красивым решением, чем городить еще кучу циклов\функцый\сравнений\флагов.


P>ЗЫж Похоже все-таки тема вырождается во флейм.


P>ЗЗЫж 2DarkGray: В каких случаях и какие "Компиляторы иногда путаются с goto, и могут не правильно вызывать конструкторы\деструкторы."


for(SDirectoryRecord* pRecord3 = pRecord1->m_pFirstRecord; pRecord3 != NULL; pRecord3 = pRecord3->m_pNextDirectory) {
   if(pRecord3 != pRecord2 && pRecord2->m_nLevel == pRecord3->m_nLevel) {
       nRes = CompareRecord(pRecord2, pRecord3, nType) == 0 ? CCB_ERR_EQUAL_ID : CCB_ERR_NOERROR;
       if(nRes != CCB_ERR_NOERROR) {
            pCheckControl->m_data1 = reinterpret_cast<long>(pRecord2);
            pCheckControl->m_data2 = reinterpret_cast<long>(pRecord3);
            nCallBackRes = pCheckControl->DoControl(nRes); //Передается юзеру, который должен как-то отреагировать на ошибку
            nErrorCount++;
            
            if(nCallBackRes == CCB_RES_MAKEAUTO || nCallBackRes == CCB_RES_IGNORE) continue; //OK, продолжаем дальше
            else if(nCallBackRes == CCB_RES_ABORT) return nErrorCount; //Failed, отмена
            else
            {
                pRecord3 = pRecord1->m_pFirstRecord; //Юзер попытался исправить ошибку, надо проверить заново
                continue;
             }
       }
   }
}


Я бы так написал, а как другие пишут мне глубоко по барабану, лишь бы не мне читать и править.
Но в данном конкретном случае оба варианта имеют право на жизнь.
Хотя если бы не комментарий в ключевой строке, дольше бы понимал суть.
Все гениальное — просто
Re[11]: Выйти из двух циклов сразу
От: IT Россия linq2db.com
Дата: 04.06.02 14:52
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

AS>Ну и последнее. По поводу надуманности примеров, где goto реально помогает. На мой взгляд, даже в Windows-программировании такие случаи бывают. Вот самый что ни есть типичный сценарий (напоминаю, пока мы остаёмся в рамках C):


AS>
void foo()
{
...
   // Работаем с данными

failure:
   bResult = FALSE;

cleanup:
...
   return bResult;
}


Во-первых. Ты всё же зря не привёл ни одного примера goto здесь. В большинстве случаев, он не используется напрямую, для него пишутся макросы в которые он заворачивается вместе и if'ом и установкой кода ошибки. Да и сами метки делаются макросами и имеют всегда предопределённые имена.

Во-вторых. Эта техника призвана решать одну проблему старого C, которая заменяется в C++ деструкторами (и не надо нас уговаривать оставаться в рамках C навсегда ;o) ) Я уверен, что большинство примеров, о которых ты говорил используют goto именно для этих целей. Это всего лишь попытка имулировать деструкты, больше ничего, вынужденная мера для обхода ограничений языка. В конце концов о jmp в ассемблере никто не спорит. Но это далеко не использование goto для построения самой логики приложения, как нам предлагает WolfHound. А мы, как я понимаю, спорим именно об этом.

В-третьих. Я ещё могу привести пример, где широко используется goto примерно для таких же целей. Написание COM-объектов на чистом API, установка HRESULT и выход из метода. Но что-то никто не хвалит эту технологию как продвинутую, все пишут на ATL. Не потому ли, что старый подход с goto глюкастее и сложнее?

В-четвёртых. Можно тот же код с очисткой ресурсов написать и без всего этого. Я писал на C года четыре и прекрасно обходился без подобной техники. Можем проверить, давай код с goto, я его перепишу на if'ы. ;o)
Если нам не помогут, то мы тоже никого не пощадим.
Re[12]: Выйти из двух циклов сразу
От: Alexander Shargin Россия RSDN.ru
Дата: 04.06.02 15:25
Оценка: 6 (1)
Привет, Игорь. Я ждал твоего появления. ;)


AS>>Ну и последнее. По поводу надуманности примеров, где goto реально помогает. На мой взгляд, даже в Windows-программировании такие случаи бывают. Вот самый что ни есть типичный сценарий (напоминаю, пока мы остаёмся в рамках C):


AS>>
IT>void foo()
IT>{
IT>...
IT>   // Работаем с данными

IT>failure:
IT>   bResult = FALSE;

IT>cleanup:
IT>...
IT>   return bResult;
IT>}
IT>


IT>Во-первых. Ты всё же зря не привёл ни одного примера goto здесь. В большинстве случаев, он не используется напрямую, для него пишутся макросы в которые он заворачивается вместе и if'ом и установкой кода ошибки. Да и сами метки делаются макросами и имеют всегда предопределённые имена.


Не очень понятно, к чему всё это. Можно конкретный пример?


IT>Во-вторых. Эта техника призвана решать одну проблему старого C, которая заменяется в C++ деструкторами (и не надо нас уговаривать оставаться в рамках C навсегда ;o) ) Я уверен, что большинство примеров, о которых ты говорил используют goto именно для этих целей. Это всего лишь попытка имулировать деструкты, больше ничего, вынужденная мера для обхода ограничений языка. В конце концов о jmp в ассемблере никто не спорит. Но это далеко не использование goto для построения самой логики приложения, как нам предлагает WolfHound. А мы, как я понимаю, спорим именно об этом.


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

1. goto не следует использовать никогда и ни при каких обстоятельствах (в том числе в случае с вложенными циклами, с которых мы начали). Моё мнение: иногда можно.
2. goto автоматически порождает нечитабельные и плохо поддерживаемые/расширяемые программы. Моё мнение: не обязательно, хотя часто.
3. Если человек использует goto, то он — ламер (и далее рассуждения о том, что это как норкотик и т. д.) Моё мнение — не факт (кстати, см. результаты голосования на текущий момент). Просто нужно пользоваться с умом. Впрочем, это касается и любых других средств языка C++.


IT>В-третьих. Я ещё могу привести пример, где широко используется goto примерно для таких же целей. Написание COM-объектов на чистом API, установка HRESULT и выход из метода. Но что-то никто не хвалит эту технологию как продвинутую, все пишут на ATL. Не потому ли, что старый подход с goto глюкастее и сложнее?


Вполне возможно. Когда без goto лучше, чем с ним, избавляйся на здоровье. Цель программиста — написать хорошую программу. Какие средства для этого используются — безразлично. Критерии "хорошести" тоже могут быть разными (см. моё предыдущее письмо), но это уже отдельный разговор.


IT>В-четвёртых. Можно тот же код с очисткой ресурсов написать и без всего этого. Я писал на C года четыре и прекрасно обходился без подобной техники. Можем проверить, давай код с goto, я его перепишу на if'ы. ;o)


Я и сам могу. Вопрос, будет ли твой вариант более читабельным. Думаю, что совсем не обязательно.
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[13]: Выйти из двух циклов сразу
От: IT Россия linq2db.com
Дата: 04.06.02 16:37
Оценка: 16 (1)
Здравствуйте Alexander Shargin, Вы писали:

AS>Привет, Игорь. Я ждал твоего появления. ;)


А я ждал, что ты ждал моего появления :))

IT>>Во-первых. Ты всё же зря не привёл ни одного примера goto здесь. В большинстве случаев, он не используется напрямую, для него пишутся макросы в которые он заворачивается вместе и if'ом и установкой кода ошибки. Да и сами метки делаются макросами и имеют всегда предопределённые имена.


AS>Не очень понятно, к чему всё это. Можно конкретный пример?


Я к тому, что даже в приведённом тобой для C случая люди пытаются боряться с goto применением всевозможных сил и средств, например, макросов примерно так:

#define START  int _err_code = 0;
#define ERROR  error:
#define EXIT   return _err_code;
#define CHECK(er,test) if (!(test)) { _err_code = er; goto _error; }

int foo(void *p)
{
    START;

    CHECK(1,p);
    // and so on

    ERROR:
    // some cleanup code
    EXIT;
}


AS>Не знаю на счёт "мы", а я спорю не об этом. Спагетти-код, который нам предложил WolfHound, я не одобряю. В своё время я наелся такими программами на ZX-Spectrum, и мне их вполне хватило. Спорю я в основном с Владом, а именно со следующими его утверждениями.


AS>1. goto не следует использовать никогда и ни при каких обстоятельствах (в том числе в случае с вложенными циклами, с которых мы начали). Моё мнение: иногда можно.


Никто не будет устанавливать лимит в применении одного goto на 1000 строчек кода или на одну программерскую жизнь. Иногда можно — значит можно вообще. Влад, мне думается, уже наелся этих можно по самое нихачу, при этом не столько своих, а тех за кем потом приходилось подчищать. По-этому, я его прекрасно понимаю. Запрет на использование goto в конторе — нормальная вещь, без него всегда можно обойтись, а с ним иногда могут быть проблемы. Тем более он говорил о C++, а ты в своих примерах упираешь на C и обосновываешь необходимость применение goto в нём. Ради бога, но не надо переносить затем результаты рассуждений на C++. В C++ goto не нужет совсем, а вот на asm'е без jmp никак, и зачем это сравнивать.

AS>2. goto автоматически порождает нечитабельные и плохо поддерживаемые/расширяемые программы. Моё мнение: не обязательно, хотя часто.


Ну вот опять. Плохо уже то, что он вообще эти проблемы пораждает.

AS>3. Если человек использует goto, то он — ламер (и далее рассуждения о том, что это как норкотик и т. д.) Моё мнение — не факт (кстати, см. результаты голосования на текущий момент). Просто нужно пользоваться с умом. Впрочем, это касается и любых других средств языка C++.


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

AS>Вполне возможно. Когда без goto лучше, чем с ним, избавляйся на здоровье. Цель программиста — написать хорошую программу. Какие средства для этого используются — безразлично. Критерии "хорошести" тоже могут быть разными (см. моё предыдущее письмо), но это уже отдельный разговор.


Безразлично только в одном случае, если твой код кроме тебя больше никому не нужен. Тогда точно безразлично.

IT>>В-четвёртых. Можно тот же код с очисткой ресурсов написать и без всего этого. Я писал на C года четыре и прекрасно обходился без подобной техники. Можем проверить, давай код с goto, я его перепишу на if'ы. ;o)


AS>Я и сам могу. Вопрос, будет ли твой вариант более читабельным. Думаю, что совсем не обязательно.


Естественно, цепочки if'ов тоже не лучший пример читабельности, но они вполне нормально заменяют технику, показанную тобой.
Если нам не помогут, то мы тоже никого не пощадим.
Re[14]: Выйти из двух циклов сразу
От: Alexander Shargin Россия RSDN.ru
Дата: 04.06.02 19:02
Оценка: 14 (2)
Здравствуйте IT, Вы писали:


AS>>Привет, Игорь. Я ждал твоего появления.


IT>А я ждал, что ты ждал моего появления


Ну, вот мы и дождались.


IT>>>Во-первых. Ты всё же зря не привёл ни одного примера goto здесь. В большинстве случаев, он не используется напрямую, для него пишутся макросы в которые он заворачивается вместе и if'ом и установкой кода ошибки. Да и сами метки делаются макросами и имеют всегда предопределённые имена.


AS>>Не очень понятно, к чему всё это. Можно конкретный пример?


IT>Я к тому, что даже в приведённом тобой для C случая люди пытаются боряться с goto применением всевозможных сил и средств, например, макросов примерно так:


Всё равно не понятно. Что с того, что кто-то так делает? Я, например, встречал такие конструкции редко. И там где встречал, матерился, так как приходилось разбираться, что стоит за этими макросами, и потом держать это в голове.


AS>>Не знаю на счёт "мы", а я спорю не об этом. Спагетти-код, который нам предложил WolfHound, я не одобряю. В своё время я наелся такими программами на ZX-Spectrum, и мне их вполне хватило. Спорю я в основном с Владом, а именно со следующими его утверждениями.


AS>>1. goto не следует использовать никогда и ни при каких обстоятельствах (в том числе в случае с вложенными циклами, с которых мы начали). Моё мнение: иногда можно.


IT>Никто не будет устанавливать лимит в применении одного goto на 1000 строчек кода или на одну программерскую жизнь. Иногда можно — значит можно вообще. Влад, мне думается, уже наелся этих можно по самое нихачу, при этом не столько своих, а тех за кем потом приходилось подчищать. По-этому, я его прекрасно понимаю. Запрет на использование goto в конторе — нормальная вещь, без него всегда можно обойтись, а с ним иногда могут быть проблемы.


Нет, Игорь, давай сразу отделим мухи от котлет. Или мы разговариваем о программировании как таковом, или об организации работы в отдельно взятой команде. На втором пути можно зайти очень далеко. Можно запретить перегрузку операторов, так как они запросто могут привести к соверщенно нечитаемым программам. Можно позапрещать нафиг шаблоны, так как они плохо переносятся и могут приводить к совершенно нечитабельному коду (и некоторые так и делают — просто запрещают их). И, кстати, большинство программистов не умеет нормально использовать исключения. Я знаю совсем немного программистов, которые в моём примере выще лёгким и непринуждённым движением руки напишут свои/используют существующие обёртки для указателей и хэндлов, а без таких обёрток исключения не доведут до добра. Короче, в команде возможны любые решения. И им нужно следовать. В своём письме я об этом написал. Если это всё, чего ты хотел от меня добиться, то тему можно закрывать.

Если же мы таки говорим о программировании, то давай говорить о нём.


IT>Тем более он говорил о C++, а ты в своих примерах упираешь на C и обосновываешь необходимость применение goto в нём. Ради бога, но не надо переносить затем результаты рассуждений на C++. В C++ goto не нужет совсем, а вот на asm'е без jmp никак, и зачем это сравнивать.


Я не упираю на C. Я вообще заговорил о нём потому, что мне было интересно: может быть, кто-то сможет убедительлно продемонстрировать ущербность goto, использую только принципы структурного программирования. Но кроме твоих if-ов, похоже, придумать нечего. Об этом я ещё напишу ниже.

А пока — согласен — давай сосредоточимся на C++. Для начала обратимся к теме, которая стоит в сабже. В первом письме я подробно остановился именно на ней. Итак, каким образом поставленная задача решается на C++ лучше, чем с использованием goto?


AS>>2. goto автоматически порождает нечитабельные и плохо поддерживаемые/расширяемые программы. Моё мнение: не обязательно, хотя часто.


IT>Ну вот опять. Плохо уже то, что он вообще эти проблемы пораждает.


Проблемы может порождать всё, что угодно. То же ООП — пачками. Я насмотрелся на пограммы, которые люди пишут в рамках концепции doc/view, которую предлагает MFC. Ей богу, уж лучше бы они свалили весь код в один класс. Именно поэтому в нашей профессии кадры решают почти всё. Эта глобальная проблема, которую не обойти запретами каких-то языковых средств. Просто если человек ламер, он повиснет на проекте тяжёлой гирей, а если нет — будет тянуть его вверх. И твои запреты вряд ли смогут сильно изменить ситуацию.


AS>>3. Если человек использует goto, то он — ламер (и далее рассуждения о том, что это как норкотик и т. д.) Моё мнение — не факт (кстати, см. результаты голосования на текущий момент). Просто нужно пользоваться с умом. Впрочем, это касается и любых других средств языка C++.


IT>Умом желательно пользоваться всегда. К сожалению, это одна из специфик нашей профессии. Конечно, челевек использующий goto не всегда ламер, но если он вообще его не использует, то он и не вызывает подозрения в том что он может быть ламером.


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


AS>>Вполне возможно. Когда без goto лучше, чем с ним, избавляйся на здоровье. Цель программиста — написать хорошую программу. Какие средства для этого используются — безразлично. Критерии "хорошести" тоже могут быть разными (см. моё предыдущее письмо), но это уже отдельный разговор.


IT>Безразлично только в одном случае, если твой код кроме тебя больше никому не нужен. Тогда точно безразлично.


Ничего подобного. Структурное программирование, ООП и пр. — не самоцель. Они должны служить делу эффективности, понятности, расширяемости и др. действительно важным целям, которые ты прекрасно знаешь и без меня. Так вот если в каком-то случае они скорее помеха, чем подмога, то лучше не забывать о главных целях, а не цепляться за принципы. Это примерно то, к чему я призываю.


IT>>>В-четвёртых. Можно тот же код с очисткой ресурсов написать и без всего этого. Я писал на C года четыре и прекрасно обходился без подобной техники. Можем проверить, давай код с goto, я его перепишу на if'ы.


AS>>Я и сам могу. Вопрос, будет ли твой вариант более читабельным. Думаю, что совсем не обязательно.


IT>Естественно, цепочки if'ов тоже не лучший пример читабельности, но они вполне нормально заменяют технику, показанную тобой.


Я скажу больше: они хуже, так как затрудняют и понимание, и расширяемость. Ты сам заметил, что метод с goto сильно напоминает эмуляцию деструкторов, которые и являются наилучшим решением данной задачи. Если в функцию добавляется ещё один ресурс, при моём подходе достаточно:
— добавить его в секцию переменных
— добавить код очистки в конец
— работать с ресурсом как ни в чём не бывало.

Логика же вложенных if-ов при таком добавлении может полностью измениться. Вот это-то как раз и плохо.
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[9]: Выйти из двух циклов сразу
От: The Lex Украина  
Дата: 04.06.02 19:39
Оценка:
Здравствуйте Patalog, Вы писали:

P>Ну, представь себе такую ситуацию.

P>Есть некий прибор, управляется ета хрень какой-нибудь двушкой в железном ящичке под голым досом. Энтих приборчиков энное количество по всему миру (например в Нижнезадрюпенской области, городе Мухосраннске).

Представляю. Даже назвать могу. И не пот "двушечкой", а под C51 и иже с ним. И не под "досом", а под своей операционкой. Французской — страшная вещь! Вино у них хорошее, а вот программеры... Знаете как все это назвается? Это называется сеть POS-терминалов...

P>Maintenance с точки зрения новых красивых фенечек ненужен\невозможен по двум причинам — как ты представляешь обновление софта в этой ситуации? И, второе, железо (приборчик) рабоает на пределе, и новых фенечек из него не высосешь. Да и не нужны они, себестоимость тоже нужно учитывать.


Вот так вот и представляешь себе обновление софта. И добавление фенечек и рюшечек. И управление всей сетью. И еще много чего. И приборчик работает на пределе. И новые фенечки из него высасывать надо, потому что с рынка улетишь в два счета — конкуренты не дремлют. А приходит новая "железячка": старый софт выбрасывать, да? Вот IT (если я ничего не перепутал) работал в этой сфере — он может подтвердить.

Так что и этот пример, увы, надуман...
Голь на выдумку хитра, однако...
Re[11]: Выйти из двух циклов сразу
От: The Lex Украина  
Дата: 04.06.02 19:47
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

AS>Собственно, я вмешался, так как хочу выступить в защиту goto. Влад говорил, что первая двадцатка топа его поддержит. Ну, я уже давно из неё выбыл, так что мне можно.


Ну, недолго музычка играла, так что теперь уже снова нельзя...
Голь на выдумку хитра, однако...
Re[12]: Выйти из двух циклов сразу
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.06.02 19:53
Оценка:
Здравствуйте Sergey, Вы писали:

S>Здравствуйте Alexander Shargin, Вы писали:


AS>>Во-первых, мне не совсем понятно, почему для противники goto постоянно щеголяют приёмами из арсенала C++. Если goto противоречит принципам структурного программирования, давайте оперировать средствами языка C. То есть никаких исключений, объектных обёрток и пр.


S>Не, это не честно. Неиспользование goto в C — это уже совсем другой вопрос. Я думаю, экстремистов, утверждающих, что goto не нужен в С куда меньше, чем протестующих против его использования в C++.


Дык, одной из задач при разработке C++ было избавление от вынужденного использования неструктурированноых goto. Кстати, в тот же С структурную обработку иключений ввели и даже лучше чем в С++. А мы всю жизнь COM-объекты на ATL делали без нее и ничего. Ни одного goto в коде и все работает.

AS>>Кстати, в Java goto убрали, но расширили синтаксис break для выхода из вложенных циклов. Похоже, ребята рассуждали похожим образом.


S>И правильно сделали. А комитет по стандартизации C++ правильно делает, что goto не убирает. Хотя break усовершенствовать не помешало бы —


Согласен.

S>глядишь, goto только в legacy коде и остался бы.


А вот тут нет. Посмотри на голосование. 100%-ых противников goto 5 человек (тебя я там не вдел , а сторонников довольно моного). Еще больше удручает C++-ый код того же MS. Там порою встречается идеальный и красивый код, а иногда вот такие прерлы:

HRESULT CFile::CopyFile(IMetaDataDispenserEx *pDisp, bool bCleanup)
{
    HRESULT hr = S_OK;
    if (m_SrcFile != NULL && _wcsicmp(m_SrcFile, m_Path) != 0) {
        if (W_CopyFile(m_SrcFile, m_Path, FALSE) == FALSE)
            return ReportError(HRESULT_FROM_WIN32(GetLastError()));
        bCleanup = true;
    } else
        hr = S_FALSE; // FLASE because we did nothing

    if (bCleanup) {
        mdToken tkAR, tkAttrib[4] = {mdTokenNil, mdTokenNil, mdTokenNil, mdTokenNil };
        const static WCHAR names [4][2] = {  {0,0}, {'S', 0}, {'M', 0}, {'S', 'M'} };
        CTokenMap Map;
        ALPEFile module;
        CComPtr<IMetaDataEmit> pNewEmit, pOldEmit;
        CComPtr<IMetaDataAssemblyImport> pAImport;
        CComPtr<IMetaDataFilter> pFilter;
        ULONG count;
        mdAssemblyRef ar[4];
        HCORENUM hEnum = NULL;

        if (FAILED(hr = module.OpenFileAs( pDisp, m_Path, true, true)))
            // A COM+ or WIN32 error
            goto CLEANUP;

        if (FAILED(hr = pDisp->DefineScope(CLSID_CorMetaDataRuntime,   // Format of metadata
                                           0,                           // flags
                                           IID_IMetaDataEmit,           // Emitting interface
                                           (IUnknown **) &pNewEmit)))
            goto CLEANUP;

        if (FAILED(hr = module.pImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&pAImport))||
            FAILED(hr = module.pImport->QueryInterface(IID_IMetaDataEmit, (void**)&pOldEmit)) ||
            FAILED(hr = module.pImport->QueryInterface(IID_IMetaDataFilter, (void**)&pFilter)))
            goto CLEANUP;


        while (SUCCEEDED(pAImport->EnumAssemblyRefs( &hEnum, ar, lengthof(ar), &count)) && count > 0) {
            ULONG len = 0;
            WCHAR buffer[32];
            for (ULONG i = 0; i < count; i++) {
                if (FAILED(hr = pAImport->GetAssemblyRefProps( ar[i], NULL, NULL, buffer, lengthof(buffer), &len, NULL, NULL, NULL, NULL))) {
                    pAImport->CloseEnum(hEnum);
                    goto CLEANUP;
                }
                if (len == 9 && wcscmp(buffer, L"mscorlib") == 0) {
                    tkAR = ar[i];
                    pAImport->CloseEnum(hEnum);
                    goto FoundMscorLib;
                }
            }
        }
        pAImport->CloseEnum(hEnum);
        tkAR = mdTokenNil;

FoundMscorLib:
        pAImport = NULL; // Release it

        for (int i = 0; i < 4 && SUCCEEDED(hr); i++) {
            WCHAR name[1024];
            wcscpy(name, MODULE_CA_LOCATION);
            mdToken tkAssem = mdTokenNil;
            wcscat(name, names[i]);
            // Try both a scoped and unscoped reference
            if (FAILED(hr = module.pImport->FindTypeRef(tkAR, name, &tkAssem)) &&
                (hr != CLDB_E_RECORD_NOTFOUND || tkAR == mdTokenNil || FAILED(hr = module.pImport->FindTypeRef(mdTokenNil, name, &tkAssem)))) {
                if (hr == CLDB_E_RECORD_NOTFOUND) {
                    hr = S_FALSE;
                    continue;
                } else
                    break;
            }

            tkAttrib[i] = tkAssem;
            if (!IsNilToken(tkAssem)) {
                DeleteToken func(pOldEmit);
                hr = EnumAllExcept( module.pImport, &func, tkAssem, NULL, 0, NULL, &CallEnumCustomAttributes);
            }
        }
        pOldEmit = NULL;
        if (FAILED(hr) || FAILED(hr = pFilter->UnmarkAll()))
            goto CLEANUP;

        {
            MarkToken func(pFilter);

            if (FAILED(hr = EnumAllExcept( module.pImport, &func, &CallEnumUserStrings, NULL, 0, NULL)) ||
                FAILED(hr = EnumAllExcept( module.pImport, &func, &CallEnumSignatures, NULL, 0, NULL)) ||
                FAILED(hr = EnumAllExcept( module.pImport, &func, &CallEnumTypeDefs, NULL, 0, &CallEnumMembers)) ||
                // Get Global functions/methods
                FAILED(hr = EnumAllExcept( module.pImport, &func, mdTokenNil, NULL, 0, &CallEnumMembers)) ||
                FAILED(hr = EnumAllExcept( module.pImport, &func, &CallEnumTypeRefs, tkAttrib, lengthof(tkAttrib), &CallEnumMemberRefs)) ||
                // Get Global functions/methods
                FAILED(hr = EnumAllExcept( module.pImport, &func, mdTokenNil, tkAttrib, lengthof(tkAttrib), &CallEnumMemberRefs)) ||
                FAILED(hr = EnumAllExcept( module.pImport, &func, &CallEnumTypeSpecs, NULL, 0, NULL)))
                goto CLEANUP;
        }

        if (FAILED(hr = pNewEmit->SetModuleProps(GetModuleName())))
            goto CLEANUP;


        if (FAILED(hr = pNewEmit->Merge( module.pImport, &Map, NULL)) ||
            FAILED(hr = pNewEmit->MergeEnd()) ||
            FAILED(hr = pNewEmit->GetSaveSize((CorSaveSize)(cssAccurate | cssDiscardTransientCAs), &count)))
            goto CLEANUP;

        if (FAILED(hr = module.EmitMembers( NULL, NULL, NULL, NULL, NULL, &Map, pNewEmit)) ||
            FAILED(hr = module.WriteNewMetaData(pNewEmit, count)) ||
            FAILED(hr = module.Close()))
            hr = ReportError(hr);

CLEANUP:; // Клинап, блин! :)
    }

    return hr;
}


S>Вот именно! Функция должна выполнять логически завершенное действие, а не служить заменой циклу. Уж лучше goto.


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

S>Что касается ATL, то в atlbase.h можно было легко обойтись без goto, не потеряв читабельности. atlcom.h — CSecurityDescriptor::GetTokenSids и CSecurityDescriptor::Attach, по-моему, написаны не слишком тщательно, там при некоторых ошибках лики вроде могут быть (да и код этот явно унаследованный). atldb.h — одно большое глюкало, явно писалось на скорую руку (когда пришлось однажды его использовать, нашел кучу разнообразных утечек). statreg.h — в CRegObject::RegisterFromResource (написанной в сишном стиле, кстати) от goto без труда можно было бы избавиться (вроде даже без объектных оберток); в CRegParser::RegisterSubkeys вообще хрен чего поймешь, что с goto, что без него — код явно write-only , напрашивается на полное переписывание при необходимости малейшей модификации. Так что насчет "настоящих ООП-пацанов" я сильно не уверен


ATL разные люди писали. Таб были и откровнные ламеры и крутые.

Кстати если посчитать количесвто goto в ATL, то окажется, что там их совесем не много и используется они приимущественно в целях обработки ошибок (VC6 реализовывал try/catch на CRT, от которого ATL-щики избавлялись). Ну, а в WTL вообще нельзя найти ни одного goto (хотя и там ламеры были). У на 5 мегов кода и ни одного goto и ничего живем и все работает.

AS>>В-третьих, любители жить без CRT тоже не очень-то жалуют исключения (и ATL/WTL вполне поддерживают такой стиль жизни).


Ну, вот мы как раз такие любители, ну, и что? Ни одного goto нет и CRL не ципляем. И вообще ресурсы в ручную не выделяем все на обертках.

S>А без CRT — это уже не С++, а "С с классами", да и то урезанный.


Ну, тут ты не прав. Для C++ под Виндовс CRT ненужна. Мы вон сделали 10 оберток над API-шными функциями и все прекрасно работает. Даже STL-ю по большому счету CRT не нужен (хотя мне STL не нравится).

AS>>Можно приводить и другие примеры. Про встроенные/низкоуровневые системы, которые с основном пишутся на C, тут уже сказали, и я с этим согласен.


Блин. Задолбал ты со своим С. Здесь речь шла о С++.

AS>>Резюме. Я призиваю участников этого флейма не быть такими категоричными. Эту ветку могут читать начинающие. Не надо прессовать им мозги "незыблемыми правилами". Лучше поучите их думать головой.


Так вот используя goto головой люди обычно и не думают. По этому я призываю начинающих думать головой и никогда не использовать goto.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Выйти из двух циклов сразу
От: The Lex Украина  
Дата: 04.06.02 20:00
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

IT>>>>В-четвёртых. Можно тот же код с очисткой ресурсов написать и без всего этого. Я писал на C года четыре и прекрасно обходился без подобной техники. Можем проверить, давай код с goto, я его перепишу на if'ы.


AS>>>Я и сам могу. Вопрос, будет ли твой вариант более читабельным. Думаю, что совсем не обязательно.


IT>>Естественно, цепочки if'ов тоже не лучший пример читабельности, но они вполне нормально заменяют технику, показанную тобой.


AS>Я скажу больше: они хуже, так как затрудняют и понимание, и расширяемость. Ты сам заметил, что метод с goto сильно напоминает эмуляцию деструкторов, которые и являются наилучшим решением данной задачи. Если в функцию добавляется ещё один ресурс, при моём подходе достаточно:

AS>- добавить его в секцию переменных
AS>- добавить код очистки в конец
AS>- работать с ресурсом как ни в чём не бывало.

Мда... В одном проекте, доставшемся мне по наследству, такой метод и используется... Честно? Я уже измучился и никак не перепишу это только потому, что проект работает себе и в данный момент дополнений/обновлений/исправлений (тьфу-тьфу-тьфу) не требует. Но все новое мной написаное в этом проекте от этого приема отошло! Не помню точно почему именно. Завтра подниму проект и скажу.
Голь на выдумку хитра, однако...
Re[13]: Выйти из двух циклов сразу
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.06.02 20:19
Оценка:
Здравствуйте Alexander Shargin, Вы писали:

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


Я, кстати, говорил, что единственное место где можно смериться с goto, это обработка ошибок в отстутсвии возможности создать врапер или использовать try. Так вот к Ричевский C — это как раз тот случай убогости языка в которой обработка ошибок и очистка ресурсов без goto сильно затруднена. С вообще довольно "грязный" язык и именно по этму не надо его брать в идеалы структурного программирования. А то можно еще порассцждать о необходимости костркций goto в первых реализациях бэйсика...

AS>Здесь речь не о том. Дело в том, что создатели Linux и библиотек Microsoft таки используют goto. По логике Влада это даёт нам право зачислить их в ламеры. Сделать это несложно, но таким образом можно ненароком прописать в ламеры и самого себя. ;)


За Линуксойдов не скажу (про С я уже говорил), а вот то, про орлов из MS сказать могу. Недавно рассматривал код CLI (сабсет .Net портируемый) там сразу видно кто код писал. Люди делятся на три категории:

1. Старперы. Те что с С преешли на С++ но разбираться им в этом языке в лом.
2. Нормальные программисты. Их код любо дорого читать.
3. Натуральные ламеры. Им вооще качество кода по барабану.

И скорее всего они пред открытием кода его тщательно причесывали. Отсуда сделал вывод, что конторах типа MS код пишится отдельными личностями на заказ. У них о культуре кода вообще говорить невозможно. Там в отдном каталоге 5 стилей.

PS

Вот кусочек кода из ATL содежащий goto. Именно о нем Sergey не очень лестно отзывался.


    HRESULT    GetPropertyInfo(ULONG cPropertySets, 
                        const DBPROPIDSET rgPropertySets[], ULONG* pcPropertyInfoSets,
                        DBPROPINFOSET**    prgPropertyInfoSets, 
                        WCHAR** ppDescBuffer, bool bInitialized = true, 
                        const GUID* pGuid = NULL)
    {
        CComCritSecLock<CComAutoCriticalSection> lock(m_oCriticalSection);
        HRESULT    hr = S_OK;
        ULONG ul, ulSet, ulNext, ulEnd;
        ULONG ulOutIndex;
        ULONG cSets;
        ULONG cPropInfos;
        //ULONG ulIndex = 0;
        ULONG cBuffers = 0;
        DWORD dwStatus = 0;
        DBPROPINFO*    pPropInfo = NULL;
        DBPROPINFO*    pCurPropInfo = NULL;
        WCHAR* pDescBuffer = NULL;
        DBPROPINFOSET* pPropInfoSet = NULL;
        UPROPINFO* pUPropInfo = NULL;
        WCHAR wszBuff[256];
        int    cch;
        CAtlArray<ULONG> rgInitPropsetIndexes;

        // If the consumer does not restrict the property sets
        // by specify an array of property sets and a cPropertySets
        // greater than 0, then we need to make sure we 
        // have some to return
        if(cPropertySets == 0)
        {
            // Determine the number of property sets supported
            // In this case, it usually the enumerator or data source asking for 
            // DBPROPSET_DBINIT information.

            if (pGuid != NULL)
            {
                // Need to determine if there are any UPROPSET_USERINIT sets here
                // they should be added to DBINIT.
                ULONG ulUserInitSets = 0;
                for (ULONG l=0; l<m_cUPropSet; l++)
                    if (m_pUPropSet[l].dwFlags & UPROPSET_USERINIT)
                        ulUserInitSets++;

                cSets = 1 + ulUserInitSets;        // one for DBINIT which is required
            }
            else
            {
                cSets = m_cUPropSet;
            }
        }
        else
        {
            cSets = 0;

            // Determine number of property sets required 
            // This is only required when any of the "special" property set GUIDs were specified
            for(ulSet=0; ulSet<cPropertySets; ulSet++)
            {
                if (GetPropertySetIndex(&(rgPropertySets[ulSet].guidPropertySet)) == S_OK)
                    cSets += m_cPropSetDex;
                else
                    cSets++;
            }
        }
        ATLASSERT(cSets);

        // Allocate the DBPROPINFOSET structures
        pPropInfoSet = (DBPROPINFOSET*)CoTaskMemAlloc(cSets * sizeof(DBPROPINFOSET));
        if(pPropInfoSet == NULL)
        {
            ATLTRACE(atlTraceDBProvider, 0, _T("Could not allocate DBPROPSET array for GetProperties\n"));
            hr =  E_OUTOFMEMORY;
            goto EXIT;
        }

        memset(pPropInfoSet, 0, cSets * sizeof(DBPROPINFOSET));

        ulOutIndex = 0;
        ULONG ulTempPropsetIndex = 0;
        ulEnd = cPropertySets == 0 ? cSets : cPropertySets; 
        // Fill in the output array
        for(ulSet=0; ulSet<ulEnd; ulSet++)
        {
             // Depending of if Property sets are specified store the
            // return property set.
            if (cPropertySets == 0)
            {
                if (pGuid != NULL)
                {
                    // Need to change this: set the guidPropertySet to the maching
                    // initialization property group (not DBINITALL).
                    for (ULONG ulCurrentInitSet = ulTempPropsetIndex; ulCurrentInitSet < m_cUPropSet; ulCurrentInitSet++)
                    {
                        // We need to locate either the DBINIT or USERINIT property sets here
                        // and set the property set index up correctly.
                        if (InlineIsEqualGUID(*(m_pUPropSet[ulCurrentInitSet].pPropSet), DBPROPSET_DBINIT) ||
                            (m_pUPropSet[ulCurrentInitSet].dwFlags & UPROPSET_USERINIT))
                        {
                            rgInitPropsetIndexes.Add(ulCurrentInitSet);
                            ulTempPropsetIndex = ulCurrentInitSet + 1;
                            //ulIndex = ulCurrentInitSet;
                            //pPropInfoSet[ulSet].guidPropertySet = *pGuid;
                            pPropInfoSet[ulSet].guidPropertySet = *(m_pUPropSet[ulCurrentInitSet].pPropSet);
                            GetPropertySetIndex(&pPropInfoSet[ulSet].guidPropertySet);    // Need to set the m_cPropSetDex variable
                            break;
                        }
                    }
                    //if (ulCurrentInitSet == m_cUPropSet)
                    //{
                    //    ulIndex = 0;
                    //}

                }
                else
                {
                    pPropInfoSet[ulSet].guidPropertySet = *(m_pUPropSet[ulSet].pPropSet);
                }
            }
            else
            {
                GUID const& guidSet = rgPropertySets[ulSet].guidPropertySet;
                if( (InlineIsEqualGUID(guidSet, DBPROPSET_DATASOURCEALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_DATASOURCEINFOALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_DBINITALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_SESSIONALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_COLUMNALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_CONSTRAINTALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_INDEXALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_TABLEALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_TRUSTEEALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_VIEWALL) ||
                    InlineIsEqualGUID(guidSet, DBPROPSET_ROWSETALL)) &&
                    GetPropertySetIndex(&guidSet) == S_OK )
                {
                    for(ul=0; ul<m_cPropSetDex; ul++,ulOutIndex++)
                    {
                        pPropInfoSet[ulOutIndex].guidPropertySet    = *(m_pUPropSet[m_rgiPropSetDex[ul]].pPropSet);
//                        pPropInfoSet[ulOutIndex].guidPropertySet    = rgPropertySets[ulSet].guidPropertySet;
                        pPropInfoSet[ulOutIndex].cPropertyInfos        = 0;
                    }
                }
                else
                {
                    // Handle non-category property sets
                    // Handle unknown property sets
                    pPropInfoSet[ulOutIndex].guidPropertySet = guidSet; 
                    pPropInfoSet[ulOutIndex].cPropertyInfos     = rgPropertySets[ulSet].cPropertyIDs; 
                    ulOutIndex++;
                }
            }
        }

        // Allocate a Description Buffer if needed
        if( ppDescBuffer )
        {
            cBuffers = CalcDescripBuffers(cSets, pPropInfoSet);
            if( cBuffers != 0 )
            {
                pDescBuffer = (WCHAR*)CoTaskMemAlloc(cBuffers * cchDescBuffSize * sizeof(WCHAR));
                if(pDescBuffer == NULL)
                {
                    hr = E_OUTOFMEMORY;
                    goto EXIT;
                }
                *ppDescBuffer = pDescBuffer;
                memset(pDescBuffer, 0, (cBuffers * cchDescBuffSize * sizeof(WCHAR)));
            }
        }

        // Process requested or derived Property sets
        dwStatus = 0;
        for(ulSet=0; ulSet<cSets; ulSet++)
        {
            ulNext=0;
            cPropInfos = 0;
            pPropInfo = NULL;
            dwStatus &= (GETPROPINFO_ERRORSOCCURRED | GETPROPINFO_VALIDPROP);

            // Calculate the number of property nodes needed for this
            // property set.
            if( cPropertySets == 0 )
            {
                ULONG ulTempSet;
                if (pGuid != NULL)
                {
                    ATLASSERT( ulSet < rgInitPropsetIndexes.GetCount() );
                    ulTempSet = rgInitPropsetIndexes[ulSet]; // ulIndex;
                }
                else
                    ulTempSet = ulSet;

                cPropInfos = m_pUPropSet[ulTempSet].cUPropInfo;
                dwStatus |= GETPROPINFO_ALLPROPIDS;
                m_rgiPropSetDex[0] = ulTempSet;
                m_cPropSetDex = 1; 
                _ATLDUMPPROPSETIID(*m_pUPropSet[ulTempSet].pPropSet, dwStatus);
            }
            else
            {
                // If the count of PROPIDs is 0 (NOTE: the above routine already determined
                // if it belonged to a category and if so set the count of properties to 0 for
                // each propset in that category.
                if( pPropInfoSet[ulSet].cPropertyInfos == 0 )
                {
                    dwStatus |= GETPROPINFO_ALLPROPIDS;
                    // We have to determine if the property set is supported and if so
                    // the count of properties in the set.
                    if( (GetPropertySetIndex(&(pPropInfoSet[ulSet].guidPropertySet)) == S_FALSE)
                        || (!bInitialized && 
                        !(InlineIsEqualGUID(pPropInfoSet[ulSet].guidPropertySet, DBPROPSET_DBINIT)) &&
                        !(InlineIsEqualGUID(pPropInfoSet[ulSet].guidPropertySet, DBPROPSET_DBINITALL))))
                    {
                        dwStatus |= GETPROPINFO_NOTSUPPORTED;
                        dwStatus |= GETPROPINFO_ERRORSOCCURRED;
                        _ATLDUMPPROPSETIID(pPropInfoSet[ulSet].guidPropertySet, dwStatus);
                        goto NEXT_SET;
                    }
                    else                        
                    {
                        ATLASSERT( m_cPropSetDex == 1 );
                        cPropInfos += m_pUPropSet[m_rgiPropSetDex[0]].cUPropInfo;
                        _ATLDUMPPROPSETIID(pPropInfoSet[ulSet].guidPropertySet, dwStatus);
                    }
//                    else
//                    {
//                        // Not Supported                    
//                        dwStatus |= GETPROPINFO_ERRORSOCCURRED;
//                        goto NEXT_SET;
//                    }
                }
                else
                {
                    // We also handle the case here where the user has requested
                    // a non-initialization group property info set while the
                    // provider is not initialized.  In this case, properties should
                    // not be set.
                    cPropInfos = pPropInfoSet[ulSet].cPropertyInfos;
                    if( (GetPropertySetIndex(&(pPropInfoSet[ulSet].guidPropertySet)) == S_FALSE)
                        || (!bInitialized && 
                        !(InlineIsEqualGUID(pPropInfoSet[ulSet].guidPropertySet, DBPROPSET_DBINIT)) &&
                        !(InlineIsEqualGUID(pPropInfoSet[ulSet].guidPropertySet, DBPROPSET_DBINITALL))))
                    {
                        dwStatus |= GETPROPINFO_NOTSUPPORTED;
                        dwStatus |= GETPROPINFO_ERRORSOCCURRED;
                        _ATLDUMPPROPSETIID(pPropInfoSet[ulSet].guidPropertySet, dwStatus);
                    }
                }
            }


            // Allocate DBPROP array
            ATLASSERT( cPropInfos != 0 );
            pPropInfo = (DBPROPINFO*)CoTaskMemAlloc(cPropInfos * sizeof(DBPROPINFO));
            if( pPropInfo )
            {
                // Initialize Buffer
                memset(pPropInfo, 0, cPropInfos * sizeof(DBPROPINFO));
                for(ULONG ulProp=0; ulProp<cPropInfos; ulProp++)
                {
                    VariantInit(&(pPropInfo[ulProp].vValues));
                    if( dwStatus & GETPROPINFO_NOTSUPPORTED )
                    {
                        // Not supported, thus we need to mark all as NOT_SUPPORTED
                        pPropInfo[ulProp].dwPropertyID = rgPropertySets[ulSet].rgPropertyIDs[ulProp];
                        pPropInfo[ulProp].dwFlags = DBPROPFLAGS_NOTSUPPORTED;
                        dwStatus |= GETPROPINFO_ERRORSOCCURRED;
                        _ATLDUMPPROPERTY(pPropInfo[ulProp].dwPropertyID, pPropInfo[ulProp].dwFlags);
                    }                    
                }
                // Make sure we support the property set
                if( dwStatus & GETPROPINFO_NOTSUPPORTED )
                {
                    ulNext = cPropInfos;
                    goto NEXT_SET;
                }

                // Retrieve the property information for this property set
                for(ul=0; ul<m_cPropSetDex; ul++)
                {
                    pUPropInfo = (m_pUPropSet[m_rgiPropSetDex[ul]].pUPropInfo);
                    ATLASSERT( pUPropInfo );

                    // Retrieve current value of properties
                    if( dwStatus & GETPROPINFO_ALLPROPIDS )
                    {
                        for(ulProp=0; ulProp<m_pUPropSet[m_rgiPropSetDex[ul]].cUPropInfo; ulProp++)
                        {
                            // Verify property is supported, if not do not return 
                            if( !TESTBIT(&(m_rgdwSupported[m_rgiPropSetDex[ul] * m_cElemPerSupported]), ulProp) )
                                continue;

                            pCurPropInfo = &(pPropInfo[ulNext]);

                            // If the ppDescBuffer pointer was not NULL, then
                            // we need supply description of the properties
                            if( ppDescBuffer )
                            {
                                // Set Buffer pointer
                                pCurPropInfo->pwszDescription = pDescBuffer;

                                // Load the string into temp buffer
                                cch = LoadDescription(pUPropInfo[ulProp].ulIDS, wszBuff, (sizeof(wszBuff)/sizeof(*wszBuff)));
                                if( cch )
                                {
                                    // Adjust for '\0'
                                    cch++;

                                    // Transfer to official buffer if room
                                    memcpy(pDescBuffer, wszBuff, cch * sizeof(WCHAR));
                                    pDescBuffer += cch;
                                }
                                else
                                {
                                    wcscpy(pDescBuffer, L"UNKNOWN");
                                    pDescBuffer += (wcslen(L"UNKNOWN") + 1);
                                    _ATLDUMPPROPERTY(pCurPropInfo->dwPropertyID, ATLDB_NO_STRING);
                                }
                            }

                            pCurPropInfo->dwPropertyID = pUPropInfo[ulProp].dwPropId;

                            // Strip out any user defined flags that may be around.  Note,
                            // this isn't a full-proof thing because properties change.  It
                            // won't work in OLE DB 2.5 if someone does a property like 0x40000
                            DWORD dwFlags = pUPropInfo[ulProp].dwFlags & 0xfffff;

                            pCurPropInfo->dwFlags = dwFlags;
                            pCurPropInfo->vtType = pUPropInfo[ulProp].VarType;
                            pCurPropInfo->vValues.vt = VT_EMPTY;

                            dwStatus |= GETPROPINFO_VALIDPROP;
                            // Increment to next available buffer
                            ulNext++;
                            _ATLDUMPPROPERTY(pCurPropInfo->dwPropertyID, pCurPropInfo->dwFlags);
                        }
                    }
                    else
                    {
                        ATLASSERT( m_cPropSetDex == 1 );
                        ULONG cIterations = ((cPropInfos>cBuffers) && (ppDescBuffer)) ? cBuffers : cPropInfos;

                        for( ulProp = 0; ulProp < cIterations; ulProp++, ulNext++ )
                        {
                            pCurPropInfo = &(pPropInfo[ulNext]);

                            // Process Properties based on Restriction array.
                            pCurPropInfo->dwPropertyID = rgPropertySets[ulSet].rgPropertyIDs[ulProp];

                            if( GetUPropInfoPtr(m_rgiPropSetDex[ul], pCurPropInfo->dwPropertyID, &pUPropInfo)
                                == S_OK )
                            {
                                // If the ppDescBuffer pointer was not NULL, then
                                // we need supply description of the properties
                                if( ppDescBuffer )
                                {
                                    // Set Buffer pointer
                                    pCurPropInfo->pwszDescription = pDescBuffer;

                                    // Load the string into temp buffer
                                    cch = LoadDescription(pUPropInfo->ulIDS, wszBuff, (sizeof(wszBuff)/sizeof(*wszBuff)));
                                    if( cch )
                                    {
                                        // Adjust for '\0'
                                        cch++;

                                        // Transfer to official buffer if room
                                        memcpy(pDescBuffer, wszBuff, cch * sizeof(WCHAR));
                                        pDescBuffer += cch;
                                    }
                                    else
                                    {
                                        wcscpy(pDescBuffer, L"UNKNOWN");
                                        pDescBuffer += (wcslen(L"UNKNOWN") + 1);
                                        _ATLDUMPPROPERTY(pCurPropInfo->dwPropertyID, ATLDB_NO_STRING);
                                    }
                                }

                                pCurPropInfo->dwPropertyID = pUPropInfo->dwPropId;

                                // Strip out any user defined flags that may be around.  Note,
                                // this isn't a full-proof thing because properties change.  It
                                // won't work in OLE DB 2.5 if someone does a property like 0x40000
                                DWORD dwFlags = pUPropInfo->dwFlags & 0xfffff;

                                pCurPropInfo->dwFlags = dwFlags;
                                pCurPropInfo->vtType = pUPropInfo->VarType;

                                dwStatus |= GETPROPINFO_VALIDPROP;
                            }
                            else
                            {
                                // Not Supported
                                pCurPropInfo->dwFlags = DBPROPFLAGS_NOTSUPPORTED;
                                dwStatus |= GETPROPINFO_ERRORSOCCURRED;
                            }
                            _ATLDUMPPROPERTY(pCurPropInfo->dwPropertyID, pCurPropInfo->dwFlags);
                        }
                    }
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
                goto EXIT;
            }

NEXT_SET:
            pPropInfoSet[ulSet].cPropertyInfos = ulNext;
            pPropInfoSet[ulSet].rgPropertyInfos = pPropInfo;
        }

        // Success, set return values
        *pcPropertyInfoSets = cSets;
        *prgPropertyInfoSets = pPropInfoSet;

        // At least one propid was marked as not S_OK
        if( dwStatus & GETPROPINFO_ERRORSOCCURRED )
        {
            // If at least 1 property was set
            if( dwStatus & GETPROPINFO_VALIDPROP )
                return DB_S_ERRORSOCCURRED;
            else
            {
                // Do not free any of the rgPropertyInfoSets, but
                // do free the ppDescBuffer
                if( pDescBuffer )
                {
                    ATLASSERT( ppDescBuffer );
                    CoTaskMemFree(pDescBuffer);
                    *ppDescBuffer = NULL;
                }
                return DB_E_ERRORSOCCURRED;
            }
        }

        return S_OK;
EXIT:
        // Check if failure and clean up any allocated memory
        if( FAILED(hr) && 
            (hr != DB_E_ERRORSOCCURRED) )
        {
            // Free Description Buffer
            if( pDescBuffer )
            {
                ATLASSERT( ppDescBuffer );

                CoTaskMemFree(pDescBuffer);
                *ppDescBuffer = NULL;
            }

            if( pPropInfoSet )
            {
                // Loop through Property Sets
                for(ulSet=0; ulSet<cSets; ulSet++)
                    CoTaskMemFree(pPropInfoSet[ulSet].rgPropertyInfos);
                CoTaskMemFree(pPropInfoSet);
            }
        }

        return hr;
    }



Я бы на метсе менеджеров из MS со стыда бы сгарел. :(

Это мог написать только ламер. Ведь он тачно слышал об обертках. И скорее всего ему объясняли, что такое декомпозиция.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.