Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 08.02.23 10:17
Оценка: +3
Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://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 type
 
    template<typename C>
    static no& test(...);       // selected otherwise
public:
    static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};


Другие методы, по моим ощущениям (например std::result_of/std::invoke_result или noexcept operator), можно реализовать только на уровне компилятора. Отсюда вопрос, надо ли пытаться понять как это все внутри работает или подобные утилиты для метапрограммирования из стандартной библиотеки просто принять как данность?

UPD.
Забыл про философский вопрос Короче заметил, что плюсовое метапрограммирование представляют для меня пока просто какой-то хаотичный набор приёмов, методов, синтаксиса, а какая-то цельная картина происходящего не складывается. Я подумал, может это из-за недостаточной теоретической базы? Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?
Errare humanum est
Отредактировано 08.02.2023 10:30 Максим . Предыдущая версия .
Re: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 08.02.23 10:41
Оценка: 8 (2) +2
Здравствуйте, Максим, Вы писали:

М>Потом еще один момент. Стандартная библиотека предлагает кучу утилитных методов для метапрограммирования. Часть из них, что-то типа 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. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее:

template<typename, typename = void>
struct is_class : std::false_type {};

template<typename T>
struct is_class<T, std::void_t<int T::*>> : std::true_type {};


И дело тут не только в количестве строк кода. Новые инструменты реально предоставляют больше возможностей. Так что, если ты ставишь себе целью прокачать свой уровень в метапрограммировании, то такие вот книжки — не очень хороший способ.

А после перехода на C++20 и эти подходы утратят свою актуальность и будут вытеснены концептами.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 08.02.23 11:02
Оценка:
R>Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее:

R>
R>template<typename, typename = void>
R>struct is_class : std::false_type {};

R>template<typename T>
R>struct is_class<T, std::void_t<int T::*>> : std::true_type {};
R>



Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?

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


Так ведь других нет (с) Буду очень благодарен за рекомендации хороших книг.

R>А после перехода на C++20 и эти подходы утратят свою актуальность и будут вытеснены концептами.


Я так и думал
Errare humanum est
Re[3]: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 08.02.23 11:09
Оценка: 4 (1)
Здравствуйте, Максим, Вы писали:

М>Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?


Большинство из этих утилит имеют простейшее определение в одну строчку. Главная их польза — это то, что они теперь есть в стандартной библиотеке и их не нужно писать самому каждый раз.

М>Так ведь других нет (с) Буду очень благодарен за рекомендации хороших книг.


Признаться честно, хороших книг в этом плане я даже и не знаю. Могу посоветовать почаще заходить на форум и не стесняться обращаться за подсказками. После приобретения некоторого старового опыта, когда ты прочувствуешь гланый принцип, ты приобретешь и большую самостоятельность в этих вопросах. Увидишь, что польшинство задач можно решить немного по-разному и научишься видедь плюсы и минусы разных решений.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Философско-практические вопросы про метапрограммирование
От: sergii.p  
Дата: 08.02.23 11:46
Оценка: 15 (2) +1
Здравствуйте, Максим, Вы писали:

М>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://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]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 08.02.23 12:05
Оценка:
Здравствуйте, sergii.p, Вы писали:

М>>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://www.cppstories.com/2018/05/tmplbook2nd-review/

SP>книга классная. Рекомендую.

С удивлением обнаружил у себя pdf-ку этой книжки. Судя по дате, валяется у меня на диске с сентября 2017-го. Вот думаю, как могло случиться, что она совершенно не отложилась у меня в памяти. Вариант, что я ее не открывал и не просматривал маловероятен.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 08.02.2023 12:06 rg45 . Предыдущая версия .
Re: Философско-практические вопросы про метапрограммирование
От: Voivoid Россия  
Дата: 08.02.23 15:08
Оценка: 8 (2) +1
Здравствуйте, Максим, Вы писали:

М>Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?


Опыт работы с функциональными языками считаю значительно поможет, т.к.

1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )
2) Шаблонное метапрограммирование иммутабельное, как в чисто функциональных языках

Конечно знакомиться с этими концепциями можно начинать и на C++, но это будет куда как сложее и менее продуктивно, чем если сразу начинать с haskell'а. При этом глубоко изучать haskell ( ну или любой другой чисто функциональный язык ) не надо, достаточно вот этих двух концепций — иммутабельность и паттерн-матчинг. Ну и про унификацию в prolog можно после этого чутка почитать, чтобы понять чем он от pattern матчинга отличается.
Отредактировано 08.02.2023 15:10 Voivoid . Предыдущая версия .
Re[2]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 08.02.23 16:17
Оценка:
V>1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )

Спасибо, только сейчас это осознал!
Errare humanum est
Re: Философско-практические вопросы про метапрограммирование
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 08.02.23 17:48
Оценка:
Здравствуйте, Максим, Вы писали:

М>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://www.cppstories.com/2018/05/tmplbook2nd-review/

М>Вот что немного затрудняет продвижение, так это многостраничные объяснения как сделать одно и то же в 11, 14 и 17 плюсах. Отсюда вопрос, на ваш взгляд, имеет ли смысл сейчас запариваться с предыдущими стандартами? Такое ощущение, что через год С++20 уже будет повсеместно, может имеет смысл сразу его осваивать? Там, насколько я понял, куча всяких нововведений типа концептов, которые еще сильнее могут упростить код.
если бы я читал книгу с таким названием, я бы прежде всего посмотрел не на то как в базе (т.е. еще до 11-х плюсов) реализовать is_base_of is_same, или is_virtual и их вариативность, мне было бы интересно как вести себя в современном мире программирования, где подавляющее большинство проектов содержит значительную часть, а зачастую и подавляющую часть легаси. как вести сбя, если есть фрагменты куда и буста то не подложить. Вы эти моменты не вычитывали?
Отредактировано 08.02.2023 17:49 ботаныч . Предыдущая версия .
Re: Философско-практические вопросы про метапрограммирование
От: cppguard  
Дата: 08.02.23 20:08
Оценка: 1 (1) +1 -6 :))) :))
Здравствуйте, Максим, Вы писали:

М>Там все таки система типов гораздо серьезней проработана... Или пустое это?


Нет никакой системы типов. Раньше были программисты, которым вкатывало обмазывать всё макросами. И пофиг на то, что коллеги потом два дня вывод компилятора разбирают, если запятой ошиблись. Потом подвезли шаблоны в С++, и эта кавалерия дружно направилась осванивать их. Возможно, у меня не хватает опыта (хотя работал в фанге на С++ проекте), но я принципиально не понимаю, чем тот же enable_if облегчает задачу. И с ним, и без него мы получаем ошибку компиляции, просто текст разный, но одинаково большой и непонятный. Исключения составляют лишь какая-нибудь хитровытраханная система типов, в которой enable_if волшебно переключает реализации. Я не могу найти пример из головы, но уверен, такое есть. Или какая-нибудь универсальная функция сериализации, которая не должна существовать, если ей передали указатель на ссылку на r-value (добавьте сюда ещё больше сюра). Так что не парься. Если тебе С++ нравится как язык, то практикуйся, смотри видео с CppCon (хотя там редко рассказывают про боевой код, больше про hellow-worldы). А если просто мучает совесть, что не знаешь С++ достаточно хорошо, чтобы за 5 минут погрузить собеседника в сон, то пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++. А из С++-программистов я могу только чемпионов по докладам на конференциях назвать. В корпорациях они, обычно, не пишут боевой код, а входят в "илитную" группу, которая после выхода каждого нового стандарта решает, как переписать всё старое говно на новый лад, или просто тупо форсят идеи корпорации при обсуждении нового стандарта в комитете.

P.S. Мне когда нечего делать, я смотрю доклады по C++ как альтернатива чтению или просмотру научной фантастики.
Re[3]: Философско-практические вопросы про метапрограммирова
От: SaZ  
Дата: 08.02.23 21:01
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>С удивлением обнаружил у себя pdf-ку этой книжки. Судя по дате, валяется у меня на диске с сентября 2017-го. Вот думаю, как могло случиться, что она совершенно не отложилась у меня в памяти. Вариант, что я ее не открывал и не просматривал маловероятен.


А её тут кто-то на форуме выкладывал. Я тоже скачал примерно в то время.
Re[2]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 09.02.23 08:18
Оценка: +2
C> Исключения составляют лишь какая-нибудь хитровытраханная система типов, в которой enable_if волшебно переключает реализации. Я не могу найти пример из головы, но уверен, такое есть.

Посто как пример. Может быть такая задача — вы пишите шаблон класса которому в качестве типа будет передан какой-то контейнер, но вы не знаете какой. Внутри вашего класса вам, по каким-то причинам, надо отсортировать этот контейнер. Причем, если контейнер внутри себя имеет метод sort, то надо дернуть его, а если нет, то вызвать std::sort(cont.begin(), cont.end()). Если нет ни sort, ни begin/end, то должна быть ошибка компиляции. Попробуйте реализуйте такое без SFINAE.
Errare humanum est
Re[3]: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 09.02.23 12:01
Оценка:
Здравствуйте, Максим, Вы писали:

М>Посто как пример. Может быть такая задача — вы пишите шаблон класса которому в качестве типа будет передан какой-то контейнер, но вы не знаете какой. Внутри вашего класса вам, по каким-то причинам, надо отсортировать этот контейнер. Причем, если контейнер внутри себя имеет метод sort, то надо дернуть его, а если нет, то вызвать std::sort(cont.begin(), cont.end()). Если нет ни sort, ни begin/end, то должна быть ошибка компиляции. Попробуйте реализуйте такое без SFINAE.


А если контейнер уже отсортирован (map, set...), то ничего не делать. В этом случае процедура сортировки существует лишь номинально.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 09.02.23 12:48
Оценка:
R>А если контейнер уже отсортирован (map, set...), то ничего не делать. В этом случае процедура сортировки существует лишь номинально.

Заинтриговали А по какимм критериям можно определить, что контейнер не нуждается в дополнительной сортировке?
Errare humanum est
Re[5]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 13:05
Оценка: 6 (2)
Здравствуйте, Максим, Вы писали:

М>Заинтриговали А по какимм критериям можно определить, что контейнер не нуждается в дополнительной сортировке?


Ну стандартные контейнеры std::map, std::set (multimap, multiset) по природе своей всегда отсортированы:

http://coliru.stacked-crooked.com/a/3ff8eb5d29c281a4

#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-ы. Ты свою обобщенную процедуру сразу делаешь заточенной на использование трейтсов. А в трейтсах уже можно навернуть аналитику любой сложности — от автоматического определения до ручной специализации. Использование трейтсов делает обощенные процедуры легко расширяемыми. Этот подход можно применить не ко всей обобщенной процедуре, а только к внутренней процедуре сортировки. В общем, все в зависимости от задачи.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 09.02.2023 13:15 rg45 . Предыдущая версия . Еще …
Отредактировано 09.02.2023 13:12 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 13:09 rg45 . Предыдущая версия .
Re[6]: Философско-практические вопросы про метапрограммирова
От: Максим Россия  
Дата: 09.02.23 13:19
Оценка: +1
R>А, по каким критериям? Ну, проще всего это сделать через traits-ы. Ты свою обобщенную процедуру сразу делаешь заточенной на использование трейтсов. А в трейтсах уже можно навернуть аналитику любой сложности — от автоматического определения до ручной специализации. Использование трейтсов делает обощенные процедуры легко расширяемыми. Этот подход можно применить не ко всей обобщенной процедуре, а только к внутренней процедуре сортировке. В общем, все в зависимости от задачи.

А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!

UPD
Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
Errare humanum est
Отредактировано 09.02.2023 14:33 Максим . Предыдущая версия . Еще …
Отредактировано 09.02.2023 13:27 Максим . Предыдущая версия .
Re[7]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 13:44
Оценка: 4 (1)
Здравствуйте, Максим, Вы писали:

М>А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!


Да, чуть позже только, сейчас немного дефицит со временем.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 14:51
Оценка:
Здравствуйте, Максим, Вы писали:

М>UPD

М>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>

Можно и так, а можно и по-другому, я потом предложу свой вариант.

P.S. Спасибо тебе за оценки, которые ты мне ставишь, но смотри не перестарайся, тут за большое количество оценок могут и оштрафовать: http://rsdn.org/Forum/Info/info.forum.rating.aspx
Автор: IT
Дата: 16.04.03
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: Философско-практические вопросы про метапрограммирова
От: Максим Россия  
Дата: 09.02.23 15:25
Оценка:
R>P.S. Спасибо тебе за оценки, которые ты мне ставишь, но смотри не перестарайся, тут за большое количество оценок могут и оштрафовать: http://rsdn.org/Forum/Info/info.forum.rating.aspx
Автор: IT
Дата: 16.04.03


Я и не думал, что тут такая развитая система рейтингов
Errare humanum est
Re[7]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 16:00
Оценка: 32 (4)
Здравствуйте, Максим, Вы писали:

М>А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!


Ну, примерно вот так. Нужно только понимать, что это эскиз, а не продакшн код, поэтому и воспринимать нужно с некоторой поправкой.

Хочется сразу сделать небольшое замечание по поводу traits-ов. В традиционном понимании traits-ы — это шаблонный клас со специализациями, содержащий в себе фиксированный набор свойств типов, которые используются в определенной обобщенной процедуре (или группе процедур). В этом примере использован немного другой подход. Traits-ы здесь представлены набором объявлений функций — именно объявлений, без определений. Эти объявления логически играют ту же самую роль — связывают между собой различные сущности времени компиляции — типы и константы. Но, по сравнению с классическим подходом, подход с фунциями имеет некоторые существенные преимущества: они допускают различную группировку по типам для разных свойств, а еще функции подчиняются правилам overload resolution и поиска по ADL. Трейтсы-функции объявляются в тех же пространствах имен, что и сами типы, прямо рядышком. Это здорово облегчает определение трейтсов и способствует комфортной расширяемости обобщенных процедур для пользовательских типов. Но есть и минус: трейтсы для стандартных типов и типов из сторонних библиотек должны быть видимы в точке использования (в метафункциях) и их приходится определять заранее. Но как по мне, это не большая плата за те преимущества, которые дает этот подход, а где-то это даже и логично.

http://coliru.stacked-crooked.com/a/f9923f0f88685d13

#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <utility>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Generic Library Area

template <typename, typename = void>
struct has_sort_member_function : std::false_type {};

template <typename T>
struct has_sort_member_function<T, std::void_t<decltype(std::declval<T&>().sort())>> : std::true_type {};

template <typename, typename = void>
struct is_std_sort_applicable : std::false_type{};

template <typename T>
struct is_std_sort_applicable<T, std::void_t<
   decltype(std::sort(std::begin(std::declval<T&>()), std::end(std::declval<T&>()))),
   decltype(*std::begin(std::declval<T&>()) = *std::begin(std::declval<T&>()))
>> : std::true_type {};

template <typename T>
auto assume_sorted_range(const T&) -> std::false_type;

// For standard containers, the traits must be defined beforehand
template <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>()));

template <typename T, std::enable_if_t<has_sort_member_function<T>::value, int> = 0>
void sort(T& t)
{
   t.sort();
}

template <typename T, std::enable_if_t<
   is_std_sort_applicable<T>::value &&
   !has_sort_member_function<T>::value &&
   !is_sorted_container<T>::value,
int> = 0>
void sort(T& t)
{
   std::sort(std::begin(t), std::end(t));
}

template <typename T, std::enable_if_t<
   is_sorted_container<T>::value &&
   !has_sort_member_function<T>::value,
int> = 0>
void sort(const T&)
{
   // Container is sorted already, so nothing to do.
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Application Area

struct MyContainer
{
   void sort() { std::cout << "MyContainer::sort" << std::endl; }
};

struct MySortedContainer
{

};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;

int main()
{
   std::string string = "Hello World!";
   sort(string); // call to std::sort
   std::cout << "<" << string << ">" << std::endl;

   int array[] { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   sort(array); // call to std::sort

   std::vector<int> vector { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   sort(vector); // call to std::sort

   std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   sort(set); // no effect

   MyContainer my_container;
   sort(my_container); // call to MyContainer::sort

   MySortedContainer my_sorted_container;
   sort(my_sorted_container); // no effect

   char str[] = "Hello World!";
   sort(str); // call to std::sort
   // sort("Hello World!"); // error: no matching function for call
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 09.02.2023 20:11 rg45 . Предыдущая версия . Еще …
Отредактировано 09.02.2023 20:09 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 20:00 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 16:22 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 16:06 rg45 . Предыдущая версия .
Re[2]: Философско-практические вопросы про метапрограммирование
От: σ  
Дата: 09.02.23 16:05
Оценка:
R>Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее

В книге примерно это пишут, ОП, видимо, не разобрался
Re[8]: Философско-практические вопросы про метапрограммирова
От: sergii.p  
Дата: 09.02.23 16:20
Оценка:
Здравствуйте, rg45, Вы писали:

R>
R>template <typename T>
R>auto consider_sorted_range(const T&) -> std::false_type;

R>// For standard containers, the traits must be defined beforehand
R>template <typename K, typename T, typename C, typename A>
R>auto consider_sorted_range(const std::map<K, T, C, A>&) -> std::true_type;

R>template <typename K, typename T, typename C, typename A>
R>auto consider_sorted_range(const std::multimap<K, T, C, A>&) -> std::true_type;

R>template <typename T, typename C, typename A>
R>auto consider_sorted_range(const std::set<T, C, A>&) -> std::true_type;

R>template <typename T, typename C, typename A>
R>auto consider_sorted_range(const std::multiset<T, C, A>&) -> std::true_type;

R>template <typename T>
R>using is_sorted_container = decltype(consider_sorted_range(std::declval<T>()));

R>


можно несколько короче

template <typename, template <typename...> typename>
struct is_instance : std::false_type {};

template <typename T, template <typename...> typename U>
struct is_instance<U<T>, U> : std::true_type {};

template <typename T>
using is_sorted_container = std::bool_constant<is_instance<T, std::set>::value
                                            && is_instance<T, std::map>::value
                                            && is_instance<T, std::multi_map>::value
                                            && is_instance<T, std::multi_set>::value>;
Re[9]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 16:29
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>можно несколько короче


SP>
SP>template <typename, template <typename...> typename>
SP>struct is_instance : std::false_type {};

SP>template <typename T, template <typename...> typename U>
SP>struct is_instance<U<T>, U> : std::true_type {};

SP>template <typename T>
SP>using is_sorted_container = std::bool_constant<is_instance<T, std::set>::value
SP>                                            && is_instance<T, std::map>::value
SP>                                            && is_instance<T, std::multi_map>::value
SP>                                            && is_instance<T, std::multi_set>::value>;
SP>


Нельзя. Возможность кастомизации для пользовательских типов тут же накроется. Там же ниже по коду специально пример использования:

struct MySortedContainer
{

};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: Философско-практические вопросы про метапрограммирова
От: Максим Россия  
Дата: 09.02.23 16:32
Оценка: +1
R>Ну, примерно вот так.

Класс!!! Спасибо большое, особенно за то, что показали как можно применять в реальной жизни "User-defined deduction guides". Не видел такого раньше!
Errare humanum est
Re[9]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 16:36
Оценка:
Здравствуйте, Максим, Вы писали:

М>Класс!!! Спасибо большое, особенно за то, что показали как можно применять в реальной жизни "User-defined deduction guides". Не видел такого раньше!


На здоровье! Обращайся, если что.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[9]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 20:30
Оценка: 1 (1)
Здравствуйте, Максим, Вы писали:

М>Класс!!! Спасибо большое, особенно за то, что показали как можно применять в реальной жизни "User-defined deduction guides". Не видел такого раньше!


Еще хочу сказать об одной "мелочи" вдогонку, про которую часто забывают. Когда пишешь какую-то обобщенную функцию, для всех возможных вариантов ее специализаций/перегрузок, нужно задаваться вопросом, в каком месте будет возникать ошибка, в случае неправильного использования. Например, при попытке вызвать метод sort для константного объекта (массива или стандартного контейнера), очень важно, чтобы ошибка компиляции возникала в клиентском коде, именно в том месте, где осуществляется попытка сделать неправильный вызов, а не где-то в потрохах библиотечного кода. А для этого нужно хорошо продумывать реализацию всех метафункций и состав фильтров SFINAE.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 10.02.2023 10:23 rg45 . Предыдущая версия . Еще …
Отредактировано 09.02.2023 20:37 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 20:31 rg45 . Предыдущая версия .
Re[9]: Философско-практические вопросы про метапрограммирова
От: Максим Россия  
Дата: 10.02.23 05:51
Оценка:
SP>можно несколько короче

SP>
SP>template <typename, template <typename...> typename>
SP>struct is_instance : std::false_type {};

SP>template <typename T, template <typename...> typename U>
SP>struct is_instance<U<T>, U> : std::true_type {};

SP>template <typename T>
SP>using is_sorted_container = std::bool_constant<is_instance<T, std::set>::value
SP>                                            && is_instance<T, std::map>::value
SP>                                            && is_instance<T, std::multi_map>::value
SP>                                            && is_instance<T, std::multi_set>::value>;
SP>


Спасибо за вариант!
Тут я так понимаю опечатка, вместо && должно быть ||.
И подскажите, пожалуйста, что это за синтаксис
template <typename T, template <typename...> typename U> struct is_instance<U<T>, U> : std::true_type {}


Что-то типа nested templates? Еще вот эти точки от variadic templates смущают.
Errare humanum est
Re[2]: Философско-практические вопросы про метапрограммирование
От: B0FEE664  
Дата: 10.02.23 17:59
Оценка:
Здравствуйте, cppguard, Вы писали:

C> пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++.


А вы пробовали читать их код?
И каждый день — без права на ошибку...
Re[7]: Философско-практические вопросы про метапрограммирова
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 11.02.23 05:31
Оценка: 4 (1)
Здравствуйте, Максим, Вы писали:

М>А можете, если есть время, более подробно написать

почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.

М>UPD

М>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
namespace hl = std;
template <template <typename... > class Tmp> struct holder {};

template <typename T> struct extract : hl::false_type {};

template <typename... Ts, template <typename... > class Tmpl>
struct extract<Tmpl<Ts...>> : hl::true_type
{
    typedef holder<Tmpl> type;
};
 
int main()
{
  enum {v = std::is_same<typename extract<T>::type, holder<std::set>>::value};
}
Re[8]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 11.02.23 11:11
Оценка:
Здравствуйте, ботаныч, Вы писали:

М>>А можете, если есть время, более подробно написать

Б> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.

М>>UPD

М>>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
Б>
Б>namespace hl = std;
Б>template <template <typename... > class Tmp> struct holder {};

Б>template <typename T> struct extract : hl::false_type {};

Б>template <typename... Ts, template <typename... > class Tmpl>
Б>struct extract<Tmpl<Ts...>> : hl::true_type
Б>{
Б>    typedef holder<Tmpl> type;
Б>};
 
Б>int main()
Б>{
Б>  enum {v = std::is_same<typename extract<T>::type, holder<std::set>>::value};
Б>}
Б>


И как потом с этим подходом можно будет расширять правила для пользовательских типов, находящихся в клиенстких простраствах имен? Например, как можно(нужно) переписать вот этот фрагмент:

http://coliru.stacked-crooked.com/a/94e3814cbca1fdad

namespace lib_01 {

template <typename T>
using is_sorted_container = decltype(assume_sorted_range(std::declval<T>()));

} // namespace lib_01

namespace app_01 {
namespace app_02 {
namespace app_03 {

struct MySortedContainer
{

};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;

} // namespace app_03
} // namespace app_02
} // namespace app_01


Теперь по поводу "покрывающим большее количество компиляторов": можешь показать ХОТЯ БЫ ОДИН компилятор, на котором твой пример компилируется, а мой нет?

А просадка времени компиляции откуда возьмется (по сравнению с твоим вариантом)? Объявления функций все просадят что ли?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.02.2023 12:03 rg45 . Предыдущая версия . Еще …
Отредактировано 11.02.2023 12:00 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 11:47 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 11:46 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 11:12 rg45 . Предыдущая версия .
Re[8]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 11.02.23 11:23
Оценка:
Здравствуйте, ботаныч, Вы писали:

Б>Здравствуйте, Максим, Вы писали:


М>>А можете, если есть время, более подробно написать

Б> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.

Б>
Б>namespace hl = std;
Б>template <template <typename... > class Tmp> struct holder {};

Б>template <typename T> struct extract : hl::false_type {};

Б>template <typename... Ts, template <typename... > class Tmpl>
Б>struct extract<Tmpl<Ts...>> : hl::true_type
Б>{
Б>    typedef holder<Tmpl> type;
Б>};
 
Б>int main()
Б>{
Б>  enum {v = std::is_same<typename extract<T>::type, holder<std::set>>::value};
Б>}
Б>


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

А то ведь знаешь как бывает, идея кажется правильной, пока не компилируется. А как начинаешь доводить до компиляции оказывается, что сама идея никуда не годится и нужно придумывать что-то другое.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.02.2023 11:52 rg45 . Предыдущая версия . Еще …
Отредактировано 11.02.2023 11:48 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 11:26 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 11:24 rg45 . Предыдущая версия .
Re[9]: Философско-практические вопросы про метапрограммирова
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 11.02.23 15:05
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, ботаныч, Вы писали:


М>>>А можете, если есть время, более подробно написать

Б>> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.

М>>>UPD

М>>>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
Б>>
Б>>namespace hl = std;
Б>>template <template <typename... > class Tmp> struct holder {};

Б>>template <typename T> struct extract : hl::false_type {};

Б>>template <typename... Ts, template <typename... > class Tmpl>
Б>>struct extract<Tmpl<Ts...>> : hl::true_type
Б>>{
Б>>    typedef holder<Tmpl> type;
Б>>};
 
Б>>int main()
Б>>{
Б>>  enum {v = std::is_same<typename extract<T>::type, holder<std::set>>::value};
Б>>}
Б>>


R>И как потом с этим подходом можно будет расширять правила для пользовательских типов, находящихся в клиенстких простраствах имен? Например, как можно(нужно) переписать вот этот фрагмент:

что ты что ты )) это прсто is_same — там в is_same set вставлен, я показал как можно сверить шаблон, ..вроде компилируется. не заметил ниже is_instance
это не сфинай для проверки наличия sort. sort сфиная проверка метода из олдовых SFINAE выглядит чуточку массивнее

R>А просадка времени компиляции откуда возьмется (по сравнению с твоим вариантом)? Объявления функций все просадят что ли?

а я не замерял, можно проверить кстати в сравнеии с олдовым SFINAE для проверки сорта с более свезжим способами. Я замечал, что тупая итерация типов в тупле гораздо дольше компилируется, чем если проделать это вручную через template <typename ...Ts> struct types;
вообще, по правде говоря встретив твой способ, воспользовался бы им, Но когда мне такое надобилось, не было таких способов, писалось классическим способом
Re[9]: Философско-практические вопросы про метапрограммирова
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 11.02.23 15:07
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, ботаныч, Вы писали:


Б>>Здравствуйте, Максим, Вы писали:


М>>>А можете, если есть время, более подробно написать

Б>> почитав код ниже, мне хочется вопрос задать. Вам это в каком контексте надо? Если для продакшин кода, то специфика одна, если для библиотечного кода, да еще и header only подход будет сршенно разным, для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов. Также Вам может понадобиться столкнуться с периодом — до variadic templates.

Б>>
Б>>namespace hl = std;
Б>>template <template <typename... > class Tmp> struct holder {};

Б>>template <typename T> struct extract : hl::false_type {};

Б>>template <typename... Ts, template <typename... > class Tmpl>
Б>>struct extract<Tmpl<Ts...>> : hl::true_type
Б>>{
Б>>    typedef holder<Tmpl> type;
Б>>};
 
Б>>int main()
Б>>{
Б>>  enum {v = std::is_same<typename extract<T>::type, holder<std::set>>::value};
Б>>}
Б>>


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


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

https://onlinegdb.com/4hmabsGci
да вот же, я же говорю, это просто проверка на инстанс сета ))
Re[10]: Философско-практические вопросы про метапрограммиров
От: rg45 СССР  
Дата: 11.02.23 15:49
Оценка:
Здравствуйте, ботаныч, Вы писали:

Б>https://onlinegdb.com/4hmabsGci

Б>да вот же, я же говорю, это просто проверка на инстанс сета ))

А для чего нам это проверка нужна вообще? Эта утилита родилась же в контексте нашей синтетической задачи, правда? Вот и попробуй применить свой подход при решении задачи: http://coliru.stacked-crooked.com/a/fdbaf2f292d21344. Только не умозрительно, а чтоб работало. Потом сравним. Обрати внимание, пример немного изменен — добавлены пространства имен, как это обычно бывает в реальной жизни.

P.S. Единственный плюс такого подхода в том, что он позволит перечислить все стандартные контейнеры в одной специализации. Но плюс достаточно сомнительный, как по мне, если учесть, что этой для того, чтобы эта монолитная специализация стала возможна, сперва нужно определить несколько дополнительных сущщностей, не слишком удобочитаемых. А в случаях, когда предполагается расширение специализаций для пользовательских типов, разбросанных по разным пространствам имен, использование этого подхода крайне неудобно и порождает в местах специализаций нагромождения кода, которые ужасно будут раздражать при первом же рефакторинге. Нагромождения связаны в первую очередь необходимостью разрывать пользовательские пространства имен для выполнения специализаций библиотечного класса. И, чем больше таких мест, тем более ужасная картина будет получаться. Вынос этих специализаций в какое-то отдельное место также имее свои ощутимые недостатки.

Вот видоизмененный пример, на котором можно прочувствовать проблему, о которой я говорю. Центральное место здесь занимает пользовательский класс MySortedContainer, который должен обрабатыватся по тем же правилам, что и отсортированные стандартные контейнеры (map, set и пр.).

http://coliru.stacked-crooked.com/a/fdbaf2f292d21344.

#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <utility>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Generic Library Area

namespace lib
{

template <typename, typename = void>
struct has_sort_member_function : std::false_type {};

template <typename T>
struct has_sort_member_function<T, std::void_t<decltype(std::declval<T&>().sort())>> : std::true_type {};

template <typename, typename = void>
struct is_std_sort_applicable : std::false_type{};

template <typename T>
struct is_std_sort_applicable<T, std::void_t<
   decltype(std::sort(std::begin(std::declval<T&>()), std::end(std::declval<T&>()))),
   decltype(*std::begin(std::declval<T&>()) = *std::begin(std::declval<T&>()))
>> : std::true_type {};

template <typename T>
auto assume_sorted_range(const T&) -> std::false_type;

// For standard containers, the traits must be defined beforehand
template <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>()));

template <typename T, std::enable_if_t<has_sort_member_function<T>::value, int> = 0>
void sort(T& t)
{
   t.sort();
}

template <typename T, std::enable_if_t<
   is_std_sort_applicable<T>::value &&
   !has_sort_member_function<T>::value &&
   !is_sorted_container<T>::value,
int> = 0>
void sort(T& t)
{
   std::sort(std::begin(t), std::end(t));
}

template <typename T, std::enable_if_t<
   is_sorted_container<T>::value &&
   !has_sort_member_function<T>::value,
int> = 0>
void sort(const T&)
{
   // Container is sorted already, so nothing to do.
}

} // namespace lib


namespace app1 {
namespace app2 {
namespace app3 {    
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Application Area

class MySortedContainer
{
public:

    auto begin() const { return m.begin(); }
    auto end() const { return m.end(); }

    void add_element(int key, int value) { m.emplace(key, value); }

private:
    std::map<int, int> m;
};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;

struct MyContainer
{
   void sort() { std::cout << "MyContainer::sort" << std::endl; }
};

void test()
{
   std::string string = "Hello World!";
   lib::sort(string);
   std::cout << "<" << string << ">" << std::endl;

   int array[] { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(array);

   std::vector<int> vector { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(vector);

   std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(set); // no effect

   MyContainer my_container;
   lib::sort(my_container);

   MySortedContainer my_sorted_container;
   lib::sort(my_sorted_container); // no effect

   char str[] = "Hello World!";
   lib::sort(str); // Ok
   // lib::sort("Hello World!"); // error: no matching function for call
}

} // namespace app3
} // namespace app2
} // namespace app1

int main()
{
    app1::app2::app3::test();
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.02.2023 17:05 rg45 . Предыдущая версия . Еще …
Отредактировано 11.02.2023 17:04 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 17:03 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:57 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:56 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:50 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:49 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:44 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:40 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:18 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:14 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:12 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:08 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:05 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 16:02 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 15:55 rg45 . Предыдущая версия .
Re[11]: Философско-практические вопросы про метапрограммиров
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 11.02.23 17:04
Оценка:
Здравствуйте, rg45, Вы писали:

R>Зачем нам эта проверка?

это был ответ на

оттолкнуться от std::is_same_v<T, std::set>

я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода.
я не говорил, что тут проверка на наличие метода sort. даже, что пример к этому относится относится
Re[12]: Философско-практические вопросы про метапрограммиров
От: rg45 СССР  
Дата: 11.02.23 17:11
Оценка:
Здравствуйте, ботаныч, Вы писали:

R>>Зачем нам эта проверка?

Б>это был ответ на
Б>

Б>оттолкнуться от std::is_same_v<T, std::set>

Б>я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода.
Б>я не говорил, что тут проверка на наличие метода sort. даже, что пример к этому относится относится

Ты говорил о предложенном мной подходе:

http://rsdn.org/forum/cpp/8467966.1
Автор: ботаныч
Дата: 11.02.23


для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов.


Как я понял, ты предлагаешь другой подход, который лучше. И мне трудно понять, на чем основываются твои выводы о скорости компиляции и о количестве доступных компиляторов. По-моему, твое решение проблемнее по обоим перечисленным аспектам. Мой подход основывается на простых объявлениях перегруженных функций, что может быть проще?. Или я неправильно тебя понял?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.02.2023 17:21 rg45 . Предыдущая версия . Еще …
Отредактировано 11.02.2023 17:19 rg45 . Предыдущая версия .
Re[12]: Философско-практические вопросы про метапрограммиров
От: rg45 СССР  
Дата: 11.02.23 17:36
Оценка:
Здравствуйте, ботаныч, Вы писали:

Б>я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода.


Нифига себе мало кода. Его мало, потому что ты не довел его до логического завершения. А ты доведи его хотя бы до определения метафункции и сравни:

  Мой вариант
// For standard containers, the traits must be defined beforehand
template <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>()));

  Твой вариант
namespace hl = std;

template <template <typename... > class Tmp> struct holder {};

template <typename T> struct extract : hl::false_type {};

template <typename... Ts, template <typename... > class Tmpl>
struct extract<Tmpl<Ts...>> : hl::true_type
{
    typedef holder<Tmpl> type;
};

template <typename, typename = void>
struct is_sorted_container : std::false_type {};

template <typename T>
struct is_sorted_container<T, std::enable_if_t<
    std::is_same<typename extract<T>::type, holder<std::set>>::value ||
    std::is_same<typename extract<T>::type, holder<std::map>>::value ||
    std::is_same<typename extract<T>::type, holder<std::multimap>>::value ||
    std::is_same<typename extract<T>::type, holder<std::multiset>>::value
>> : std::true_type {};


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

А еще поставь себя на место нового сотрудника, который недавно пришел и разбирается с исходниками компании. Какой вариант легче для понимания?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.02.2023 18:12 rg45 . Предыдущая версия . Еще …
Отредактировано 11.02.2023 18:11 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 18:02 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 17:57 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 17:52 rg45 . Предыдущая версия .
Отредактировано 11.02.2023 17:40 rg45 . Предыдущая версия .
Re[13]: Философско-практические вопросы про метапрограммиров
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 11.02.23 18:32
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, ботаныч, Вы писали:


R>>>Зачем нам эта проверка?

Б>>это был ответ на
Б>>

Б>>оттолкнуться от std::is_same_v<T, std::set>

Б>>я просто показал, что параметром шаблона неспециализированный шаблон низя, но сравнить можно — вот так, и ..там мало кода.
Б>>я не говорил, что тут проверка на наличие метода sort. даже, что пример к этому относится относится

R>Ты говорил о предложенном мной подходе:


R>http://rsdn.org/forum/cpp/8467966.1
Автор: ботаныч
Дата: 11.02.23


R>

R>для нового продакшин кода, подход rg45 достаточно лаконичен, где стандарт и набор компиляторов практически предопределен и меняется довольно редко (хотя и там будут вопросы по скорости компиляции, иногда короткие лаконичные вещи компилируются дольше). Если же это библиотечный код, то часто предпочтение отдается дедовским версиям SFINAE громоздким, но покрывающим большее количество компиляторов.


R>Как я понял, ты предлагаешь другой подход, который лучше.

нет. я сказал, что есть практики основанные на олдовых SFINAE для определения наличия методов. просто из своего опыта. из этой-же причине я предположил, что эти две разные имплементации могут компилироваться с разной скоростью.

R>И мне трудно понять, на чем основываются твои выводы о скорости компиляции и о количестве доступных компиляторов. По-моему, твое решение проблемнее по обоим перечисленным аспектам. Мой подход основывается на простых объявлениях перегруженных функций, что может быть проще?. Или я неправильно тебя понял?

свои предположения я строю опять-же на практике. я упоминал прецедент с туплом и тривиальным вариэдиком.

я не считаю один солюшин лучше, другой хуже. просто описал свои соображения. единственно что )) так это я не уверен, что смогу заставить себя тащить "свой" (он не мой) вариант.
Re[3]: Философско-практические вопросы про метапрограммирование
От: cppguard  
Дата: 12.02.23 16:09
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

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


C>> пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++.


BFE>А вы пробовали читать их код?


Код Торвальдся вполне читаем. У Беллара — write-only (я читал код tcc).
Re[9]: Философско-практические вопросы про метапрограммирова
От: _NN_ www.nemerleweb.com
Дата: 16.02.23 06:21
Оценка: +1
Здравствуйте, sergii.p, Вы писали:

SP>
SP>template <typename, template <typename...> typename>
SP>struct is_instance : std::false_type {};

SP>template <typename T, template <typename...> typename U>
SP>struct is_instance<U<T>, U> : std::true_type {};

SP>template <typename T>
SP>using is_sorted_container = std::bool_constant<is_instance<T, std::set>::value
SP>                                            && is_instance<T, std::map>::value
SP>                                            && is_instance<T, std::multi_map>::value
SP>                                            && is_instance<T, std::multi_set>::value>;
SP>


Так делать не стоит.
Дело в том, что операции && и || в этом контексте вынудят компилятор инстанциировать все варианты is_instance, а потом только произвести операцию «И», «ИЛИ».

Здесь нужно использовать std::conjuction, std::disjunction.

Или концепты где && и || будут означать std::conjuction, std::disjunction.

https://en.cppreference.com/w/cpp/types/conjunction
https://en.cppreference.com/w/cpp/types/disjunction
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Философско-практические вопросы про метапрограммирование
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 20.02.23 07:09
Оценка:
Здравствуйте, 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]: Философско-практические вопросы про метапрограммирование
От: Voivoid Россия  
Дата: 21.02.23 12:20
Оценка: +1
Здравствуйте, ботаныч, Вы писали:

Б> вы имеете в виду что нельзя сделать 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++
struct Nil;

template <typename Head, typename Tail>
struct Cons;

template <template <class> typename F, typename List>
struct Map;

template <template <class> typename F>
struct Map<F, Nil> {
    using result = Nil;
};

template <template <class> typename F, typename Head, typename Tail>
struct Map<F, Cons<Head, Tail>>
{
    using result = Cons<typename F<Head>::result, typename Map<F, Tail>::result>;
};


Тем более что по haskell куда больше книг и туториалов( по сравнению с материалами по шаблонному метапрограммированию ). А стоит ли говорить про сообщения об ошибках? По c++ ошибкам в таких делах будет практически невозможно понять что вообще нет так, что особенно фатально, когда только начинаешь со всем этим знакомиться.

Б> вы много назовете проектов, где будет такая возможность? если мы говорим о профессии. эо должно очень повезти, чтобы тебе на проекте позволили найти выделенный scope, чтобы внедрять что-то написанное на haskell, rust etc..

Так а зачем это в рамках рабочих проектов делать? Мне видится, что достаточно просто часов 20-30 потратить на базовое знакомство с haskell'ем, необходимости что-то в production'е на нем делать нет никакой.

Б> я предлагаю таки учить это на практике, именно на практических задачах, чтобы ты видел как оно взлетает

Это ж не rocket science, там ж не прям что-то такое, чего нельзя было бы осилить за 1-2 недели неспешных экспериментов с учебными примерами.
Re[3]: Философско-практические вопросы про метапрограммирование
От: Кодт Россия  
Дата: 21.03.23 17:13
Оценка:
Здравствуйте, Максим, Вы писали:

М>Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?


Есть такой ресурс — http://en.cppreference.com/w/
там часто приводятся типичные/упрощённые/эквивалентные определения всяких вещей "просто используя конструкции языка".

https://en.cppreference.com/w/cpp/types/integral_constant

template<class T, T v>
struct integral_constant
{
    static constexpr T value = v;
    using value_type = T;
    using type = integral_constant;
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; }
};

template<bool v> using bool_constant = integral_constant<bool, v>;

using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
Перекуём баги на фичи!
Re[4]: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 21.03.23 18:07
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Есть такой ресурс — http://en.cppreference.com/w/

К>там часто приводятся типичные/упрощённые/эквивалентные определения всяких вещей "просто используя конструкции языка".

С возвращением! Ты насовсем вернулся?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Философско-практические вопросы про метапрограммирование
От: Кодт Россия  
Дата: 22.03.23 11:01
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>С возвращением! Ты насовсем вернулся?


С набегом. Евгений Охотников во вконтакте написал что-то с отсылкой сюда, вот и заглянул.
Перекуём баги на фичи!
Re[6]: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 22.03.23 13:08
Оценка:
Здравствуйте, Кодт, Вы писали:

К>С набегом. Евгений Охотников во вконтакте написал что-то с отсылкой сюда, вот и заглянул.


Жаль, что набегом. Тебя здесь реально не хватает.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Философско-практические вопросы про метапрограммирова
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 23.03.23 00:25
Оценка:
Здравствуйте, Voivoid, Вы писали:


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


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 недели неспешных экспериментов с учебными примерами.

в одном из своих проектов я как-то ляпнул с дуру, что в принципе я бы понял
Отредактировано 23.03.2023 0:27 ботаныч . Предыдущая версия .
Re[5]: Философско-практические вопросы про метапрограммирова
От: vopl Россия  
Дата: 23.03.23 08:01
Оценка:
Здравствуйте, ботаныч, Вы писали:

Б> ... вопрос куда это все лепить? о тайп листах кстати

Б>они сейчас такие
Б>
Б>template <typename ... > struct type_list;
Б>


Б>главное для чего все это? где это в практике применить?

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

Например, я использую такое, кроме прочего, для "автоматизации работы с АПИ"

Сам тайплист: https://github.com/vopl/dci-core-utils/blob/master/include/dci/utils/ct.hpp#L27-L28

Пример его использования, для описания API некоего "объекта Future", состоящего из неких "свойств" и "методов": https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/cmtFuture.hpp#L94
далее этот лист (вместе с листами остальных "объектов") просовывается в qml и там можно объект Future использовать, работать с его свойствами и методами. Если бы эти АПИ проводить не через лист — пришлось бы изыскивать какие то другие средства для представления "набора сущностей", как например в Qt делается за счет ее мета-подсистемы. В общем, с тайплистами — получается вполне удобно. И весьма по сиплюсплюсному — максимум работы проделывается в компайл-тайм, а на рантайм остается только необходимый минимум.
Re[6]: Философско-практические вопросы про метапрограммирова
От: vopl Россия  
Дата: 23.03.23 08:16
Оценка:
Здравствуйте, vopl, Вы писали:

V>Например


Или даже так:
есть некий IDL, в рамках которого формируются некие структуры (имя + набор именованых типизированых полей + наследование)
состав этих структур IDL-компилятором проводится в такую вот интроспекцию https://github.com/vopl/dci-core-idl/blob/master/include/dci/idl/introspection.hpp#L31
из такой интроспекции получаем поля структуры https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L27
https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L39
(и это все в тех самых тайплистах размещается)
получаем для структуры базовое АПИ https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L43
склеиваем все воедино https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L45
и затем все это добро проводится в qml.

Итого, с помощью тайплистов удалось провести структуру (а там рядом и другие штуки — "интерфейс", "исключение", ..) из IDL сквозь C++ в QML
Re[7]: Философско-практические вопросы про метапрограммирова
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 23.03.23 15:59
Оценка:
Здравствуйте, vopl, Вы писали:

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


V>>Например


V>Или даже так:

V>есть некий IDL, в рамках которого формируются некие структуры (имя + набор именованых типизированых полей + наследование)
V>состав этих структур IDL-компилятором проводится в такую вот интроспекцию https://github.com/vopl/dci-core-idl/blob/master/include/dci/idl/introspection.hpp#L31
V>из такой интроспекции получаем поля структуры https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L27
V>https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L39
V>(и это все в тех самых тайплистах размещается)
V>получаем для структуры базовое АПИ https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L43
V>склеиваем все воедино https://github.com/vopl/dci-core-qml/blob/master/include/dci/qml/qmeta/def/idlStruct.hpp#L45
V>и затем все это добро проводится в qml.

V>Итого, с помощью тайплистов удалось провести структуру (а там рядом и другие штуки — "интерфейс", "исключение", ..) из IDL сквозь C++ в QML

я понимаю, зачем нужньі тайп листьі в С++. я задавал применимо к хацкелю. я понимаю зачем они нужньі в легаси проектах с дико разбухающим кодом. Но там везде С++, C bash я практически нигде не встречал хацкель. соответственно я и спросил куда мне вставлять ..работа с тайп листами добавляет возможность создавать компайлтайм метаданньіе в рамках которьіх можно половину задачи решить. наверное на хацкеле оно вьіглядело бьі куда интереснее, отсюда вопрос интеграции его для испольщования в проектах
Re[8]: Философско-практические вопросы про метапрограммирова
От: Voivoid Россия  
Дата: 23.03.23 21:09
Оценка:
Здравствуйте, ботаныч, Вы писали:

Б> я понимаю, зачем нужньі тайп листьі в С++. я задавал применимо к хацкелю.

хацкель затем чтобы познакомиться с иммутабельными рекурсивными структурами данных и алгоритмами, паттерн матчингом и алгебраическими типами данных не отвлекаясь при этом на чисто c++'ные проблемы неизбежно возникающие при попытке все вышеперечисленное использовать
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.