Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Аноним  
Дата: 12.02.13 13:20
Оценка:
Есть полиморфный массив, то есть массив указателей BaseItem*, каждый из которых реально указывает на объект какого-то класса-наследника BaseItem. Наследников много и все они образуют довольно ветвистое дерево.

Нужна шаблонаня функция вида GetItem<T>(int i), которая должна сделать следующее: если реальный тип i-того элемент массива реально является классом T или наследником T (где в качестве T передается некоторый наследник BaseItem), то вернуть адрес этого объекта, иначе вернуть null.
Возможно ли такое? (без буста).
Re: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: watch-maker  
Дата: 12.02.13 13:25
Оценка: +2
Здравствуйте, Аноним, Вы писали:

А>Нужна шаблонаня функция вида GetItem<T>(int i), которая должна сделать следующее: если реальный тип i-того элемент массива реально является классом T или наследником T (где в качестве T передается некоторый наследник BaseItem), то вернуть адрес этого объекта, иначе вернуть null.

А>Возможно ли такое? (без буста).
return dynamic_cast<T*>(array[i]);
Re: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 12.02.13 13:57
Оценка: -2
Здравствуйте, Аноним, Вы писали:

А>Есть полиморфный массив, то есть массив указателей BaseItem*, каждый из которых реально указывает на объект какого-то класса-наследника BaseItem. Наследников много и все они образуют довольно ветвистое дерево.


А>Нужна шаблонаня функция вида GetItem<T>(int i), которая должна сделать следующее: если реальный тип i-того элемент массива реально является классом T или наследником T (где в качестве T передается некоторый наследник BaseItem), то вернуть адрес этого объекта, иначе вернуть null.

Толком из этого ничего хорошего не выйдет. Ты пытаешься мешать динамику и статику, а это не сработает тут. Единственный веный путь использовать rtti и dynamic_cast.
Sic luceat lux!
Re: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Vamp Россия  
Дата: 12.02.13 14:11
Оценка: +2
А>Нужна шаблонаня функция вида GetItem<T>(int i), которая должна сделать следующее: если реальный тип i-того элемент массива реально является классом T или наследником T (где в качестве T передается некоторый наследник BaseItem), то вернуть адрес этого объекта, иначе вернуть null.
В 99% случаев подобный вопрос указывает на ошибку проектирования.
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 12.02.13 14:12
Оценка: -2
Здравствуйте, watch-maker, Вы писали:
А>>Возможно ли такое? (без буста).
WM>
return dynamic_cast<T*>(array[i]);

dynamic_cast выкидывает исключение если ему подсунуть нулевой указатель. Если это не желательно, а хотелось бы получить либо указатель, либо нулевой указатель, то можно чуть модифицировать:
return (array[i] != NULL) ? dynamic_cast<T*>(array[i]) : NULL;


Ну и надо помнить, что это использование RTTI и это дает некоторое уменьшение производительности
Re[3]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: uzhas Ниоткуда  
Дата: 12.02.13 14:15
Оценка: +1
Здравствуйте, Serg27, Вы писали:

S>dynamic_cast выкидывает исключение если ему подсунуть нулевой указатель

это неверно. возвращается ноль

S>Ну и надо помнить, что это использование RTTI и это дает некоторое уменьшение производительности

это верно
Re[3]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: watch-maker  
Дата: 12.02.13 14:19
Оценка:
Здравствуйте, Serg27, Вы писали:

WM>>
return dynamic_cast<T*>(array[i]);

S>dynamic_cast выкидывает исключение если ему подсунуть нулевой указатель.
Нет.

5.2.7 Dynamic cast
4. If the value of v is a null pointer value in the pointer case, the result is the null pointer value of type T.

Re[4]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 12.02.13 15:00
Оценка:
Здравствуйте, uzhas, Вы писали:

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


S>>dynamic_cast выкидывает исключение если ему подсунуть нулевой указатель

U>это неверно. возвращается ноль

Для VS2003 и 2010 это так. Цитата из MSDN: здесь
The value of a failed cast to pointer type is the null pointer. A failed cast to reference type throws a bad_cast Exception. If expression does not point to or reference a valid object, a __non_rtti_object exception is thrown.
Re[5]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: watch-maker  
Дата: 12.02.13 15:15
Оценка:
Здравствуйте, Serg27, Вы писали:

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


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


S>>>dynamic_cast выкидывает исключение если ему подсунуть нулевой указатель

U>>это неверно. возвращается ноль

S>Для VS2003 и 2010 это так. Цитата из MSDN: здесь

S>The value of a failed cast to pointer type is the null pointer. A failed cast to reference type throws a bad_cast Exception. If expression does not point to or reference a valid object, a __non_rtti_object exception is thrown.

Ты не то цитируешь. Смотри по своей же ссылке:

A null pointer value is converted to the null pointer value of the destination type by dynamic_cast.

Re[6]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 12.02.13 15:24
Оценка:
Здравствуйте, watch-maker, Вы писали:

WM>Ты не то цитируешь. Смотри по своей же ссылке:

A null pointer value is converted to the null pointer value of the destination type by dynamic_cast.


Да, действительно. Но это мое мнение основано на реальном баге в производственном коде. Возможно за давностью лет что-то уже забылось, но сейчас не на работе — проверить не могу. Завтра посмотрю более точную причину. Да и было это с VS2003. Ну все равно завтра...
Re[7]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: uzhas Ниоткуда  
Дата: 12.02.13 16:13
Оценка:
Здравствуйте, Serg27, Вы писали:

S>Но это мое мнение основано на реальном баге в производственном коде.


я могу потелепатировать: в студии dynamic_cast указателей кидает исключение в случае, если указатель пришел из библиотеки, которая скомпилирована без поддержки rtti
исключение вроде имеет тип __non_rtti_object
про баги не слышал, а с таким поведением встречался
Re: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 13.02.13 00:22
Оценка: -1
Здравствуйте, Аноним, Вы писали:

А>Есть полиморфный массив, то есть массив указателей BaseItem*, каждый из которых реально указывает на объект какого-то класса-наследника BaseItem. Наследников много и все они образуют довольно ветвистое дерево.


А>Нужна шаблонаня функция вида GetItem<T>(int i), которая должна сделать следующее: если реальный тип i-того элемент массива реально является классом T или наследником T (где в качестве T передается некоторый наследник BaseItem), то вернуть адрес этого объекта, иначе вернуть null.

А>Возможно ли такое? (без буста).

Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 13.02.13 07:06
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, Аноним, Вы писали:


А>>Есть полиморфный массив, то есть массив указателей BaseItem*, каждый из которых реально указывает на объект какого-то класса-наследника BaseItem. Наследников много и все они образуют довольно ветвистое дерево.


А>>Нужна шаблонаня функция вида GetItem<T>(int i), которая должна сделать следующее: если реальный тип i-того элемент массива реально является классом T или наследником T (где в качестве T передается некоторый наследник BaseItem), то вернуть адрес этого объекта, иначе вернуть null.

А>>Возможно ли такое? (без буста).

J>Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.

Кстати тоже подумал про визитор, но тут он не нужен.
Sic luceat lux!
Re[8]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 13.02.13 08:49
Оценка:
Здравствуйте, uzhas, Вы писали:

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


S>>Но это мое мнение основано на реальном баге в производственном коде.


U>я могу потелепатировать: в студии dynamic_cast указателей кидает исключение в случае, если указатель пришел из библиотеки, которая скомпилирована без поддержки rtti

U>исключение вроде имеет тип __non_rtti_object
U>про баги не слышал, а с таким поведением встречался

Телепатия на половину правильная. Поведение описано абсолютно точно . Но насчет RTTI сразу исключалось — дисциплина сборки и компиляции этого не допускала.

А баг у нас был такой. Раз в несколько месяцев программа падала. Сообщение соответствовало ("__non_rtti_object"). При повторе действий ничего не выявлялось. Именно тогда я и предположил, что это происходит из dynamic_cast который у нас применялся для проверки указателей и преобразований типов. Если ему суется невалидный указатель, то это и могло происходить. А невалидным он мог стать, например, если был произведен delete (маловероятно, так как использовали shared_ptr) или переписью мусором. Баг мы не нашли, так как была переписана подсистема, которая и так вызывала подозрения и нарекания. Он просто исчез.
Из этой истории я себе хорошо запомнил, что в dynamic_cast надо пихать только валидные указатели, иначе именно в этом месте будет выброшено исключение. В этом поведение dynamic_cast отличается от static_cast. C течением времени сама история забылась, а правило про валидные указатели и dynamic_cast в голове осталось. Еще с течением времени в голове "валидные" указатели преобразовались в "валидные" и "не нулевые". Ну вот отсюда и моя ошибка. Сейчас все в голове обновилось. thanks...
Re[3]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 13.02.13 09:25
Оценка:
Здравствуйте, Kernan, Вы писали:

J>>Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.

K>Кстати тоже подумал про визитор, но тут он не нужен.

хз. Имхо, очень редко бывает, что надо просто узнать тип, чаще что-нть нужно со всем найденным сделать, причем условия на выполнение имеют тенденцию усложняться — вот тут-то визитор и понадобится.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 13.02.13 14:35
Оценка:
Здравствуйте, jazzer, Вы писали:

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


J>>>Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.

K>>Кстати тоже подумал про визитор, но тут он не нужен.

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

А можешь сказать юзкей когда надо узнать тип и что-то с ним сделать? Не правильнее ли просто сделать виртуальные методы и не узнавать тип либо использовать двойную диспетчеризацию, к примеру?
Sic luceat lux!
Re[2]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Vamp Россия  
Дата: 13.02.13 16:00
Оценка: +1
J>Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.
А почему через визитер, а не просто виртуальными функциями?
Да здравствует мыло душистое и веревка пушистая.
Re[9]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 13.02.13 17:25
Оценка:
Здравствуйте, Serg27, Вы писали:

S>Из этой истории я себе хорошо запомнил, что в dynamic_cast надо пихать только валидные указатели, иначе именно в этом месте будет выброшено исключение. В этом поведение dynamic_cast отличается от static_cast.


Не совсем так.
Во-первых, формально, в С++ с невалидными указателями много чего нельзя делать, например копировать их небезопасно.
Но это всё теоретические несколько UB, что же касается практики, то опасно преобразовывать мусорные указатели такого типа, для преобразований которых надо лезть в данные.
А это не только RTTI, но и преобразования к от виртуальной базы, например
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 13.02.13 17:54
Оценка: +1 -1
Здравствуйте, Vamp, Вы писали:

J>>Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.

V>А почему через визитер, а не просто виртуальными функциями?

визитер — это, можно сказать, внешняя виртуальная функция.

С нормальными виртуальными функциями ты всю функциональность вставляешь прямо в классы иерархии, даже если они там не нужны (по Single Responsibility Principle, например), а нужны лишь в одном только месте программы.
Т.е. представь, что тебе надо как-то хитро распечатывать объекты, причем в 10 разных местах программы это нужно делать по-разному (и только в этих местах, т.е. reuse=0). С виртуальными функциями тебе придется сделать все эти 10 функций прямо в классах, несмотря на то, что каждая будет зваться ровно в одном месте программы — это ж ужас.

С визиторами же ты можешь наделать сколько угодно разнообразных визиторов, каждый из которых будет локальным в данном месте программы, и это не потребует изменения классов иерархии.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 13.02.13 17:55
Оценка:
Здравствуйте, Kernan, Вы писали:

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


Про виртуальные фукнции я уже рядом ответил (и юзкейс там же), а про двойную диспетчеризацию — так визитер этим и занимается.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Vamp Россия  
Дата: 13.02.13 18:18
Оценка:
J>С визиторами же ты можешь наделать сколько угодно разнообразных визиторов, каждый из которых будет локальным в данном месте программы, и это не потребует изменения классов иерархии.
Не, теорию визитеров я знаю. Я имею в виду, почему ты предложил их в этом конкретном случае.
Да здравствует мыло душистое и веревка пушистая.
Re[4]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 13.02.13 20:57
Оценка:
Здравствуйте, jazzer, Вы писали:

J>С визиторами же ты можешь наделать сколько угодно разнообразных визиторов, каждый из которых будет локальным в данном месте программы, и это не потребует изменения классов иерархии.


А потом понадобится добавить в иерархию ещё пяток классов, а из уже имеющихся 150, примерно 40 переписать.

Вот потом сиди и выискивай по всему клиентскому коду этих визиоров и приводи их в соответствие
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 13.02.13 20:59
Оценка:
Здравствуйте, jazzer, Вы писали:

J>визитер — это, можно сказать, внешняя виртуальная функция.


На самом деле это способ прибить гвоздями в 100500 местах программы сведения о ВСЕХ членах иерархии...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 13.02.13 21:48
Оценка:
Здравствуйте, jazzer, Вы писали:

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


J>>>Корректно это делается через паттерн Visitor. Шаблоны тут не помогут, так как у тебя динамика, а не статика.

V>>А почему через визитер, а не просто виртуальными функциями?

J>визитер — это, можно сказать, внешняя виртуальная функция.


J>С нормальными виртуальными функциями ты всю функциональность вставляешь прямо в классы иерархии, даже если они там не нужны (по Single Responsibility Principle, например), а нужны лишь в одном только месте программы.

J>Т.е. представь, что тебе надо как-то хитро распечатывать объекты, причем в 10 разных местах программы это нужно делать по-разному (и только в этих местах, т.е. reuse=0). С виртуальными функциями тебе придется сделать все эти 10 функций прямо в классах, несмотря на то, что каждая будет зваться ровно в одном месте программы — это ж ужас.

J>С визиторами же ты можешь наделать сколько угодно разнообразных визиторов, каждый из которых будет локальным в данном месте программы, и это не потребует изменения классов иерархии.

Ну а если иерархия растёт мы не получим неприятный зуд пятой точки от того, что придётся всю иерархию перебирать встраивая новый класс?
  Скрытый текст
А давайте использовать метапрограммирование для генерации визитёрки!
Sic luceat lux!
Re[5]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 14.02.13 00:14
Оценка:
Здравствуйте, Erop, Вы писали:

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


J>>визитер — это, можно сказать, внешняя виртуальная функция.


E>На самом деле это способ прибить гвоздями в 100500 местах программы сведения о ВСЕХ членах иерархии...


Да, это tradeoff. Жизнь инженера состоит из tradeoff-ов.

Либо ты вкрячиваешь всю функциональность прямо в классы иерархии, даже если она используется ровно в одном месте, либо выносишь все это в визиторы и получаешь чистую иерархию, но тебе придется в визитерах закодировать иерархию (с нужной тебе в данном месте степенью детализации, которая может быть весьма низкой, кстати).
Ну и решение через RTTI — это визитер для бедных, так как тебе там тоже придется закодировать всю иерархию.

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

Пример — HTML DOM. Как ты будешь его обрабатывать? Неужели вставишь всю логику виртуально прямо в классы тегов?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 14.02.13 00:16
Оценка:
Здравствуйте, Vamp, Вы писали:

J>>С визиторами же ты можешь наделать сколько угодно разнообразных визиторов, каждый из которых будет локальным в данном месте программы, и это не потребует изменения классов иерархии.

V>Не, теорию визитеров я знаю. Я имею в виду, почему ты предложил их в этом конкретном случае.
Потому что в этом случае (узнать, наследник ты определенного класс или нет) виртуальная функция не поможет, так как класс тебе нужен каждый раз разный.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 02:20
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Потому что в этом случае (узнать, наследник ты определенного класс или нет) виртуальная функция не поможет, так как класс тебе нужен каждый раз разный.


У класса может быть какой-то ID...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 14.02.13 02:22
Оценка:
Здравствуйте, Erop, Вы писали:

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


J>>Потому что в этом случае (узнать, наследник ты определенного класс или нет) виртуальная функция не поможет, так как класс тебе нужен каждый раз разный.


E>У класса может быть какой-то ID...


Т.е. RTTI своими руками? Ну и нафига? Не говоря уже о том, что засунуть в ID произвольные отношения наследования — нетривиальная задача.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[10]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 02:38
Оценка:
Здравствуйте, Erop, Вы писали:

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


S>>Из этой истории я себе хорошо запомнил, что в dynamic_cast надо пихать только валидные указатели, иначе именно в этом месте будет выброшено исключение. В этом поведение dynamic_cast отличается от static_cast.


E>Не совсем так.

E>Во-первых, формально, в С++ с невалидными указателями много чего нельзя делать, например копировать их небезопасно.
E>Но это всё теоретические несколько UB, что же касается практики, то опасно преобразовывать мусорные указатели такого типа, для преобразований которых надо лезть в данные.
E>А это не только RTTI, но и преобразования к от виртуальной базы, например
Мне всегда казалось, что static_cast делается во время компиляции, поэтому он не должен участвовать при преобразовании от виртуальной базы:
struct Base {};
struct Derived : public virtual Base {};

int main()
{
    Base* b = new Derived;
    Derived* d = static_cast<Derived*>(b);
}

Этот фрагмент не компилируется. здесь Или имелось в виду какое-то другое преобразование?
Re[6]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 03:10
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Пример — HTML DOM. Как ты будешь его обрабатывать? Неужели вставишь всю логику виртуально прямо в классы тегов?


Я не против визитёра вообще, я против того, что бы его пихать где надо и не надо.
В частности в обсуждаемой в этой ветке проблеме скорее не надо, чем надо
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 03:11
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Т.е. RTTI своими руками? Ну и нафига? Не говоря уже о том, что засунуть в ID произвольные отношения наследования — нетривиальная задача.


Зачем своими? Можно и билиотеку какую-нибдуь взять и RTTI как таковой, кстати, тоже...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 14.02.13 03:18
Оценка:
Здравствуйте, Erop, Вы писали:

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


J>>Т.е. RTTI своими руками? Ну и нафига? Не говоря уже о том, что засунуть в ID произвольные отношения наследования — нетривиальная задача.


E>Зачем своими? Можно и билиотеку какую-нибдуь взять и RTTI как таковой, кстати, тоже...

Ну и получится тот же самый визитёр, только в рукопашную через RTTI.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[7]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 14.02.13 03:22
Оценка:
Здравствуйте, Erop, Вы писали:

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


J>>Пример — HTML DOM. Как ты будешь его обрабатывать? Неужели вставишь всю логику виртуально прямо в классы тегов?


E>Я не против визитёра вообще, я против того, что бы его пихать где надо и не надо.

Где не надо — тут вопросов нет, но в чем проблема с тем, чтобы пихать его где надо?

E>В частности в обсуждаемой в этой ветке проблеме скорее не надо, чем надо

Согласен, при такой куцой постановке задачи достаточно прямого RTTI. Но, по опыту, элементарными задачами дело обычно не исчерпывается.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[11]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 03:27
Оценка:
Здравствуйте, Serg27, Вы писали:

E>>А это не только RTTI, но и преобразования к от виртуальной базы, например

S>Мне всегда казалось, что static_cast делается во время компиляции, поэтому он не должен участвовать при преобразовании от виртуальной базы:

Вот тебе вообще без static_cast...
struct Base { virtual~Base() {} };
struct Der1 : public virtual Base { int F1; };
struct Der2 : public virtual Base { int F2; };
struct Der3 : Der1, Der2 { int F3; };

Base* toBase( Der2* p ) { return p; }

void foo()
{
    delete toBase( new Der2 );
    delete toBase( new Der3 );
}

там же
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 03:32
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну и получится тот же самый визитёр, только в рукопашную через RTTI.


От задачи завичит...
Скажем, мы хотим профильтровать дерево по типам узлов. Зачем там визитёр?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 03:35
Оценка:
Здравствуйте, jazzer, Вы писали:

E>>Я не против визитёра вообще, я против того, что бы его пихать где надо и не надо.

J>Где не надо — тут вопросов нет, но в чем проблема с тем, чтобы пихать его где надо?
Где действительно надо, ты никуда от него не денешься...

E>>В частности в обсуждаемой в этой ветке проблеме скорее не надо, чем надо

J>Согласен, при такой куцой постановке задачи достаточно прямого RTTI. Но, по опыту, элементарными задачами дело обычно не исчерпывается.

Дык по опыту, опять же, по умолчанию надо смотреть на решения с виртуальными функциями.
Ворой популярный юзкейс -- поддержка чего-то вроде QueryInterface. Типа спрашиваем у типа, умеет ли он так. Если умеет, то делаем. А визитёр надо в 0.0001% случаев
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: dilmah США  
Дата: 14.02.13 03:54
Оценка: +1
S>Из этой истории я себе хорошо запомнил, что в dynamic_cast надо пихать только валидные указатели, иначе именно в этом месте будет выброшено исключение. В этом поведение dynamic_cast отличается от static_cast.

это очень наивно (про отличие от static_cast).
static_cast в случае множественного, особенно виртуального наследования делает весьма нетривиальные вещи, которые могут приводить к undefined behavior (и сегфолтам) в случае невалидного указателя.
Re[11]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: jazzer Россия Skype: enerjazzer
Дата: 14.02.13 03:59
Оценка:
Здравствуйте, Erop, Вы писали:

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


J>>Ну и получится тот же самый визитёр, только в рукопашную через RTTI.


E>От задачи завичит...

E>Скажем, мы хотим профильтровать дерево по типам узлов. Зачем там визитёр?

Не, ты не понял, что я имел в виду. Я говорю о том, что рукопашное решение через RTTI — это тот же самый визитёр, просто вид сбоку.
Рукопашное решение:
if (typeid(x) == typeid(T1)) {....}
else if (typeid(x) == typeid(T1)) {....}
...

Визитёр:
struct Visitor {
  void visit(T1& x) {....}
  void visit(T2& x) {....}
  ...
};

И то, и другое придется переписывать, если, как ты там сказал, "понадобится добавить в иерархию ещё пяток классов, а из уже имеющихся 150, примерно 40 переписать".
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 05:29
Оценка:
Здравствуйте, Erop, Вы писали:

А причем здесь преобразование в стиле C?

Напомню речь шла о различии dynamic_cast и static_cast. Последний не выбрасывает исключений при своей работе. Потом конечно может быть что угодно при использовании полученного указателя, если исходный был не валидный. Это было то, что я сказал.
Вами была сделана поправка, что невалидные указатели в общем-то и копировать нельзя. Согласен и могу представить такие машинные архитектуры. Ну и потом Вы сказали —
E>что же касается практики, то опасно преобразовывать мусорные указатели такого типа, для преобразований которых надо лезть в данные.
E>А это не только RTTI, но и преобразования к от виртуальной базы, например

Я и удивился — static_cast обычно делается во время компиляции и для приведения от виртуальной базы не годится. Если же имеется в виду преобразование в "стиле C", то конечно может произойти что угодно. Здесь обсуждать нечего.

Мне действительно интересно — можно ли придумать ситуацию, когда static_cast указателя приведет к выбросу исключения. Вопрос о том что будет потом при использовании полученного указателя давайте обсуждать не будем — тут все понятно. Ну и такой должен сработать на современных распространенных архитектурах.
Re[10]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 05:35
Оценка:
Здравствуйте, dilmah, Вы писали:

D>это очень наивно (про отличие от static_cast).

Речь идет о выбросе исключений или segvol во время выполнения static_cast для указателя. Спорить с тем, что будет при использовании полученного указателя потом, я не собирался и не буду. Если это кто-то утверждал, то не я.

Если есть такой пример неприятностей во время выполнения static_cast, то интересно посмотреть.

Хотя конечно это вопрос академический — неприятности все равно будут, рано или поздно.
Re[11]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: dilmah США  
Дата: 14.02.13 05:45
Оценка:
S>Если есть такой пример неприятностей во время выполнения static_cast, то интересно посмотреть.

неприятности именно во время выполнения.
При виртуальном наследовании для того чтобы сделать static_cast из базового типа в производный во всех разумных реализациях происходит обращение к памяти объекта (потому что смещение нельзя сделать константным как при невиртуальном наследовании, поэтому недостаточно просто прибавить/отнять константу). То есть во время выполнения static_cast будет происходить обращение к памяти рядом с указателем, что для плохого указателя чревато.
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 06:08
Оценка: 8 (1)
Здравствуйте, dilmah, Вы писали:


S>>Если есть такой пример неприятностей во время выполнения static_cast, то интересно посмотреть.


D>неприятности именно во время выполнения.

D>При виртуальном наследовании для того чтобы сделать static_cast из базового типа в производный во всех разумных реализациях происходит обращение к памяти объекта (потому что смещение нельзя сделать константным как при невиртуальном наследовании, поэтому недостаточно просто прибавить/отнять константу). То есть во время выполнения static_cast будет происходить обращение к памяти рядом с указателем, что для плохого указателя чревато.

static_cast для этого случая не компилируется. Причины Вы указали — нужно обращение к памяти объекта в runtime. Пример я приводил:
#include <iostream>
using namespace std;
struct Base 
{
virtual ~Base() {};
};
struct Derived : public virtual Base {};

int main()
{
    Base* b = new Derived;
#if 0
   Derived* d = dynamic_cast<Derived*>(b);
#else   
  Derived* d = static_cast<Derived*>(b); 
#endif  
    cout << d0 << endl;
}


Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?
Re[13]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 07:28
Оценка: 4 (1)
Здравствуйте, Serg27, Вы писали:

S>Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?


Да, обратное. Его можно и static_cast'ом сделать, если охота...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 08:25
Оценка:
Здравствуйте, Serg27, Вы писали:

S>Мне действительно интересно — можно ли придумать ситуацию, когда static_cast указателя приведет к выбросу исключения. Вопрос о том что будет потом при использовании полученного указателя давайте обсуждать не будем — тут все понятно. Ну и такой должен сработать на современных распространенных архитектурах.


Собственно вот...
Я так понимаю, что "компилируется" в ответ на "запустить" обозначает, что до return в main мы не добрались
На MSVC 8 всё так, как тебе хотелось -- GP внутри static_cast...

Только он там не обязвтельный, даже если его убрать и оставить неявное приведение типов, всё равно жахнет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 08:31
Оценка:
Здравствуйте, Erop, Вы писали:

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


S>>Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?


E>Да, обратное. Его можно и static_cast'ом сделать, если охота...

Да, действительно: (здесь)

struct Base {};
struct D : public virtual Base {};
int main()
{
    D* d = new D;
    Base * b = static_cast<Base*>(d);
    cout << b << endl;
    d = NULL;
    b = static_cast<Base*>(d);
    cout << b << endl;
    d = (D*)32;
    cout << "d=" << d <<endl;
    b = static_cast<Base*>(d);
    cout << b << endl;
}


static_cast хоть и compile-time оператор, но лезет в этом случае за смещением на которое надо сдвинуть указатель в память. Ну значит получать указатель на виртуальную базу надо с большой осторожностью. Хорошо, что мне пришлось за всю жизнь всего один раз это использовать...
Re[14]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 08:44
Оценка: +1 :)
Здравствуйте, Erop, Вы писали:

E
E>Собственно вот...
E>Я так понимаю, что "компилируется" в ответ на "запустить" обозначает, что до return в main мы не добрались
это означает, что загрузка сервера велика, и он все еще компилирует. У меня когда я зашел статус был — "успешно"
Т.е. ничего не падает. Вот мой вариант с печатью в конце — здесь

Я ниже уже дал вариант, когда происходит падение на static_cast — здесь
Автор: Serg27
Дата: 14.02.13

Все таки мы его завалили
Re[15]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 10:33
Оценка:
Здравствуйте, Serg27, Вы писали:

E>>Собственно вот...

S>Т.е. ничего не падает. Вот мой вариант с печатью в конце — здесь

S>Я ниже уже дал вариант, когда происходит падение на static_cast — здесь
Автор: Serg27
Дата: 14.02.13

S>Все таки мы его завалили

Ну просто эта ideone зачем-то оптимизирует и всё выкинула.

Надо вот так просто сделать было и всё...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 15.02.13 05:06
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Рукопашное решение:

J>
J>if (typeid(x) == typeid(T1)) {....}
J>else if (typeid(x) == typeid(T1)) {....}
J>...
J>

J>Визитёр:
J>
J>struct Visitor {
J>  void visit(T1& x) {....}
J>  void visit(T2& x) {....}
J>  ...
J>};
J>


Может быть и так:
if( IIinterface1* i1 = x->QI<IIinterface1*>() ) {
    i1->useInterfce1();
}
if( IIinterface2* i2 = x->QI<IIinterface2*>() ) {
    i1->useInterfce2();
}


J>И то, и другое придется переписывать, если, как ты там сказал, "понадобится добавить в иерархию ещё пяток классов, а из уже имеющихся 150, примерно 40 переписать".


Тогда переписывать может и не понадобиться...

Фишка же не в том, что бы формально не использовать визитёра, и как-то это дело закомуфлировать, а в том, что бы так спроектировать программу, что визитёр будет не нужен...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Безопасность static_cast (резюме обсуждения)
От: Serg27  
Дата: 17.02.13 10:21
Оценка:
.
S>Из этой истории я себе хорошо запомнил, что в dynamic_cast надо пихать только валидные указатели, иначе именно в этом месте будет выброшено исключение. В этом поведение dynamic_cast отличается от static_cast.

Это было основано на моем понимании static_cast и dynamic_cast:
static_cast выполняется в compile-time и представляет просто добавление константы к значению указателя
dynamic_cast выполняется в run-time и нуждается в информации, которая связана с конкретным объектом на который указывает указатель.

На это мое замечание Егор и Dilmah ответили, что жизнь сложнее, с невалидными указателями работать надо осторожно (по стандарту копировать их и то опасно). С практической точки зрения было указано на опасность преобразования указателя на объект использующий виртуальный базовый класс. (Естественно не обсуждался вопрос что любое использование полученного значения указателя может привести к непредсказуемым последствиям — это и так понятно)
В процессе обсуждения было выяснено, что static_cast не может быть использован для преобразования от виртуального базового класса к указателю на наследник. Это просто не компилируется, так как для такого преобразования нужна run-time информация.
Но Егор указал пример обратного преобразования, который благополучно компилировался и оканчивался крахом программы. В упрощенном виде:
struct Base {};
struct D : public virtual Base {};
int main()
{
    D* d = new D;
    Base * b = static_cast<Base*>(d);
    cout << b << endl;
    d = NULL;
    b = static_cast<Base*>(d);
    cout << b << endl;
    d = (D*)32;
    cout << "d=" << d <<endl;
    b = static_cast<Base*>(d);  //здесь происходит крах программы
    cout << b << endl;
}

Пример, когда использование static_cast невалидного указателя приводит краху, был найден. Но я задумался над словами Егора, который он сказал несколько раз — "здесь можно и без static_cast". Действительно, преобразования указателя к базовому типу является одним из основных преобразований в C++ и не требует использования никакого из явных операторов преобразования c++, ни тем более преобразования в стиле C. Т.е. static_cast в этом случае просто не требуется, а компилятор милостиво нам прощает эту неточность:
#include <iostream>
using namespace std;
struct Base {};
struct D : public virtual Base {};
int main()
{
    D* d = new D;
    Base * b = d;
    cout << b << endl;
    d = NULL;
    b = d;
    cout << b << endl;
    d = (D*)32;
    cout << "d=" << d <<endl;
    b = d;
    cout << b << endl;
}

Таким образом, я бы свое утверждения сформулировал так — если у нас есть указатель, и нам нужно преобразовать его к другому типу, и для этого необходимо использовать static_cast, то в практическом смысле это не приведет ни к каким исключениям или краху программы во время выполнения static_cast вне зависимости от валидности исходного казателя. Конечно, можно представить себе машинную архитектуру, которая контролирует указатели run-time, но я не слышал о таких современных архитектурах. Ну и конечно, речь идет именно о чистых указателях, а не о итераторах, пользовательских указателях-объектах и т.д. и т.п.)

Конечно вопрос довольно академический, но я могу представить что это можно использовать при поиске багов указателей при использовании системных функций типа IsBadWritePtr(), _CrtIsValidHeapPointer, _CrtIsValidPointer
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.