Информация об изменениях

Сообщение Re[3]: шаблонный виртуальный метод от 29.06.2015 18:48

Изменено 29.06.2015 19:10 watchmaker

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


BFE>Всё это похоже на отговорки, потому, что:

BFE>1. приведённый код можно разбросать по многим файлам и он будет работать (и компилироваться, и линковаться).
Конечно. Но смысл раздельной компиляции и наследования не в том, что можно один cpp файл разбить на два, а в том, что можно отдельно откомпилировать код предка, которому не нужно знать о всех-всех способах использования своих наследников.

BFE>При этом внешняя шаблонная функция, заменяющая таблицу вызовов для виртуальных шаблонных методов, может быть создана кодогенератором (это сложно, но никакой интеллектуальной работы требующей участия человека не требуется)

Верно, составить список в целом не проблема.

BFE>2. реализация вызова виртуальных методов не обязана базироваться на единой таблице — стандарт этого не требует.

Да, можешь и хочешь заменить vtable на что-то другое — пожалуйста.

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

Хорошо, запустили компилятор один раз и собрали сигнатуры. А что дальше? Запускать компиляцию по второму кругу чтобы добавить код для найденных сигнатур? Потом ещё раз чтобы пересобрать появившееся зависимости? И ещё и ещё раз пока процесс не сойдётся когда-нибудь? И это не просто каждого файла в отдельности (это как раз тривиально и просто, как у тебя в исходном примере кода и написано), а для совокупности всех исходников и всех библиотек. (Для библиотек, кстати говоря, не для всех доступны исходники чтобы перекомпиляцию запустить — их и объектные файлы тоже нужно запретить в мире c++ как и dll/so?).

Ты так себе видишь процесс компиляции? Если нет, то распиши, пожалуйста, свою схему по-подробнее с точки зрения компилятора: вот мы взяли файл a.cpp (с базовым классом) и на выходе получили такой-то код для таких сигнатур + различные вспомогательные таблицы. Потом взяли файл b.cpp (с наследником) и на выходе получили такой-то код для других сигнатур и другие таблицы. Потом запускам linker и собираем программу. Вот и опиши, что должен делать linker и что должно быть в коде и в таблицах чтобы программа собралась. Для классических виртуальных функций такие действия просты и известны. Для экспорта шаблонов известна пара костылей, которая нормально так и не используется. А что будет в твоём случае?

BFE>Сейчас шаблонные функции тоже разбросаны по разным модулям и что? Ведь все шаблонные функции с одинаковой сигнатурой заменяются единственной реализацией. Что сложного в том, чтобы взять адрес такой функции и записать его в таблицу? Не понимаю.

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

W>>В варианте без шаблонов просто создаётся vtable со всеми возможными сигнатурами уже известных виртуальных функций, а наследники её используют. В случае же шаблонов что и сколько класть в такую vtable неизвестно пока не получен список всех наследников и всех используемых специализаций.

BFE>Наследник может добавить новый виртуальный метод в таблицу. Это не сильно отличается от того, что новая специализация может добавить ещё одну строчку в таблицу. Какая в этом сложность?
Есть отличие принципиальное — наследник не модифицирует при этом код предка. Предок вообще не знает, что какой-то метод у него добавился. Для ВШФ же нужно изменить код предка после обнаружения какой-то специализации и научить предка с этим работать.

W>>наследник может объявится где-нибудь в сторонней dll или so.

BFE>Можно не поддерживать dll и so для этих методов. Кстати, наследник поменяет не больше, чем обычная перегрузка виртуального метода. Поменять таблицу может только новая специализация — она добавляет ещё одну функцию в таблицу. Что в этом сложного?
Откуда одна dll узнает, что нужно сгенерировать специализацию foo<int> в базовом классе, если внутри себя она foo<int> не использует, а из другой dll этот метод будет требоваться по зависимостям из какого наследника?

W>>Так экспорт шаблонов наоборот убрали из стандарта. А ждать шаблонные виртуальные методы раньше возврата экспорта ну явно не стоит.

BFE>Хмм. А причём тут экспорт шаблонов?
Экспорт шаблонов — это отличная база для реализации шаблонных виртуальных функций. А если у тебя есть шаблонные виртуальные методы, то ты сразу получаешь экспорт шаблонов. По сути в реализации вся сложность именно в экспорте, а виртуальность поверх него уже относительно просто добавить. Но экспорт не реализуется за приемлемую стоимость.
Re[3]: шаблонный виртуальный метод
Здравствуйте, B0FEE664, Вы писали:

И ещё давай уточним всё же насчёт границ применимости. У тебя какие ограничения предполагаются? Минимальные, когда вся программа умещается в одном cpp файле или более расслабленные? Просто когда есть только один cpp-файл, или хотя бы все шаблонные виртуальные классы со всеми зависимостями заперты в одном cpp-файле, и есть полный список всех допустимых наследников и всех требуемых сигнатур (список хоть ручной, хоть автогенерированный — не важно), то это одна ситуация. А если есть хотя бы два объектных файла с зависимостями между ними, то другая и при этом принципиально более сложная.

BFE>Всё это похоже на отговорки, потому, что:

BFE>1. приведённый код можно разбросать по многим файлам и он будет работать (и компилироваться, и линковаться).
Конечно. Но смысл раздельной компиляции и наследования не в том, что можно один cpp файл разбить на два, а в том, что можно отдельно откомпилировать код предка, которому не нужно знать о всех-всех способах использования своих наследников.

BFE>При этом внешняя шаблонная функция, заменяющая таблицу вызовов для виртуальных шаблонных методов, может быть создана кодогенератором (это сложно, но никакой интеллектуальной работы требующей участия человека не требуется)

Верно, составить список в целом не проблема.

BFE>2. реализация вызова виртуальных методов не обязана базироваться на единой таблице — стандарт этого не требует.

Да, можешь и хочешь заменить vtable на что-то другое — пожалуйста.

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

Хорошо, запустили компилятор один раз и собрали сигнатуры. А что дальше? Запускать компиляцию по второму кругу чтобы добавить код для найденных сигнатур? Потом ещё раз чтобы пересобрать появившееся зависимости? И ещё и ещё раз пока процесс не сойдётся когда-нибудь? И это не просто каждого файла в отдельности (это как раз тривиально и просто, как у тебя в исходном примере кода и написано), а для совокупности всех исходников и всех библиотек. (Для библиотек, кстати говоря, не для всех доступны исходники чтобы перекомпиляцию запустить — их и объектные файлы тоже нужно запретить в мире c++ как и dll/so?).

Ты так себе видишь процесс компиляции? Если нет, то распиши, пожалуйста, свою схему по-подробнее с точки зрения компилятора: вот мы взяли файл a.cpp (с базовым классом) и на выходе получили такой-то код для таких сигнатур + различные вспомогательные таблицы. Потом взяли файл b.cpp (с наследником) и на выходе получили такой-то код для других сигнатур и другие таблицы. Потом запускам linker и собираем программу. Вот и опиши, что должен делать linker и что должно быть в коде и в таблицах чтобы программа собралась. Для классических виртуальных функций такие действия просты и известны. Для экспорта шаблонов известна пара костылей, которая нормально так и не используется. А что будет в твоём случае?

BFE>Сейчас шаблонные функции тоже разбросаны по разным модулям и что? Ведь все шаблонные функции с одинаковой сигнатурой заменяются единственной реализацией. Что сложного в том, чтобы взять адрес такой функции и записать его в таблицу? Не понимаю.

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

W>>В варианте без шаблонов просто создаётся vtable со всеми возможными сигнатурами уже известных виртуальных функций, а наследники её используют. В случае же шаблонов что и сколько класть в такую vtable неизвестно пока не получен список всех наследников и всех используемых специализаций.

BFE>Наследник может добавить новый виртуальный метод в таблицу. Это не сильно отличается от того, что новая специализация может добавить ещё одну строчку в таблицу. Какая в этом сложность?
Есть отличие принципиальное — наследник не модифицирует при этом код предка. Предок вообще не знает, что какой-то метод у него добавился. Для ВШФ же нужно изменить код предка после обнаружения какой-то специализации и научить предка с этим работать.

W>>наследник может объявится где-нибудь в сторонней dll или so.

BFE>Можно не поддерживать dll и so для этих методов. Кстати, наследник поменяет не больше, чем обычная перегрузка виртуального метода. Поменять таблицу может только новая специализация — она добавляет ещё одну функцию в таблицу. Что в этом сложного?
Откуда одна dll узнает, что нужно сгенерировать специализацию foo<int> в базовом классе, если внутри себя она foo<int> не использует, а из другой dll этот метод будет требоваться по зависимостям из какого наследника?

W>>Так экспорт шаблонов наоборот убрали из стандарта. А ждать шаблонные виртуальные методы раньше возврата экспорта ну явно не стоит.

BFE>Хмм. А причём тут экспорт шаблонов?
Экспорт шаблонов — это отличная база для реализации шаблонных виртуальных функций. А если у тебя есть шаблонные виртуальные методы, то ты сразу получаешь экспорт шаблонов. По сути в реализации вся сложность именно в экспорте, а виртуальность поверх него уже относительно просто добавить. Но экспорт не реализуется за приемлемую стоимость.