Кстате, следует упомянуть что forward_as_tuple и invoke у вас — это дополнительные функциональные объекты, обертки над стандартными функциями. Стандартную функцию в apply таким образом не запихать.
template <typename ... As>
constexpr auto part (As && ... as)
Почему нельзя использовать "стандартную" сигнатуру?
template<typename F, typename ... As >
constexpr auto part (F && f, As &&... as )
Это позволит проконтролировать, что первым аргументом должен идти объект, над которым будет выполненно преобразование. А если сохранить объект отдельно от кортежа, то можно вызывать просто apply. Стандартные apply и invoke оба обрабатывают callable-объекты, и скорее всего apply будет реализован через invoke.
Мне кажется это очень странное, вводящие в заблужение название.
Здесь ближе значение единый, одинаковый.
Квалификатор uniform подчеркивает, что значение переменной будет одинаковым в процессе обработки всего примитива. В отличии от переменной с квалификатором attribute, значение которой засисит от обрабатываемой вершины или переменной с квалификатором varying, значение которой может меняться (интерполироваться).
Также, на мой взгляд, никаким улучшением времени компеляции, путь даже она будет происходит мгновенно при любом изменение, нельзя оправдать увеличение потребления памяти в рантайме с учетом того, что производительность останется таже. Вы же этот продукт в конечном итоге отдатите пользователям. Как минимум, все подобные манипуляции должы производиться только под дебагом. И здесь pimpl опять лучше, потому что не меняет интерфейс класса. Если я без скрытой реализации писал a.result_by_module(4), то и со скрытой реализацией я буду писать а.result_by_module(4).
В вашей статье речь идет не об изменении заголовочного файла, а об изменение закрытых полей некоторого класса. Если в вашем проекте есть класс, в котором часто меняются приватные поля и это класс цепляется по всему большому проету (но он должен цепляться в явном виде, чтобы приводить к перекомпеляции) — то что-то в архитектуре этого проекта в принципе не так.
И потом, из стать несовсем понятно, чем ваш пример лучше pimpl? Вы привели единственный аргумент
необходимость писать обертку для всех методов класса примерно таким образом (опустим дополнительные сложности по управлению памятью):
Как заметели в коментариях ниже, такой необходимости нет.
Например, в качестве скрытой структуры может выступать A_context из вашего примера, тогда можно реализовать методы естественным путем, без дополнительного уровня косвенности.
По поводу памяти, можно также использовать unique_ptr для хранения указателя на реализацию. Плюс ко всему здесь есть улучшение под название The Fast Pimpl Idiom.
Пример
// A.h
class A
{
public:
A();
~A();
void next_step();
int result_by_module(int);
private:
struct impl;
std::unique_ptr<impl> _impl;
};
// A.cpp
struct A::impl
{
int counter_ = 0;
};
A::A()
: _impl(std::make_unique<impl>())
{}
A::~A() = default;
void A::next_step()
{
++_impl->_counter;
}
int A::result_by_module(int m)
{
return _impl->_counter % m;
}
Объект класса А занимает 4 байта (поле типа int), независимо от сложности предоставляемого интерфейса.
В вашей реализации объект класса А занимает:
16 байт * 2 (количество_методов в интерфейсе) +
4 байта для указателя на объект (unique_ptr) +
4 байта A_context +
8 байт для указателя на контекст (shader_ptr) +
8 байт объект bind +
8 байт для указатель на контект в bind'е +
8 байт для указатель на контект в лямбде.
Но как пишет Александреску — это самый неудачный паттерн, потому редко используемый
Это немного упрошенный пример, у Александреску более аккуратный, обобщенный шаблон (в Loki, dynamic_cast вынемен отдельно), но суть там такая же. Не такой уж он и неудачный.
Намой взгляд, возможности по расшерению и поддержке этой модели, такие же как и у Cyclic Visitor. Только вместо виртуальной функции мне нужно добавить поле в enum, вы же понимаете, что если я добавлю поле в enum все существующие visitor'ы нужно будет перекомпелировать. Что если это библиотека и у меня нет возможности добавить поле в enum, также как у меня может не быть возможности добавить виктуальную функцию в случаи с Cyclic Visitor и перекомпилировать все?
У obj нет метода visit. Если бы был, тогда нужна перегрузка для всех типов — это циклическая зависимость.
Про этот пример можно подробно прочитать в книге Andrei Alexandrescu. Modern c++ design.
Про Новосибирск поддержу. Жил некоторое время в Академгородке — это потрясающее место, как-будто попадаешь в другой мир. По работе там большую часть одеяла на себя стягивает 2gis, мне кажется.
Давайте предположим что можно. И пусть, у нас пока есть только диффузная компонента.
Тогда в результате, если записывать сразу в буфер цвета, будет следующее:
Эту проблему можно решить если записывать результат в отдельную текстуру. Тогда шейдер источника света будет брать цвет из буфера, умножать его на свой цвет и добавлять в результирующий буфер, получается то что нужно.
Проблема в том, что у нас есть еще и зеркальная компонента, для которой нам нужно делать тоже самое, а потом сложить с диффузной компонентой. Тогда нам потребуется еще раз отрисовать все объекты и сохранить в текстуру степень зеркальности. И… как результат мы приходим к общему случаю отложеного освещения, в котором эти тектуры — нормали, альбедо и степень зеркальности создается за один проход.
Обратите внимание на формулу.
Как вы предлагаете это считать, если будите рендерить шары в буфер цвета? Ну точнее, каким образом нужно рендерить эти шары?
С лямбдами будет скорее всего все как у вас, просто кортеж переедет в capture.
Типа токого
или такого
У вас вроде понагляднее.
Кстате, следует упомянуть что
forward_as_tuple
иinvoke
у вас — это дополнительные функциональные объекты, обертки над стандартными функциями. Стандартную функцию в apply таким образом не запихать.Почему нельзя использовать "стандартную" сигнатуру?
Это позволит проконтролировать, что первым аргументом должен идти объект, над которым будет выполненно преобразование. А если сохранить объект отдельно от кортежа, то можно вызывать просто apply. Стандартные apply и invoke оба обрабатывают callable-объекты, и скорее всего apply будет реализован через invoke.
Мне кажется это очень странное, вводящие в заблужение название.
Здесь ближе значение единый, одинаковый.
Квалификатор uniform подчеркивает, что значение переменной будет одинаковым в процессе обработки всего примитива. В отличии от переменной с квалификатором attribute, значение которой засисит от обрабатываемой вершины или переменной с квалификатором varying, значение которой может меняться (интерполироваться).
Также, на мой взгляд, никаким улучшением времени компеляции, путь даже она будет происходит мгновенно при любом изменение, нельзя оправдать увеличение потребления памяти в рантайме с учетом того, что производительность останется таже. Вы же этот продукт в конечном итоге отдатите пользователям. Как минимум, все подобные манипуляции должы производиться только под дебагом. И здесь pimpl опять лучше, потому что не меняет интерфейс класса. Если я без скрытой реализации писал a.result_by_module(4), то и со скрытой реализацией я буду писать а.result_by_module(4).
В вашей статье речь идет не об изменении заголовочного файла, а об изменение закрытых полей некоторого класса. Если в вашем проекте есть класс, в котором часто меняются приватные поля и это класс цепляется по всему большому проету (но он должен цепляться в явном виде, чтобы приводить к перекомпеляции) — то что-то в архитектуре этого проекта в принципе не так.
И потом, из стать несовсем понятно, чем ваш пример лучше pimpl? Вы привели единственный аргумент
Как заметели в коментариях ниже, такой необходимости нет.
Например, в качестве скрытой структуры может выступать A_context из вашего примера, тогда можно реализовать методы естественным путем, без дополнительного уровня косвенности.
По поводу памяти, можно также использовать unique_ptr для хранения указателя на реализацию. Плюс ко всему здесь есть улучшение под название The Fast Pimpl Idiom.
Еще ваша реализация полность убирает понятие константности из интерфейса.
Объект класса А занимает 4 байта (поле типа int), независимо от сложности предоставляемого интерфейса.
В вашей реализации объект класса А занимает:
16 байт * 2 (количество_методов в интерфейсе) +
4 байта для указателя на объект (unique_ptr) +
4 байта A_context +
8 байт для указателя на контекст (shader_ptr) +
8 байт объект bind +
8 байт для указатель на контект в bind'е +
8 байт для указатель на контект в лямбде.
А если в интерфейсе 10 методов?
А если у меня иерархия не линейная?
Это немного упрошенный пример, у Александреску более аккуратный, обобщенный шаблон (в Loki, dynamic_cast вынемен отдельно), но суть там такая же. Не такой уж он и неудачный.
Намой взгляд, возможности по расшерению и поддержке этой модели, такие же как и у Cyclic Visitor. Только вместо виртуальной функции мне нужно добавить поле в enum, вы же понимаете, что если я добавлю поле в enum все существующие visitor'ы нужно будет перекомпелировать. Что если это библиотека и у меня нет возможности добавить поле в enum, также как у меня может не быть возможности добавить виктуальную функцию в случаи с Cyclic Visitor и перекомпилировать все?
У obj нет метода visit. Если бы был, тогда нужна перегрузка для всех типов — это циклическая зависимость.
Про этот пример можно подробно прочитать в книге Andrei Alexandrescu. Modern c++ design.
Ознакомьтесь со значением слова конкурс.
Про Новосибирск поддержу. Жил некоторое время в Академгородке — это потрясающее место, как-будто попадаешь в другой мир. По работе там большую часть одеяла на себя стягивает 2gis, мне кажется.
Ну, вроде Ростов обсудили, uTeam вроде тоже — все ищем Риту!
Зато посмотрите как Рита кодит.
Из всех перечисленных аргументов, самым серьезным оказалась Рита.
Еще был вопрос в комментариях (я его отклонил, случайно), "почему такие засветы на потолке?"
Это потому что нет тененй пока.
Нет, посмотрите внимательно, там указано вот так
где Ka, Kd, Ks — это свойства метериала отражать соответствующий свет — т.е. то что у вас в цветовом буфере.
Давайте предположим что можно. И пусть, у нас пока есть только диффузная компонента.
Тогда в результате, если записывать сразу в буфер цвета, будет следующее:
А должно быть так:
Эту проблему можно решить если записывать результат в отдельную текстуру. Тогда шейдер источника света будет брать цвет из буфера, умножать его на свой цвет и добавлять в результирующий буфер, получается то что нужно.
Проблема в том, что у нас есть еще и зеркальная компонента, для которой нам нужно делать тоже самое, а потом сложить с диффузной компонентой. Тогда нам потребуется еще раз отрисовать все объекты и сохранить в текстуру степень зеркальности. И… как результат мы приходим к общему случаю отложеного освещения, в котором эти тектуры — нормали, альбедо и степень зеркальности создается за один проход.
Phong reflection model
Обратите внимание на формулу.
Как вы предлагаете это считать, если будите рендерить шары в буфер цвета? Ну точнее, каким образом нужно рендерить эти шары?
Ну и да, там нет окон — это дырки(отверстия) просто.