Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregorhttps://www.cppstories.com/2018/05/tmplbook2nd-review/
Вот что немного затрудняет продвижение, так это многостраничные объяснения как сделать одно и то же в 11, 14 и 17 плюсах. Отсюда вопрос, на ваш взгляд, имеет ли смысл сейчас запариваться с предыдущими стандартами? Такое ощущение, что через год С++20 уже будет повсеместно, может имеет смысл сразу его осваивать? Там, насколько я понял, куча всяких нововведений типа концептов, которые еще сильнее могут упростить код.
Потом еще один момент. Стандартная библиотека предлагает кучу утилитных методов для метапрограммирования. Часть из них, что-то типа std::is_class можно реализовать, например, используя SFINAE
template<typename T>
class is_class
{
typedef char yes[1];
typedef char no[2];
template<typename C>
static yes& test(int C::*); // selected if C is a class typetemplate<typename C>
static no& test(...); // selected otherwisepublic:
static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};
Другие методы, по моим ощущениям (например std::result_of/std::invoke_result или noexcept operator), можно реализовать только на уровне компилятора. Отсюда вопрос, надо ли пытаться понять как это все внутри работает или подобные утилиты для метапрограммирования из стандартной библиотеки просто принять как данность?
UPD.
Забыл про философский вопрос Короче заметил, что плюсовое метапрограммирование представляют для меня пока просто какой-то хаотичный набор приёмов, методов, синтаксиса, а какая-то цельная картина происходящего не складывается. Я подумал, может это из-за недостаточной теоретической базы? Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?
Здравствуйте, Максим, Вы писали:
М>Потом еще один момент. Стандартная библиотека предлагает кучу утилитных методов для метапрограммирования. Часть из них, что-то типа std::is_class можно реализовать, например, используя SFINAE
М>
М>template<typename T>
М>class is_class
М>{
М> typedef char yes[1];
М> typedef char no[2];
М> template<typename C>
М> static yes& test(int C::*); // selected if C is a class type
М> template<typename C>
М> static no& test(...); // selected otherwise
М>public:
М> static bool const value = sizeof(test<T>(0)) == sizeof(yes);
М>};
М>
Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее:
И дело тут не только в количестве строк кода. Новые инструменты реально предоставляют больше возможностей. Так что, если ты ставишь себе целью прокачать свой уровень в метапрограммировании, то такие вот книжки — не очень хороший способ.
А после перехода на C++20 и эти подходы утратят свою актуальность и будут вытеснены концептами.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Философско-практические вопросы про метапрограммирование
R>Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее:
R>
Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?
R>И дело тут не только в количестве строк кода. Новые инструменты реально предоставляют больше возможностей. Так что, если ты ставишь себе целью прокачать свой уровень в метапрограммировании, то такие вот книжки — не очень хороший способ.
Так ведь других нет (с) Буду очень благодарен за рекомендации хороших книг.
R>А после перехода на C++20 и эти подходы утратят свою актуальность и будут вытеснены концептами.
Я так и думал
Errare humanum est
Re[3]: Философско-практические вопросы про метапрограммирование
Здравствуйте, Максим, Вы писали:
М>Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?
Большинство из этих утилит имеют простейшее определение в одну строчку. Главная их польза — это то, что они теперь есть в стандартной библиотеке и их не нужно писать самому каждый раз.
М>Так ведь других нет (с) Буду очень благодарен за рекомендации хороших книг.
Признаться честно, хороших книг в этом плане я даже и не знаю. Могу посоветовать почаще заходить на форум и не стесняться обращаться за подсказками. После приобретения некоторого старового опыта, когда ты прочувствуешь гланый принцип, ты приобретешь и большую самостоятельность в этих вопросах. Увидишь, что польшинство задач можно решить немного по-разному и научишься видедь плюсы и минусы разных решений.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Философско-практические вопросы про метапрограммирование
Здравствуйте, Максим, Вы писали:
М>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregorhttps://www.cppstories.com/2018/05/tmplbook2nd-review/
книга классная. Рекомендую.
М>Другие методы, по моим ощущениям (например std::result_of/std::invoke_result или noexcept operator), можно реализовать только на уровне компилятора.
https://en.cppreference.com/w/cpp/types/result_of
часть Possible implementation
М>Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?
кмк — пустое. Моё мнение, что любую абстракцию надо понимать на частных примерах. Вот шаблоны — частный пример функциональщины. Логично начать с них. Там хотя бы сразу можно увидеть практическое применение.
Re[2]: Философско-практические вопросы про метапрограммирова
Здравствуйте, sergii.p, Вы писали:
М>>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregorhttps://www.cppstories.com/2018/05/tmplbook2nd-review/ SP>книга классная. Рекомендую.
С удивлением обнаружил у себя pdf-ку этой книжки. Судя по дате, валяется у меня на диске с сентября 2017-го. Вот думаю, как могло случиться, что она совершенно не отложилась у меня в памяти. Вариант, что я ее не открывал и не просматривал маловероятен.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Максим, Вы писали:
М>Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?
Опыт работы с функциональными языками считаю значительно поможет, т.к.
1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )
2) Шаблонное метапрограммирование иммутабельное, как в чисто функциональных языках
Конечно знакомиться с этими концепциями можно начинать и на C++, но это будет куда как сложее и менее продуктивно, чем если сразу начинать с haskell'а. При этом глубоко изучать haskell ( ну или любой другой чисто функциональный язык ) не надо, достаточно вот этих двух концепций — иммутабельность и паттерн-матчинг. Ну и про унификацию в prolog можно после этого чутка почитать, чтобы понять чем он от pattern матчинга отличается.
V>1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )
Спасибо, только сейчас это осознал!
Errare humanum est
Re: Философско-практические вопросы про метапрограммирование
Здравствуйте, Максим, Вы писали:
М>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregorhttps://www.cppstories.com/2018/05/tmplbook2nd-review/ М>Вот что немного затрудняет продвижение, так это многостраничные объяснения как сделать одно и то же в 11, 14 и 17 плюсах. Отсюда вопрос, на ваш взгляд, имеет ли смысл сейчас запариваться с предыдущими стандартами? Такое ощущение, что через год С++20 уже будет повсеместно, может имеет смысл сразу его осваивать? Там, насколько я понял, куча всяких нововведений типа концептов, которые еще сильнее могут упростить код.
если бы я читал книгу с таким названием, я бы прежде всего посмотрел не на то как в базе (т.е. еще до 11-х плюсов) реализовать is_base_of is_same, или is_virtual и их вариативность, мне было бы интересно как вести себя в современном мире программирования, где подавляющее большинство проектов содержит значительную часть, а зачастую и подавляющую часть легаси. как вести сбя, если есть фрагменты куда и буста то не подложить. Вы эти моменты не вычитывали?
Здравствуйте, Максим, Вы писали:
М>Там все таки система типов гораздо серьезней проработана... Или пустое это?
Нет никакой системы типов. Раньше были программисты, которым вкатывало обмазывать всё макросами. И пофиг на то, что коллеги потом два дня вывод компилятора разбирают, если запятой ошиблись. Потом подвезли шаблоны в С++, и эта кавалерия дружно направилась осванивать их. Возможно, у меня не хватает опыта (хотя работал в фанге на С++ проекте), но я принципиально не понимаю, чем тот же enable_if облегчает задачу. И с ним, и без него мы получаем ошибку компиляции, просто текст разный, но одинаково большой и непонятный. Исключения составляют лишь какая-нибудь хитровытраханная система типов, в которой enable_if волшебно переключает реализации. Я не могу найти пример из головы, но уверен, такое есть. Или какая-нибудь универсальная функция сериализации, которая не должна существовать, если ей передали указатель на ссылку на r-value (добавьте сюда ещё больше сюра). Так что не парься. Если тебе С++ нравится как язык, то практикуйся, смотри видео с CppCon (хотя там редко рассказывают про боевой код, больше про hellow-worldы). А если просто мучает совесть, что не знаешь С++ достаточно хорошо, чтобы за 5 минут погрузить собеседника в сон, то пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++. А из С++-программистов я могу только чемпионов по докладам на конференциях назвать. В корпорациях они, обычно, не пишут боевой код, а входят в "илитную" группу, которая после выхода каждого нового стандарта решает, как переписать всё старое говно на новый лад, или просто тупо форсят идеи корпорации при обсуждении нового стандарта в комитете.
P.S. Мне когда нечего делать, я смотрю доклады по C++ как альтернатива чтению или просмотру научной фантастики.
Re[3]: Философско-практические вопросы про метапрограммирова
Здравствуйте, rg45, Вы писали:
R>С удивлением обнаружил у себя pdf-ку этой книжки. Судя по дате, валяется у меня на диске с сентября 2017-го. Вот думаю, как могло случиться, что она совершенно не отложилась у меня в памяти. Вариант, что я ее не открывал и не просматривал маловероятен.
А её тут кто-то на форуме выкладывал. Я тоже скачал примерно в то время.
Re[2]: Философско-практические вопросы про метапрограммирование
C> Исключения составляют лишь какая-нибудь хитровытраханная система типов, в которой enable_if волшебно переключает реализации. Я не могу найти пример из головы, но уверен, такое есть.
Посто как пример. Может быть такая задача — вы пишите шаблон класса которому в качестве типа будет передан какой-то контейнер, но вы не знаете какой. Внутри вашего класса вам, по каким-то причинам, надо отсортировать этот контейнер. Причем, если контейнер внутри себя имеет метод sort, то надо дернуть его, а если нет, то вызвать std::sort(cont.begin(), cont.end()). Если нет ни sort, ни begin/end, то должна быть ошибка компиляции. Попробуйте реализуйте такое без SFINAE.
Errare humanum est
Re[3]: Философско-практические вопросы про метапрограммирование
Здравствуйте, Максим, Вы писали:
М>Посто как пример. Может быть такая задача — вы пишите шаблон класса которому в качестве типа будет передан какой-то контейнер, но вы не знаете какой. Внутри вашего класса вам, по каким-то причинам, надо отсортировать этот контейнер. Причем, если контейнер внутри себя имеет метод sort, то надо дернуть его, а если нет, то вызвать std::sort(cont.begin(), cont.end()). Если нет ни sort, ни begin/end, то должна быть ошибка компиляции. Попробуйте реализуйте такое без SFINAE.
А если контейнер уже отсортирован (map, set...), то ничего не делать. В этом случае процедура сортировки существует лишь номинально.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Философско-практические вопросы про метапрограммирование
#include <iostream>
#include <set>
int main()
{
std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
for (const int value : set)
std::cout << " " << value; // -> 0 1 2 3 4 5 6 7 8 9
}
А, по каким критериям? Ну, проще всего это сделать через traits-ы. Ты свою обобщенную процедуру сразу делаешь заточенной на использование трейтсов. А в трейтсах уже можно навернуть аналитику любой сложности — от автоматического определения до ручной специализации. Использование трейтсов делает обощенные процедуры легко расширяемыми. Этот подход можно применить не ко всей обобщенной процедуре, а только к внутренней процедуре сортировки. В общем, все в зависимости от задачи.
--
Не можешь достичь желаемого — пожелай достигнутого.
R>А, по каким критериям? Ну, проще всего это сделать через traits-ы. Ты свою обобщенную процедуру сразу делаешь заточенной на использование трейтсов. А в трейтсах уже можно навернуть аналитику любой сложности — от автоматического определения до ручной специализации. Использование трейтсов делает обощенные процедуры легко расширяемыми. Этот подход можно применить не ко всей обобщенной процедуре, а только к внутренней процедуре сортировке. В общем, все в зависимости от задачи.
А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!
UPD
Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
Здравствуйте, Максим, Вы писали:
М>А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!
Да, чуть позже только, сейчас немного дефицит со временем.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: Философско-практические вопросы про метапрограммирова
Здравствуйте, Максим, Вы писали:
М>А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!
Ну, примерно вот так. Нужно только понимать, что это эскиз, а не продакшн код, поэтому и воспринимать нужно с некоторой поправкой.
Хочется сразу сделать небольшое замечание по поводу traits-ов. В традиционном понимании traits-ы — это шаблонный клас со специализациями, содержащий в себе фиксированный набор свойств типов, которые используются в определенной обобщенной процедуре (или группе процедур). В этом примере использован немного другой подход. Traits-ы здесь представлены набором объявлений функций — именно объявлений, без определений. Эти объявления логически играют ту же самую роль — связывают между собой различные сущности времени компиляции — типы и константы. Но, по сравнению с классическим подходом, подход с фунциями имеет некоторые существенные преимущества: они допускают различную группировку по типам для разных свойств, а еще функции подчиняются правилам overload resolution и поиска по ADL. Трейтсы-функции объявляются в тех же пространствах имен, что и сами типы, прямо рядышком. Это здорово облегчает определение трейтсов и способствует комфортной расширяемости обобщенных процедур для пользовательских типов. Но есть и минус: трейтсы для стандартных типов и типов из сторонних библиотек должны быть видимы в точке использования (в метафункциях) и их приходится определять заранее. Но как по мне, это не большая плата за те преимущества, которые дает этот подход, а где-то это даже и логично.
R>Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее
В книге примерно это пишут, ОП, видимо, не разобрался
Re[8]: Философско-практические вопросы про метапрограммирова
Здравствуйте, Максим, Вы писали:
М>Класс!!! Спасибо большое, особенно за то, что показали как можно применять в реальной жизни "User-defined deduction guides". Не видел такого раньше!
На здоровье! Обращайся, если что.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[9]: Философско-практические вопросы про метапрограммирова
Здравствуйте, Максим, Вы писали:
М>Класс!!! Спасибо большое, особенно за то, что показали как можно применять в реальной жизни "User-defined deduction guides". Не видел такого раньше!
Еще хочу сказать об одной "мелочи" вдогонку, про которую часто забывают. Когда пишешь какую-то обобщенную функцию, для всех возможных вариантов ее специализаций/перегрузок, нужно задаваться вопросом, в каком месте будет возникать ошибка, в случае неправильного использования. Например, при попытке вызвать метод sort для константного объекта (массива или стандартного контейнера), очень важно, чтобы ошибка компиляции возникала в клиентском коде, именно в том месте, где осуществляется попытка сделать неправильный вызов, а не где-то в потрохах библиотечного кода. А для этого нужно хорошо продумывать реализацию всех метафункций и состав фильтров SFINAE.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, cppguard, Вы писали:
C> пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++.
А вы пробовали читать их код?
И каждый день — без права на ошибку...
Re[7]: Философско-практические вопросы про метапрограммирова
Здравствуйте, Максим, Вы писали:
М>А можете, если есть время, более подробно написать
почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.
М>UPD М>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
Здравствуйте, ботаныч, Вы писали:
М>>А можете, если есть время, более подробно написать Б> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.
М>>UPD М>>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set> Б>
И как потом с этим подходом можно будет расширять правила для пользовательских типов, находящихся в клиенстких простраствах имен? Например, как можно(нужно) переписать вот этот фрагмент:
Теперь по поводу "покрывающим большее количество компиляторов": можешь показать ХОТЯ БЫ ОДИН компилятор, на котором твой пример компилируется, а мой нет?
А просадка времени компиляции откуда возьмется (по сравнению с твоим вариантом)? Объявления функций все просадят что ли?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, ботаныч, Вы писали:
Б>Здравствуйте, Максим, Вы писали:
М>>А можете, если есть время, более подробно написать Б> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.
Б>
Ну и кроме того, давай, ты сначала доведешь своей пример до компилируемого состояния, покажешь пример использования в контесте задачи, а потом я расскажу, почему это плохо. Договорились?
А то ведь знаешь как бывает, идея кажется правильной, пока не компилируется. А как начинаешь доводить до компиляции оказывается, что сама идея никуда не годится и нужно придумывать что-то другое.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, ботаныч, Вы писали:
М>>>А можете, если есть время, более подробно написать Б>> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.
М>>>UPD М>>>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set> Б>>
R>И как потом с этим подходом можно будет расширять правила для пользовательских типов, находящихся в клиенстких простраствах имен? Например, как можно(нужно) переписать вот этот фрагмент:
что ты что ты )) это прсто is_same — там в is_same set вставлен, я показал как можно сверить шаблон, ..вроде компилируется. не заметил ниже is_instance
это не сфинай для проверки наличия sort. sort сфиная проверка метода из олдовых SFINAE выглядит чуточку массивнее
R>А просадка времени компиляции откуда возьмется (по сравнению с твоим вариантом)? Объявления функций все просадят что ли?
а я не замерял, можно проверить кстати в сравнеии с олдовым SFINAE для проверки сорта с более свезжим способами. Я замечал, что тупая итерация типов в тупле гораздо дольше компилируется, чем если проделать это вручную через template <typename ...Ts> struct types;
вообще, по правде говоря встретив твой способ, воспользовался бы им, Но когда мне такое надобилось, не было таких способов, писалось классическим способом
Re[9]: Философско-практические вопросы про метапрограммирова
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, ботаныч, Вы писали:
Б>>Здравствуйте, Максим, Вы писали:
М>>>А можете, если есть время, более подробно написать Б>> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.
Б>>
R>Ну и кроме того, давай, ты сначала доведешь своей пример до компилируемого состояния, покажешь пример использования в контесте задачи, а потом я расскажу, почему это плохо. Договорились?
R>А то ведь знаешь как бывает, идея кажется правильной, пока не компилируется. А как начинаешь доводить до компиляции оказывается, что сама идея никуда не годится и нужно придумывать что-то другое. https://onlinegdb.com/4hmabsGci
да вот же, я же говорю, это просто проверка на инстанс сета ))
Re[10]: Философско-практические вопросы про метапрограммиров
Здравствуйте, ботаныч, Вы писали:
Б>https://onlinegdb.com/4hmabsGci Б>да вот же, я же говорю, это просто проверка на инстанс сета ))
А для чего нам это проверка нужна вообще? Эта утилита родилась же в контексте нашей синтетической задачи, правда? Вот и попробуй применить свой подход при решении задачи: http://coliru.stacked-crooked.com/a/fdbaf2f292d21344. Только не умозрительно, а чтоб работало. Потом сравним. Обрати внимание, пример немного изменен — добавлены пространства имен, как это обычно бывает в реальной жизни.
P.S. Единственный плюс такого подхода в том, что он позволит перечислить все стандартные контейнеры в одной специализации. Но плюс достаточно сомнительный, как по мне, если учесть, что этой для того, чтобы эта монолитная специализация стала возможна, сперва нужно определить несколько дополнительных сущщностей, не слишком удобочитаемых. А в случаях, когда предполагается расширение специализаций для пользовательских типов, разбросанных по разным пространствам имен, использование этого подхода крайне неудобно и порождает в местах специализаций нагромождения кода, которые ужасно будут раздражать при первом же рефакторинге. Нагромождения связаны в первую очередь необходимостью разрывать пользовательские пространства имен для выполнения специализаций библиотечного класса. И, чем больше таких мест, тем более ужасная картина будет получаться. Вынос этих специализаций в какое-то отдельное место также имее свои ощутимые недостатки.
Вот видоизмененный пример, на котором можно прочувствовать проблему, о которой я говорю. Центральное место здесь занимает пользовательский класс MySortedContainer, который должен обрабатыватся по тем же правилам, что и отсортированные стандартные контейнеры (map, set и пр.).
Здравствуйте, rg45, Вы писали:
R>Зачем нам эта проверка?
это был ответ на
оттолкнуться от std::is_same_v<T, std::set>
я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода.
я не говорил, что тут проверка на наличие метода sort. даже, что пример к этому относится относится
Re[12]: Философско-практические вопросы про метапрограммиров
Здравствуйте, ботаныч, Вы писали:
R>>Зачем нам эта проверка? Б>это был ответ на Б>
Б>оттолкнуться от std::is_same_v<T, std::set>
Б>я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода. Б>я не говорил, что тут проверка на наличие метода sort. даже, что пример к этому относится относится
для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов.
Как я понял, ты предлагаешь другой подход, который лучше. И мне трудно понять, на чем основываются твои выводы о скорости компиляции и о количестве доступных компиляторов. По-моему, твое решение проблемнее по обоим перечисленным аспектам. Мой подход основывается на простых объявлениях перегруженных функций, что может быть проще?. Или я неправильно тебя понял?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, ботаныч, Вы писали: Б>я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода.
Нифига себе мало кода. Его мало, потому что ты не довел его до логического завершения. А ты доведи его хотя бы до определения метафункции и сравни:
Мой вариант
// For standard containers, the traits must be defined beforehandtemplate <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::map<K, T, C, A>&) -> std::true_type;
template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::multimap<K, T, C, A>&) -> std::true_type;
template <typename T, typename C, typename A>
auto assume_sorted_range(const std::set<T, C, A>&) -> std::true_type;
template <typename T, typename C, typename A>
auto assume_sorted_range(const std::multiset<T, C, A>&) -> std::true_type;
template <typename T>
using is_sorted_container = decltype(assume_sorted_range(std::declval<T>()));
Про расширяемость типов я уже молчу. В моем подходе все делается либо легким движением руки, либо вообще автоматически. Например, все свойства, заданные для базовых классов автоматически распространяются на производные, но можно и переопределить без труда, если нужно. А расширение типов при твоем подходе — это хождение по мукам.
А еще поставь себя на место нового сотрудника, который недавно пришел и разбирается с исходниками компании. Какой вариант легче для понимания?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, ботаныч, Вы писали:
R>>>Зачем нам эта проверка? Б>>это был ответ на Б>>
Б>>оттолкнуться от std::is_same_v<T, std::set>
Б>>я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода. Б>>я не говорил, что тут проверка на наличие метода sort. даже, что пример к этому относится относится
R>Ты говорил о предложенном мной подходе:
R>http://rsdn.org/forum/cpp/8467966.1
R>для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов.
R>Как я понял, ты предлагаешь другой подход, который лучше.
нет. я сказал, что есть практики основанные на олдовых SFINAE для определения наличия методов. просто из своего опыта. из этой-же причине я предположил, что эти две разные имплементации могут компилироваться с разной скоростью.
R>И мне трудно понять, на чем основываются твои выводы о скорости компиляции и о количестве доступных компиляторов. По-моему, твое решение проблемнее по обоим перечисленным аспектам. Мой подход основывается на простых объявлениях перегруженных функций, что может быть проще?. Или я неправильно тебя понял?
свои предположения я строю опять-же на практике. я упоминал прецедент с туплом и тривиальным вариэдиком.
я не считаю один солюшин лучше, другой хуже. просто описал свои соображения. единственно что )) так это я не уверен, что смогу заставить себя тащить "свой" (он не мой) вариант.
Re[3]: Философско-практические вопросы про метапрограммирование
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, cppguard, Вы писали:
C>> пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++.
BFE>А вы пробовали читать их код?
Код Торвальдся вполне читаем. У Беллара — write-only (я читал код tcc).
Re[9]: Философско-практические вопросы про метапрограммирова
Так делать не стоит.
Дело в том, что операции && и || в этом контексте вынудят компилятор инстанциировать все варианты is_instance, а потом только произвести операцию «И», «ИЛИ».
Здесь нужно использовать std::conjuction, std::disjunction.
Или концепты где && и || будут означать std::conjuction, std::disjunction.
Здравствуйте, Voivoid, Вы писали:
V>Здравствуйте, Максим, Вы писали:
М>>Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?
V>Опыт работы с функциональными языками считаю значительно поможет, т.к.
V>1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )
салют коллега. у меня любимый вопрос на собесах — что общего между SFINAE и жадностью.
V>2) Шаблонное метапрограммирование иммутабельное, как в чисто функциональных языках
вы имеете в виду что нельзя сделать i=i+1 ? like of prolog? надо понимать, что С++ образовался, и надо сказать образовывается как .. как jazz. у меня коллега джавист высказал фразу, что шаблонное программирование (имеется в виду рекурсивная инстанциация шаблона) была открыта как остров. ) да замечательно.
V>Конечно знакомиться с этими концепциями можно начинать и на C++, но это будет куда как сложее и менее продуктивно,
вы много назовете проектов, где будет такая возможность? если мы говорим о профессии. эо должно очень повезти, чтобы тебе на проекте позволили найти выделенный scope, чтобы внедрять что-то написанное на haskell, rust etc..
V>чем если сразу начинать с haskell'а. При этом глубоко изучать haskell ( ну или любой другой чисто функциональный язык ) не надо, достаточно вот этих двух концепций — иммутабельность и паттерн-матчинг. Ну и про унификацию в prolog можно после этого чутка почитать, чтобы понять чем он от pattern матчинга отличается.
я предлагаю таки учить это на практике, именно на практических задачах, чтобы ты видел как оно взлетает
Re[3]: Философско-практические вопросы про метапрограммирование
Здравствуйте, ботаныч, Вы писали:
Б> вы имеете в виду что нельзя сделать i=i+1
Да, но в первую очередь конечно иммутабельные структуры данных и алгоритмы. Те же typelist'ы, краеугольный камень шаблонного метапрограммирования, это стандартная структура данных в haskel'е. Без базового знакомства с функциональным программированием я практически уверен, что будет совершенно непонятно почему все именно так и что вообще со всем этим можно делать.
Думаю никто не будет спорить, что код на haskell
data List a = Nil | Cons a (List a)
map _ Nil = Nil
map f Cons(head, tail) = Cons(f(head), map(f, tail))
понятнее и легче воспринимается чем аналогичный код на C++
Тем более что по haskell куда больше книг и туториалов( по сравнению с материалами по шаблонному метапрограммированию ). А стоит ли говорить про сообщения об ошибках? По c++ ошибкам в таких делах будет практически невозможно понять что вообще нет так, что особенно фатально, когда только начинаешь со всем этим знакомиться.
Б> вы много назовете проектов, где будет такая возможность? если мы говорим о профессии. эо должно очень повезти, чтобы тебе на проекте позволили найти выделенный scope, чтобы внедрять что-то написанное на haskell, rust etc..
Так а зачем это в рамках рабочих проектов делать? Мне видится, что достаточно просто часов 20-30 потратить на базовое знакомство с haskell'ем, необходимости что-то в production'е на нем делать нет никакой.
Б> я предлагаю таки учить это на практике, именно на практических задачах, чтобы ты видел как оно взлетает
Это ж не rocket science, там ж не прям что-то такое, чего нельзя было бы осилить за 1-2 недели неспешных экспериментов с учебными примерами.
Re[3]: Философско-практические вопросы про метапрограммирование
Здравствуйте, Максим, Вы писали:
М>Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?
Есть такой ресурс — http://en.cppreference.com/w/
там часто приводятся типичные/упрощённые/эквивалентные определения всяких вещей "просто используя конструкции языка".
Здравствуйте, Кодт, Вы писали:
К>Есть такой ресурс — http://en.cppreference.com/w/ К>там часто приводятся типичные/упрощённые/эквивалентные определения всяких вещей "просто используя конструкции языка".
С возвращением! Ты насовсем вернулся?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Философско-практические вопросы про метапрограммирование
вот прежде всего, я собирался писать совсем другой пост, о он вышел как вышел .. а сейчас, я попытаюсь высказать квентиссенцию ..
простота разрабатываемых в хацкеле тайплистов лучше вероятно, чем изобретенных в С++
V>Здравствуйте, ботаныч, Вы писали:
Б>> вы имеете в виду что нельзя сделать i=i+1 V>Да, но в первую очередь конечно иммутабельные структуры данных и алгоритмы. Те же typelist'ы, краеугольный камень шаблонного метапрограммирования, это стандартная структура данных в haskel'е. Без базового знакомства с функциональным программированием я практически уверен, что будет совершенно непонятно почему все именно так и что вообще со всем этим можно делать.
V>Думаю никто не будет спорить, что код на haskell V>
V>data List a = Nil | Cons a (List a)
V>map _ Nil = Nil
V>map f Cons(head, tail) = Cons(f(head), map(f, tail))
V>
V>понятнее и легче воспринимается чем аналогичный код на C++
это выглядит как мотив. мотив для вас ) вам не нравится С++ в этом, в котором собственно метапрограммирование было не спроектировано, а открыто "как остров" (хотя сфинай). я относительно недавно доказал себе, что эппроач реализуем, я реализовывал в с++ ограниченный синтаксис регекпов (с компиляцией компайл тайм строк), с довольно шустрой рантайм мoделью. ну я понимаю, до этого я подобным занимался в других масштабах, но я просто доказал себе, что это реализуемо, инородный синтаксис в С++, но вы можете реализовать тоже самое, в С++ довольно недолго, если вопрос касается тайп листов — мгновенно. вопрос куда это все лепить? о тайп листах кстати
они сейчас такие
template <typename ... > struct type_list;
главное для чего все это? где это в практике применить?
я знаю, где такое применимо .. — в коде который писался десятилетия разными программерами. с разными, почти всегда олдовыми концептами. где размер кода зашкаливает. и над принять решение, когда паутина архитектуры строится с таким дикими изначально приближениями о том, что этот концепт таки удовлтворителен для использемых его кейсов.
V>Тем более что по haskell куда больше книг и туториалов( по сравнению с материалами по шаблонному метапрограммированию ).
да, потому, что ) всегда так бывает ))
V> А стоит ли говорить про сообщения об ошибках? По c++ ошибкам в таких делах будет практически невозможно понять что вообще нет так, что особенно фатально, когда только начинаешь со всем этим знакомиться. невозможно вопрос — спорный (и где? gcc msvc?)
Б>> вы много назовете проектов, где будет такая возможность? если мы говорим о профессии. эо должно очень повезти, чтобы тебе на проекте позволили найти выделенный scope, чтобы внедрять что-то написанное на haskell, rust etc.. V>Так а зачем это в рамках рабочих проектов делать? Мне видится, что достаточно просто часов 20-30 потратить на базовое знакомство с haskell'ем, необходимости что-то в production'е на нем делать нет никакой.
))) 20 30 ). кошмар. ну у меня на знакомство с перл (исключительно в рамках базовой задачи) ушло 2- часа. я почему-то так и прочитал ваше 20-30. откуда у меня 20-30. для чего?
Б>> я предлагаю таки учить это на практике, именно на практических задачах, чтобы ты видел как оно взлетает V>Это ж не rocket science, там ж не прям что-то такое, чего нельзя было бы осилить за 1-2 недели неспешных экспериментов с учебными примерами.
в одном из своих проектов я как-то ляпнул с дуру, что в принципе я бы понял
Здравствуйте, ботаныч, Вы писали:
Б> ... вопрос куда это все лепить? о тайп листах кстати Б>они сейчас такие Б>
Б>template <typename ... > struct type_list;
Б>
Б>главное для чего все это? где это в практике применить? Б> я знаю, где такое применимо .. — в коде который писался десятилетия разными программерами. с разными, почти всегда олдовыми концептами. где размер кода зашкаливает. и над принять решение, когда паутина архитектуры строится с таким дикими изначально приближениями о том, что этот концепт таки удовлтворителен для использемых его кейсов.
Например, я использую такое, кроме прочего, для "автоматизации работы с АПИ"
Пример его использования, для описания API некоего "объекта Future", состоящего из неких "свойств" и "методов": https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/cmtFuture.hpp#L94
далее этот лист (вместе с листами остальных "объектов") просовывается в qml и там можно объект Future использовать, работать с его свойствами и методами. Если бы эти АПИ проводить не через лист — пришлось бы изыскивать какие то другие средства для представления "набора сущностей", как например в Qt делается за счет ее мета-подсистемы. В общем, с тайплистами — получается вполне удобно. И весьма по сиплюсплюсному — максимум работы проделывается в компайл-тайм, а на рантайм остается только необходимый минимум.
Re[6]: Философско-практические вопросы про метапрограммирова
Здравствуйте, ботаныч, Вы писали:
Б> я понимаю, зачем нужньі тайп листьі в С++. я задавал применимо к хацкелю.
хацкель затем чтобы познакомиться с иммутабельными рекурсивными структурами данных и алгоритмами, паттерн матчингом и алгебраическими типами данных не отвлекаясь при этом на чисто c++'ные проблемы неизбежно возникающие при попытке все вышеперечисленное использовать