- 1
- 2
- 3
- 4
- 5
THTTPServer::TDynamicResponse::~TDynamicResponse( void )
{
if(typeid(*this)==typeid(TDynamicResponse))//Борьба с pure virtual function call.
this->flush();
};
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
+163
THTTPServer::TDynamicResponse::~TDynamicResponse( void )
{
if(typeid(*this)==typeid(TDynamicResponse))//Борьба с pure virtual function call.
this->flush();
};
Проект поменьше.
defecate-plusplus 28.03.2012 16:30 # 0
Говногость 28.03.2012 18:04 # 0
Просто flush - виртуальная функция и если вызвать flush в деструкторе предка (а не в деструкторе реального объекта), то вызывается не тот flush, тк реальный объект к этому времени деструктирован.
Автор написал такой воркэраунд вокруг этой проблемы.
Я пока это не переписывал и пока не знаю как, но планирую.
defecate-plusplus 28.03.2012 22:46 # +1
http://ideone.com/0jc2b
воркэраунд ни о чем, это просто изначально идиотская идея пытаться в деструктор предка засунуть несвойственные ему задачи - якобы пытаться в нем выполнить виртуальные операции, за которые он никакой ответственности нести не имеет права, а потом с этим героически бороться
Говногость 28.03.2012 22:55 # 0
Капитанские погоны вам к лицу.
>воркэраунд ни о чем
Как не о чем? Свою задачу выполняет. Вызывается именно flush реального типа объекта, а не base. Добиваются этого именно копипастой этих магических строк и заменой TDynamicResponse на имя деcтруктора без символа ~.
Если знаете как это исправить правильно, то говорите, не стесняйтесь.
defecate-plusplus 28.03.2012 23:37 # +1
>> //Борьба с pure virtual function call
> Капитанские погоны вам к лицу.
не к лицу, просто неясность изъявления ваших мыслей привела меня к выводу, что работа деструкторов для кого то хранит слишком много тайн
если идея в том, что каждый предок имеет свой собственный flush, который своим вызовом должен исключить дальнейший вызов flush предка в деструкторе, то гораздо проще этого добиться с помощью protected: bool was_flushed_ в базовом классе и вызовом с банальной проверкой if (!was_flushed_) { this->flush(); was_flushed_ = true; }
но, если честно, подобные махинации в деструкторах уже наводят на мысль что в рахитектуре изначально что то пошло не так
> Свою задачу выполняет
каким образом он может выполнить свою задачу, если в деструкторе объекта ~sometype() равенство typeid(*this) == typeid(sometype) - это тождество?
http://ideone.com/cME4z
Говногость 29.03.2012 09:23 # 0
http://ideone.com/cME4z
А вот это уже интересно... В студии выполняется, а в GCC нет... Кто прав? GCC или студия 2008?
Говногость 29.03.2012 09:37 # 0
defecate-plusplus 29.03.2012 09:37 # 0
Microsoft Visual Studio 2008
Версия 9.0.30729.1 SP
но если в другом компиляторе будет иное поведение, то это лишний раз доказывает некорректность притягивания RTTI к решению проблемы в деструкторах
Говногость 29.03.2012 09:45 # 0
OMG, по непонятной причине работает - не буду трогать... :-[
roman-kashitsyn 29.03.2012 09:52 # +1
Тарас, заметь: даже программисты на цпп со стажем не понимают, почему оно работает. Вот уж действительно КРЕСТОПРОБЛЕМЫ™. Хотя я недавно собеседовал Java-программиста с 6-летним стажем, не знающего элементарных вещей.
Говногость 29.03.2012 09:58 # 0
defecate-plusplus 29.03.2012 10:00 # 0
RTTI вообще сам по себе тот еще костыль, и очень вендор-специфик
запросто ожидаются даже проблемы с нерабочим dynamic_cast<>, если библиотеку и приложение собрать разными версиями одного компилятора
да и в конструкторах и деструкторах RTTI противопоказан, очень неудачная идея его там использовать
Говногость 29.03.2012 10:11 # 0
Меня это тоже раздражает... Неужели стандартизаторы С++ до сих пор не смогли стандартизировать ABI для RTTI? Казалось бы dynamic_cast - конструкция языка, но вдруг неожиданного может перестать работать, если взять более новую версию библиотеки.
defecate-plusplus 29.03.2012 10:45 # 0
это всего лишь костыль, призванный в сжатые сроки ставить подпорки в разваливающуюся глиняную рахитектуру приложения, поэтому в хорошем проекте grep -r "dynamic_cast" обязан выдать 0 результатов даже на миллионе LoC
Говногость 29.03.2012 10:52 # 0
roman-kashitsyn 29.03.2012 10:54 # 0
Use Lisp, Luke.
Говногость 29.03.2012 11:30 # 0
defecate-plusplus 29.03.2012 11:33 # 0
virtual integraltype get_type() const;
void do_multi(base * b1, base * b2) и дальше уже как фантазия разыграется делать соответствие make_pair(b1->get_type(), b2->get_type()) и методу
Говногость 29.03.2012 11:40 # 0
defecate-plusplus 29.03.2012 12:02 # 0
в любом случае С++ мультиметод не будет таким красивым, как ему положено быть в приспособленных для этого языках
Говногость 29.03.2012 12:05 # 0
Можно пример?
defecate-plusplus 29.03.2012 12:28 # 0
возможно будет выглядеть красивее, чем
впрочем, я не знаю как там у вас делаются мультиметоды, потому что у меня их нет вовсе
Говногость 29.03.2012 12:38 # 0
Тут вся сложность в том, что несколько классов могут по ошибке заявить, что они type_eatable и поэтому они не верно будут обрабатываться, что возможно заметят далеко не сразу.
TarasB 29.03.2012 10:55 # 0
Что-то я начинаю думать (извините, но я реально так начинаю думать), что РАИИ и ООП несовместимы. РАИИ оставьте для всякой хрени, которая маскируется под встроенный тип, а ООП с РТТИ, наследованиями и всяким динамоговном лучше оставить ссылочным и без всякой неявной херни вообще, т.е. с ручный конструктором и деструктором.
Можно заворачивать говнообъект в РАИИ-указатель, можно подключать ГЦ, действующий только для таких объектов.
roman-kashitsyn 29.03.2012 11:00 # 0
TarasB 29.03.2012 11:02 # 0
Говногость 30.03.2012 09:54 # 0
А какая разница ссылочные типы или не ссылочные? Будет та же проблема, что видна в этом топике. А уж тем более без разницы ручные деструкторы или автоматические.
TarasB 30.03.2012 11:05 # 0
Вызываться будет только то, что вызвалось.
Говногость 30.03.2012 12:06 # 0
TarasB 30.03.2012 14:55 # 0
Желающие пусть сами заводят структуру, содержащую класс и вызывающую виртуальный метод flush в деструкторе этой структуры. И flush вызовется лишь 1 раз.
Lure Of Chaos 30.03.2012 16:57 # 0
ммм, это каких же?
TheCalligrapher 29.03.2012 22:29 # 0
Говногость 30.03.2012 07:54 # 0
каким образом он может выполнить свою задачу, если в деструкторе объекта ~sometype() равенство typeid(*this) == typeid(sometype) - это тождество?
Я понимаю о чем вы, просто автор кода видимо думал, что в деструкторе\конструкторе typeid работает также, как и везде.
В С++ это не так.
А вот стандартное поведение:
http://ideone.com/lNoNx
Видимо, автор думал, что код в деструкторе работает также.
Я понимаю что работать этот код не должен, но почему то работает. Думаю, там ещё какие то проверки и флаги есть, которые всё-таки работают. Я в тот исходник смотрел только 2 минуты, но больше смотреть не хочу. Не моё это дело переписывать чужой работающий говнокод.
TheCalligrapher 29.03.2012 21:53 # 0
На территории конструктора и деструктора класса 'A' (т.е. непосредственно внутри реализации 'A::A' и 'A::~A') выражение 'typeid(*this)' всегда возвращает строго 'typeid(A)', незавивисимо от того, какой тип имеет "полный" конструируемый или уничтожаемый объект.
По этой причине в данном примере условие под 'if' - тавтология. Оно всегда истинно. Поэтому соврешенно не понятно, во-первых, что пытались сделать, и, во-вторых, чего достигли. Данный 'if' - бессмысленен и ничего не меняет.
Никакой борьбы с pure virtual function call таким способом получить не удастся.
wvxvw 30.03.2012 03:45 # 0
TheCalligrapher 30.03.2012 06:49 # 0
wvxvw 30.03.2012 12:44 # 0
Говногость 30.03.2012 12:55 # 0
ECEHuHCKuu_nemyx 18.10.2020 23:52 # 0
Говногость 28.03.2012 22:03 # 0
TarasB 29.03.2012 10:49 # +2
Kirinyale 30.03.2012 12:45 # 0
Говногость 30.03.2012 12:53 # 0
Kirinyale 30.03.2012 12:57 # 0
Говногость 30.03.2012 13:31 # 0
if(typeid(*this)==typeid(TDynamicRespons e))
избежать 7ми кратного вызова flush
Kirinyale 30.03.2012 13:40 # 0
TarasB 30.03.2012 18:03 # 0
Чтобы он соответствовал typetag, надо делать его виртуальным.
Говногость 30.03.2012 18:23 # 0
В деструкторе базового класса в С++ вызывать flush нельзя, тк класс потомок уже деструктирован и вызов будет некорректный. Вызовется flush базового класса или pure virtual call method stub, если метод flush базового класса не имеет реализации.
TarasB 30.03.2012 18:35 # 0
Чё? Все деструкторы пустые, кроме базового, они ниего не будут делать, деструктор базового вызовется как раз вовремя.
Kirinyale 30.03.2012 18:50 # +2
Вообще, любая попытка вызвать любую виртуальную функцию из конструктора или деструктора - говнокод по определению. Это же крестоосновы.
TarasB 30.03.2012 18:55 # 0
defecate-plusplus 30.03.2012 19:29 # 0
но всегда можно выстрелить себе в ногу и без помощи ТВМ - сохранить в конструкторе derived адрес метода в поле base::my_virtual_flush_ (типа = boost::bind(derived::flush, this)) и его вызывать в base::~base()
только это всё равно будет мегаговнокостыль
потому что derived::flush() наверняка захочет поработать с какими-нибудь членами derived, а они уже давно разрушены, потому то из коробки виртуальные функции в конструкторах и деструкторах в С++ работают как работают
TarasB 30.03.2012 19:57 # 0
В общем, сдаётся мне, что наследование и РАИИ несовместимы
TheCalligrapher 30.03.2012 20:56 # 0
А 'boost'... Неужели 'boost' предоставляет возможности для того, чтобы подавить это поведение, т.е. привязать указатель непосредственно к конкретной виртуальной функции еще на этапе инициализации из исключить ТВМ из рассмотрения в момент вызова?
TarasB 30.03.2012 21:03 # +1
ЗА ВИРАТУЛА!!!
Ахаха я знаю как назову следующего виртуала
defecate-plusplus 30.03.2012 22:31 # 0
биндить виртуальную функцию - попадать в ТВМ
ну значит решение выстреливания себе в ногу - для каждой виртуальной inherited::flush() нужна невиртуальная inherited::do_flush(), делающая то, что нужно
в невиртуальной do_flush(), понятное дело, нельзя другие виртуальные вызовы
http://ideone.com/hm5Ve
TheCalligrapher 30.03.2012 20:50 # +2
TheCalligrapher 30.03.2012 20:47 # 0
Виртуальность в конструкторе/деструкторе прокрасно работает, но лишь до уровня конструируемого/деструктируемого класса. Логика этого поведения - общеизвестна.
Помня об этом, можно спокойно вызывать виртуальные функции из конструктора/деструктора.
Kirinyale 30.03.2012 22:34 # 0
Да, иногда такие вызовы работают и делают именно то, что задумано. Но по сути меньшим говнокодом от того не становятся. Я бы вообще на это дело варнинги компиляторам добавлял, благо их и так хватает неоднозначных.
А высший пилотаж - это когда конструктор или деструктор вызывает обычную функцию, та - ещё десяток обычных, а какая-то из них на десятом уровне вложенности добирается до виртуальной... это уже никакой компилятор сам не найдёт :(
TheCalligrapher 30.03.2012 23:29 # 0
Но как только мы начинаем рассматривать "опосредованные" вызовы, ситуация меняется. Виртуальные механизмы во время конструкции/деструкции продолжают работать так же, как и работали. Меняется только динамический тип объекта. А виртуальность работает по-старому в рамках "нового" динамического типа.
Например, пусть у нас есть три класса
В данном примере вызов 'foo' внутри 'A::bar' выполнятеся виртуально. Но при вызове 'A::bar' из деструктора 'B::~B' виртуальность ограничена глубиной класса 'B'.
Kirinyale 30.03.2012 23:52 # 0
Пока мне не встретится реальный (а не искусственный) пример, когда такое действительно необходимо и при этом правильно и прозрачно выглядит и работает во всех обстоятельствах - продолжаю считать подобные вызовы ошибками (потенциально очень опасными). Встречаются они, к сожалению, совсем не редко, т.к. следить за всеми вызовами нереально, а обнаруживаются обычно при очередном крэше.
В C++ вообще много чего можно делать, но не всё - нужно.
Antervis 27.07.2015 09:13 # 0
Варианта два: либо выносить всю логику flush() в предка, либо вызывать flush() в дектрукторе каждого наследника.