cNoNim
>> мне не совсем понятно если честно как решение пускай даже на дискрипторах поможет в описываемом тобой случае
Дык элементарно просто - у контекста будет метод clone(), который создаст копию самого контекста и в ней - копии внутренних таблиц текстур/шейдеров, а также вызовет метод wglShareLists(). После чего полученные юзером дексрипторы (айдишники, фактически - смещения внутри таблиц) безо всяких изменений можно будет использовать в обоих контекстах.
В случае double dispatching'а все еще проще, потому что внутренних таблиц у нас нет :) Сразу после wglShareLists() выданные юзеру интерфейсы можно будет биндить в обоих контекстах.
В случае же жесткой привязки текстуры к контексту будет ой-ой-ой.
>> мульти-апи
Забудь про мульти-апи, разговор теперь более простой - про скрытие реализаций двух сильно связанных интерфейсов и возможности их последующего совместного использования.
crsib
Я не очень жестко вел дискуссию? Извини, если обидел. Просто очень не люблю тайпкасты, вот меня и понесло.
Z
Земледелец, извини - я, по-моему, чересчур жестко отреагировал на твой первый пост. Меня сегодня понесло что-то :(
winter
> (Type *)
C-каст. Фи)
winter
> а) дурной тон вообще;
В общем случае, да.
winter
> описанные в этом треде случаи даже не близки к крайним
Описанные в данном треде позволяют безопасный static_cast
winter
> если ты сам семантически проверил его безопасность
Я гарантирую, что он семантически верен. Хочет этого кто или нет, но я четко знаю, какой тип будет реально у интерфеса. Твой код, заметь, тоже не исключает возможность поведения пользователем через ****. Например reinterpret_cast во внешнем коде. Ровно как и код Суслика.
winter
> Следовательно, считать твое решение хорошим или вообще сколь-нибудь приемлемым
> никто не будет.
Еще в обществе считается дурным тоном делать вызов функций, в которых на сам вызов будет затрачено больше времени, чем на выполнение. Дабл-диспатч в данном случае - это как раз такой вариант.)
winter
> занимаем совершенно непримиримые позиции по данному вопросу и твои попытки
> убедить меня в чем-то закончатся ничем
Уговорил. Но все же не тот пример ты привел) Есть примеры, где double-dispatch - отличное и, при грамотной реализации, очень удобное решение. Элджер стайл это плохой подход к дабл диспатчу. Элджеровский код, кстати, в чистом виде не компилируется. А в том, в котором компилируется - пример плохого дизайна системы. Слишком много не нужной информации напрямую доступно пользователю. Кстати наиболее общий и один из наиболее быстрых и расширяемых способов двойной диспетчеризации от Александреску базирован на рефлекшене)
winter
> В случае же жесткой привязки текстуры к контексту будет ой-ой-ой.
И еще раз. Текстура не привязывается к контексту. Она может на него устанавливаться. Но связанна она именно с девайсом. Если хочешь, то можно считать девайсом фабрику, порождающую контекст и текстуры. Заметь - контекст и девайс семантически разные вещи.
Давай заменим пример. Например на реализацию variant типа. Или на разработку некой примитивной системы коллизий. Потому что дабл-диспатч применим только к объектам, находящимся на одном семантическом уровне. Что не так для системы текстура-девайс-контекст.
@winter
Не, всегда приятно порассуждать на вопросы вселенского мироздания))
crsib
Элджер - рулез!
Но я согласен, что double dispatching в его случае открывает слишком много информации - собственно, что я и имел в виду, написав про "срам" в конце нулевого поста.
Если ты укажешь мне другой способ делать dd, причем без кастов, без дополнительного сохранения указателей внутри объектов и без рефлекшна - буду рад. ИМХО это невозможно, но буду рад ошибиться.
<правка>
>> Еще в обществе считается дурным тоном делать вызов функций, в которых на сам вызов будет затрачено больше времени, чем на выполнение.
Хмм, ну если так посчитать, затраты на вызов dd-метода - это затраты на вызов виртуального метода... Тело dd-метода - это, по определению, вызов другого виртуального метода... Т.е. на выполнение будет потрачено в среднем ровно столько же времени, что и на вызов.
Z
> Тоесть будем в дивайсе тайпкастить и трогать кишки текстуръ из дивайса?
> Ето ведь не особо "ООП", разве нет?
Хех, "ООП практически такая же мистификация как и искусственный интеллект".
winter
> без кастов, без дополнительного сохранения указателей внутри объектов и без
> рефлекшн
Ну, сохранение указателей это лишнее)) А так в, общем случае, действительно никак
Хотя рефлекшн сводится к добавлению одного виртуального метода)
Тут всегда надо балансировать на грани расширяемость-производительность. В любом случае. Либо касты, либо интрузивный подход. И в случае интрузивного подхода в большинстве случаев рефлекшн будет давать значительно больше плюшек, чем отнимать производительности. Возможно тебе это покажется странным, но рефлекшн часто применяется даже в performance critical коде. И не стоит забывать про людей, которые за использование виртуальных функций в этом самом perf critical коде готовы отрывать голову.
winter
> Т.е. на выполнение будет потрачено в среднем ровно столько же времени, что и на
> вызов.
Не совсем точно выразился. Правильное слово - соизмеримо.
crsib
>> Возможно тебе это покажется странным, но рефлекшн часто применяется даже в performance critical коде
За всю свою практику ни разу не встречал рефлекшна в performance critical коде, даже в жаве, где рефлекшн уже встроенный. Самое ходовое использование - для skeleton'ов и stub'ов удаленных объектов, т.е. не внутри, а снаружи бизнес-логики (на стыке объектов с middleware).
Можешь привести примеры использования рефлекшна в performance critical коде?
What is "интрузивный подход"?
есть еще такой метод, не знаю как называется (паралельный интерфейс?)
// interface.h: struct ITexture { virtual void func1() = 0; virtual void func2( ) = 0; ... }; struct IDevice { virtual ITexture* CreateTex( ) = 0; virtual void Bind( ITexture* ) = 0; ... }; IDevice* CreateDevice( ); // impl_dx.h: struct Texture { // не наследуемся virtual void func1( ) {...} virtual void func2( ) {...} ... members; }; struct Device { // не наследуемся virtual Texture* CreateTex( ) { return new Texture; } virtual void Bind( Texture* p ) { p->member;... } ... members; }; IDevice* CreateDevice( ) { return ( IDevice*)new Device; // единственный каст тут }
метод работает при условии, что порядок виртуальных функций полностью совпадает.
т.к. это интерфейс, а интерфейс (часто) меняться не должен, это условие соблюсти не трудно.
зато имплементация не замусорена кастами/диспатчами, нет дополнительных вызовов и указателей.
есть подозрение что в dx сделано именно так.
winter
> Можешь привести примеры использования рефлекшна в performance critical коде?
Рефлекшн например используется в коде симуляций в CERN. Во всяком случае они так говорят. Здесь про рефлекшн писал Сергей Макеев.
Я не совсем понимаю удивления. То, что нужно от рефлекшена в P-C коде - это безопасность приведения типов. И не более. Для ее обеспечения достаточно одного виртуального вызова и одного сравнения. Причем фактически zero-cost сравнения на современных процессорах. А лишний виртуальный вызов и так есть в дабл-диспатче. То есть мы в любом случае разруливаем проблему с типами за 2 виртуальных вызова. Но при этом имеет в подарок кучу плюшек, правда уже не для P-C кода
Adler
> интрузивный подход
Подход, требующий адаптации интерфейса под детали реализацию. То есть добавления методов/полей не имеющих отношения к прямой функциональности. Хотя это очень грубое определение.
uce
> (IDevice*)
Очень плохой каст. По факту reinterpret_cast. Вот так точно нельзя делать. К тому же кто его этот компилятор знает, что ему в голову взбредет. static_cast по крайней мере гарантирует, что vtable окажется в нужном месте (при условии что мы кастуем в правильный тип)
uce
> uce
это нечем не отличаться от наследование от ITexture и IDevice тока является намного опасней.
можно так замутить, типы будут проверяться на момент компиляции
template< int _ID_ > class Device;
template< int _ID_ > class Texture;
template< int _ID_ >
class Device
{
enum {ID = _ID_ };
Texture<_ID_>& create();
void bind(Texture<_ID_> &txtr);
};
template< int _ID_ >
class Texture
{
enum {ID = _ID_ };
};
template< int _ID_ > void bind(Device<_ID_> &dvc, Texture<_ID_> &txtr);
для программ можно замутить что то типа то кого.
template< typename STRUCT >
class CProgram: protected gl::program::CProgram , public STRUCT
{
public:
void init(gl::program::SUniform1i STRUCT::*offset);
void set(gl::program::SUniform1i STRUCT::*offset, uint32 i);
...
private:
};
--------------------------------------------------------
struct prgS {
gl::program::SUniform1i sText;
gl::program::SUniform4f vEye;
};
CProgram< prgS > prg;
.. f(...)
{
prg.init(&prgS::sText);
prg.set(&prgS::sText,34);
}если взять за провела для каждой программы свою struct prgS то проверка будет проходить на момент компиляции.
Нет динамического полиморфиза) Не катит. При его отсутствии нет проблем с сильной связанностью интерфейсов, потому что "производные" типы для компилятора будут отличаться. Но есть очень много проблем. Например интерфейс не отделим от реализации.
Впрочем, производительность будет на высоте)
winter
> То, что Торвальдс - абсолютный моральный урод,
вот это правильно (если он ругает С++, а он, насколько я понял из контекста ОПа, его ругает).
> совершенно неудобоваримые, слабосильные генерики, невозможность
> объявления константных методов/ссылок и отсутствие перегрузки операторов, на
> мой взгляд, перевешивают встроенную сборку мусора.
Я бы сказал, не "перевешивают", а "дополняют" (ибо сборка мусора, что бы там ни говорили, суть генетическое архитектурное уродство).
> Торвальдс спорит с этим утверждением, указывая на простой факт: внутренняя
> кухня операционной системы лучше всего описывается вовсе не объектами, а
> системами объектов и взаимодействиями между ними. Следовательно, ООП с его
> "малой сцепленностью" сразу идет как бы далеко и надолго, и мы вынуждены
> вернуться к старой процедурной парадигме: данные и функции для работы с ними.
> Несколько раз (сразу нужно сказать, что нечасто) я встречался с чем-то подобным
> - например, когда проектировал свой видео-движок.
Это как бы верно и не только для операционной системы. Но, если на основе этих соображений торвальдс критикует С++, он глуп. С++ хорош как раз тем, что, в отличие от жабы и сотоварищей, это не язык ООП. Что бы там ни думали по этому поводу страуструпы и саттеры. С++ - это в первую очередь язык удобного и логичного синтаксического сахера над "старой процедурной парадигме: данные и функции для работы с ними". Можно на нём писать ООП, можно алгебраически-ориентированный код, а можно что-нибудь ещё. А можно и всё это даже комбинировать в пределах одного куска кода. Чем С++ и силён, в отличие от.
>Очень плохой каст. По факту reinterpret_cast. Вот так точно нельзя делать.
ничего там страшного не случится, зато это единственный каст на всю систему
>К тому же кто его этот компилятор знает, что ему в голову взбредет.
я то знаю)
Тема в архиве.