LINUX.ORG.RU

Modern C++ vs шаблонные метапрограммисты

 


2

9

Вот вам пример нормального современного C++, который, кстати, скоро может войти в состав SDK для оффтопа: github.com/kennykerr/modern

Вот вам пример, как библиотечные возможности STL и boost помогают отказаться от большинства паттернов GoF и тем самым избежать мусорных типов, таких как IXxxListener, IXxxObserver и т.д и т.п: accu.org/content/conf2013/Tobias_Darm_Effective_GoF_Patterns.pdf

Это всё - примеры нормального, простого языка, в котором авторы библиотек где-то далеко пишут мощные средства, работающие по принципу чёрного ящика с детальным описанием поведения, а обычные разработчики пишут просто и понятно даже джуниору.

Глядя же на большинство статей по С++11 и C++14, начинает уже тошнить от мета-программирования, супершаблонов с мегавариадиками, гипердеклтайпов с невероятно компильтаймовым вычислением абсолютно ненужных вещей (которые в 99% программ вообще незачем считать в компильтайме).

Почему так?

Глядя же на большинство статей по С++11 и C++14, начинает уже тошнить от мета-программирования, супершаблонов с мегавариадиками, гипердеклтайпов с невероятно компильтаймовым вычислением абсолютно ненужных вещей (которые в 99% программ вообще незачем считать в компильтайме).

Почему так?

Потому что статьи готовят авторов, которые где-то вдалеке пишут библиотеки // К.О.

tailgunner ★★★★★ ()

Статьи пишут те, у кого не получается не писать. Вряд ли после этого у них остаётся время заниматься кодом.

i-rinat ★★★★★ ()
Последнее исправление: i-rinat (всего исправлений: 1)

Не умеешь работать — учи. Старо, как мир. Вот и придумывают всякие ЪООП, как вон этот, который с io носится.

anonymous ()

Потому что в C++ (и/или в STL) нет некоторых часто необходимых вещей из коробки. Пара сильно сферических примеров в вакууме: получение некого идентефикатора типа в compile-time, какое-нибудь подобие концептов, сигналы. Тут тебе и variadic templates сразу оказываются нужны, и type erasure, и куча дополнительных хаков. Хорошо еще сейчас есть std::function, а до C++11 вообще простой абстрактный колбек метода класса (делегат) без обмазывания шаблонами сделать было невозможно. А вот идентефикатор типа получить вообще переносимым образом невозможно. Практически единственный вариант - парсить в compile-time выхлоп __PRETTY_FUNCTION__ (GCC/Clang-only) с последующим рассчетом хеша. При чем от всего этого извращения реально уже тошнит, но если потребуется сделать что-то хоть немного претендующее на универсальное решение - приходется вновь и вновь придумывать какого-то очередного уродца, героически сражаясь с ветряными мельницами. А в итоге все равно получится сомнительное решение. Конечно можно сказать что все мои примеры ущербны и высосаны из пальца, но я просто написал первое, что в пришло голову.

m0rph ★★★★★ ()
Последнее исправление: m0rph (всего исправлений: 3)
Ответ на: комментарий от m0rph

А вот идентефикатор типа получить вообще переносимым образом невозможно.

А можно на эту тему подробнее? Для каких случаев нужен хеш в compile-time?

eao197 ★★★★★ ()

А PDF по ссылке кто-нибудь читал?

Criticism
Debugging
• Do not debug Library code

Как мило.

tailgunner ★★★★★ ()
Ответ на: комментарий от eao197

А можно на эту тему подробнее? Для каких случаев нужен хеш в compile-time?

Чтобы можно было сделать некий аналог RTTI. В свое время для какой-то цели мне это было нужно. Можно конечно порассуждать на темы «убогий дизайн»/«кривое решение», т.к. в данный момент это выглядит именно так (раз не могу сходу описать подходящий пример). Сейчас могу привести довольно убогий вариант - автоматическое получение идентификатора типа для регистрации на фабрике, чтобы потом по этому id можно было создать объект. Этот вариант сильно спорный, т.к. id всегда можно задать вручную и автоматическая его генерация лишь немного оградила бы от ошибок. Но если заниматься чем-то подобным сейчас, то с любыми возможными костылями получится компиляторонепереносимый код, который использовать ни в коем случае нельзя (особенно, если применять в библиотеках).

m0rph ★★★★★ ()

Все нижесказанное - мое ИМХО

https://github.com/kennykerr/modern/blob/master/10.0.10240.complete/Sample.Co...

1.using namespace Windows::ApplicationModel::Core;
Если у тебя каждый второй тип/переменная функция/... из этого namespace - да, согласен. Но вцелом, я бы не рекомендоавл так сходу уж using по всем namespace: они ж для чего задумвались.

2. Описывать функции в классе - структуре - ну, для того примера еще нормально (в первном случае - лишь одна функция-член, во втором - короткая), но ИМХО не best practice.

3.

начинает уже тошнить от мета-программирования

Не хочешь - не используй. Но метапрограммирование, если его правилно использовать, позволяет отловить много ошибок на этапе компиляции; особенно актуально при модификации/реинжиниринге.
Хотя, да, метапрограммирование в C++ наркоманское.

А по поводу exception'ов - там отдельная тема.

А вот чего в С++ правда не хватает - так это общепризанного code notation и programming best practice. Это бы здорово облегчило понимание чужого кода.

Kroz ★★★★★ ()
Ответ на: комментарий от m0rph

Ну так покажите, когда нужен псевдо-идентификатор типа именно в compile-time. В compile-time вполне себе самими типами можно оперировать.

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Ну так покажите, когда нужен псевдо-идентификатор типа именно в compile-time. В compile-time вполне себе самими типами можно оперировать.

Если я приведу один пример, над которым в свое время работал исключительно из академического интереса, меня закидают тухлыми помидорами. Ну чтож, попробуем:
Один из моих лютых велосипедов - некий ServiceManager. Данный класс призван решить одну из проблем использования одиночек - неопределенность порядка создания/уничтожения. Сам ServiceManager является одиночкой, но при этом постулируется, что он должен быть единственной одиночной в программе. Он может регистрировать конкретные сервисы - модули, доступ к которым требуется из разных частей программы и которые иначе (если предположить, что разработчик решиться их использовать) были бы одиночками. ServiceManager берет на себя вопрос создания сервисов в правильном порядке (учитываются зависимости между ними), а так же их правильного удаления. Вот для регистрации сервисов в этом менеджере с учетом зависимостей и требуется знать id каждого конкретного сервиса в compile time.

m0rph ★★★★★ ()
Последнее исправление: m0rph (всего исправлений: 1)
Ответ на: комментарий от m0rph

Можно ли какой-то фрагмент псевдокода или кода показать. А то не понятно вообще, зачем здесь что-то сложнее чем

template< typename T > struct type_id {};

eao197 ★★★★★ ()

Вот вам пример, как библиотечные возможности STL и boost помогают

Глядя же на большинство статей по С++11 и C++14, начинает уже тошнить от мета-программирования

Только вот какое дело, без супершаблонов с мегавариадиками и гипердеклтайпами не было бы ни STL, ни boost'а, в том виде в котором на них можно писать «просто и понятно даже джуниору».

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

slovazap ★★★★★ ()
Ответ на: комментарий от eao197

Если только завтра. Пока что могу сказать, что функция регистрации сервиса является шаблонной функцией и идентификаторы сервисов-зависимостей передаются в качестве аргументов шаблона. Что-то вроде:

template<typename T, typename ...Deps>
bool ServiceManager::registerService()
{
    / skipped
}


Почему сделанно именно так (через аргументы шаблона) - не помню. Завтра попробую разобрать и тогда отвечу.

m0rph ★★★★★ ()
Ответ на: комментарий от m0rph

То, что тип сервиса для инстанциирования задается через параметр шаблона — это предсказуемо. Но ведь дальше можно использовать что-либо вроде Boost.MPL-векторов, в котором поиск будет вестись по самому типу, а не по его хэшу.

eao197 ★★★★★ ()

Продакшен код всегда пишется несколько не так, как исследовательские статьи, цель которых состоит в проверке чего-либо на прочность (либо возможностей языка, либо возможностей автора статьи). В продакшен слишком стремно запускать то, что еще не прошло достаточной проверки временем. Да и заумный код для продакшена не есть хорошо, т.к. не всегда легко найти другого такого же продвинутого разработчика, который мог бы заумный код сопровождать.

Ну и, не исключено, что для самих авторов статей код не кажется слишком уж сложным.

eao197 ★★★★★ ()

accu.org/content/conf2013/Tobias_Darm_Effective_GoF_Patterns.pdf

В Chain варианты с bind и лямбдами хуже «классического» варианта, ибо один и тот же список приходится повторять два раза. После добавления в один список можно забыть про остальные (их может быть больше, чем два) — и привет, баги.

i-rinat ★★★★★ ()

В C++ нет никакого метапрограммирования и никакого ООП. Это подмена понятий.

anonymous ()

Почему так?

потому что какие-то идиоты делают кривые костыли для компайл-тайм вычислений. Казалось бы, кто мешает сделать нормальный дополнительный язык для компайлтайма? Нет, сделают говно, а программисты потом начинают выискивать всякие лазейки как забацать на этом недоязыке нечто такое, что казалось бы забацать нельзя.

Т.е. изначально была заложена ущербная логика — вот вам типа тьюринг полный нормальный(в меру удобный) основной язык и вот вам ещё дополнительный покоцаный недоязык для компайл-тайма потому что мы считаем что вам хватит, а если не хватает то вы делаете что-то не правильно и хотите странного и вообще вы дураки.

Результат такого подхода несколько предсказуем.(а теперь сравните с макросами лиспа)

Bad_ptr ★★★★ ()
Последнее исправление: Bad_ptr (всего исправлений: 2)
Ответ на: комментарий от Bad_ptr

Т.е. изначально была заложена ущербная логика

AFAIK, логика изначально была несколько другая, никто не делал еще один недоязык для compile-time. Это уже потом продвинутые пользователи затем выяснили, что на шаблонах можно программировать в compile-time.

а теперь сравните с макросами лиспа

Судя по тому, что лисп нужен еще меньшему количество извращенцев, чем C++, результат как-то не в польщу лиспа получается.

eao197 ★★★★★ ()
Ответ на: комментарий от tailgunner

Список чего?

Ну вот пример «классического» подхода:

Condiment* milk = new Milk();
Condiment* sugarMilk = new Sugar(milk);
Condiment* doubleSugarMilk = new Sugar(sugarMilk);

cout << "Condiments: " << doubleSugarMilk->description() << '\n';
cout << "Price: " << doubleSugarMilk->price() << '\n';

в нём рецепт задаётся один раз.

А вот вариант с bind'ом:

Condiment condiments;
condiments.description = bind(&accu<string>, &Milk::description, condiments.description);
condiments.description = bind(&accu<string>, &Sugar::description, condiments.description);
condiments.description = bind(&accu<string>, &Sugar::description, condiments.description);

condiments.price = bind(&accu<float>, &Milk::price, condiments.price);
condiments.price = bind(&accu<float>, &Sugar::price, condiments.price);
condiments.price = bind(&accu<float>, &Sugar::price, condiments.price);

cout << "Condiments: " << condiments.description() << '\n';
cout << "Price: " << condiments.price() << '\n';

в нём последовательность Milk, Sugar, Sugar надо повторить дважды.

i-rinat ★★★★★ ()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от Kroz

насчёт code notation в C++. Вроде как там Страуструп и компания на гитхабе что-то подобное составляют. как раз таки большой сборник рекомендаций, как рекомендуется писать на крестах

zamazan4ik ★★ ()
Ответ на: комментарий от Bad_ptr

то есть Вы предлагаете держать 2 языка одновременно : С++RE(RuntimeEdition) и С++CE(Compile-time Edition).

Мне кажется, что это просто приведёт к тому, что появится ещё один отдельный ЯП, на который забьют и всё.

zamazan4ik ★★ ()
Ответ на: комментарий от zamazan4ik

Вы предлагаете держать 2 языка одновременно : С++RE(RuntimeEdition) и С++CE(Compile-time Edition).
Мне кажется, что это просто приведёт к тому, что появится ещё один отдельный ЯП, на который забьют и всё.

Ещё со времён Си есть 2 языка — сам си и си препроцессор(ну и что мешает например в си препроцессоре добавить присвоение переменных(передефинивание дефинов внутри дефинов)). И никто на него не забивает.
В С++ добавили ещё 1 костыль — язык шаблонов.

Bad_ptr ★★★★ ()
Ответ на: комментарий от Bad_ptr

В С++ добавили ещё 1 костыль — язык шаблонов.

А вот интересно, вам доводилось пользоваться C++ом когда шаблонов в нем еще не было?

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Если из C++ выбросить лямбды и auto, он превращается в «хотите испытать настоящую боль?» Если из него ещё и шаблоны убрать, получится что-то уж совсем а гранью.

i-rinat ★★★★★ ()
Ответ на: комментарий от Bad_ptr

Проблема препроцессора в том, что это отдельная тулза, которая работает до компилятора и ничего не знает про какие-то программы, языки и прочее. Он просто тупо ходит по файлу и заменяет один кусок текста другим. На нём просто невозможно сделать какую-то хоть сколько-нибудь сложную логику. Собственно, поэтому и родились шаблоны.

Gvidon ★★★★ ()
Ответ на: комментарий от i-rinat

Я просто помню, каким он был без шаблонов, STL и исключений. Помню, как в конце 90-х у меня была своя библиотека контейнеров, реализованная исключительно на макросах, т.к. приходилось работать с разными компиляторами на разных платформах, на некоторых из которых STL-ем даже не пахло.

Возвращаться в те времена не хочется от слова совсем.

Так что можно бросаться говном в шаблоны и жаловаться, что они не такие гладкие и шелковистые, как макросы в лиспе... Но C++ без шаблонов был мало кому нужен еще 20 лет назад. Не говоря уже про сейчас.

eao197 ★★★★★ ()

Где-то здесь проскакивала мысль одного умного человека, что C++ говно. Так вот я — с ним согласен.

ps: коты на юзерпиках из бэк!

anonymous ()
Ответ на: комментарий от Gvidon

В C++03 нет вещей, гораздо более важных - variadic templates и rvalue references.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от eao197

C++ без шаблонов был мало кому нужен еще 20 лет назад. Не говоря уже про сейчас.

Ну теперь-то понятно, что шаблоны УЖЕ не выбросят. Зато можно создать ещё 1 костыль, на котором будет проще делать компайл-тайм рейтрейсеры, чтобы не насиловать для этого шаблоны.

Bad_ptr ★★★★ ()
Ответ на: комментарий от Gvidon

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

Да. Препроцессор оперирует строками.

На нём просто невозможно сделать какую-то хоть сколько-нибудь сложную логику.

Ага. Потому что он ущербный. Возьмём утилиту sed — хотя она и предназначена для манипуляций с текстом, с помощью её языка был написан тетрис(вроде бы).

То же самое и с си препроцессором. Его можно было бы развить и до тьюринг полноты. Но нет, ни за что нельзя давать программисту мощные макросы, лучше пусть программисты занимаются такими извращениями — http://jhnet.co.uk/articles/cpp_magic

Bad_ptr ★★★★ ()
Последнее исправление: Bad_ptr (всего исправлений: 1)
Ответ на: комментарий от Gvidon

Ты не поверишь

Я в курсе. Просто я много раз порывался вникнуть в C++, но не складывалось. А с C++11 всё как-то веселее пошло.

i-rinat ★★★★★ ()
Ответ на: комментарий от eao197

Вряд ли. Это считается «плохой практикой». Но тяга программиста к извратам и оптимизации за счёт компайл-тайма неистребима.

Bad_ptr ★★★★ ()
Ответ на: комментарий от Bad_ptr

Эта тяга есть всегда. Даже когда людей загоняют в рамки Java или C#, они начинают лепить велосипеды на основе аспектно-ориентированного программирования, рефлексии в ран-тайме, аннотаций и пр.

eao197 ★★★★★ ()
Ответ на: комментарий от eao197

Эта тяга есть всегда.

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

Bad_ptr ★★★★ ()
Ответ на: комментарий от eao197

Судя по тому, что лисп нужен еще меньшему количество извращенцев, чем C++, результат как-то не в польщу лиспа получается.

Ну в нашем мире, по-моему, не всегда лучшее побеждает(во всяком случае в краткосрочной перспективе).

Bad_ptr ★★★★ ()
Последнее исправление: Bad_ptr (всего исправлений: 1)
Ответ на: комментарий от Bad_ptr

Им дали шаблоны - используй, используй шаблоны. Б*я, не хочу - хочу жрать макросы. Что такое? Это программисты?

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

Он вполне логичные вещи говорит. Шаблоны это «ну типа вот здесь как и здесь, и тут вот какой тип прилетит, такое поведение и подставим». Когда вся эта декларативность встает лесенкой, ты огребаешь комбинаторный взрыв. Другое дело, если бы можно было в компайл-тайме императивно собрать класс/функцию/модуль, запихать в него что-то оттуда и вон оттуда (причем только если путь к файлу не содержит буквы «ъ» и сейчас не полдень), и потом дать команду на взлет без засирания скопов, с нормальной степ-by-степ отладкой сборки и с произвольной диагностикой вместо однотипных марсианских выхлопов компилятора.

Вся крестовая писанина сводится к написанию программ, которые пишут программы, на языке, который для этого не предназначен, в среде, которая для этого не годится.

Им дали шаблоны - жри, жри шаблоны. Омномном. Что такое? Это программисты?

fixed that for us

arturpub ★★ ()
Ответ на: комментарий от Bad_ptr

и вот вам ещё дополнительный покоцаный недоязык для компайл-тайма

Шаблоны изначально вообще не предполагалось использовать для каких либо compiletime-вычислений. Это уже потом рептилоиды прислали Александреску, который и открыл ящик Пандоры. В предисловии к его Modern C++ design так и сказано.

one_more_hokum ★★★ ()

помогают отказаться от большинства паттернов GoF

Слышал, что уже про паттерны давным-давно на собеседованиях по C++ не спрашивают.

Modern это, кстати, просто очередной виток развития ATL. В данном случае с тулзой которая собирает библиотеку для удобной работы с ABI WinRT (Хотя какой, в жопу, «win»! Woe!) и умеет читать IDL.

Короче говоря, то что ребята из Борладна умели делать 20 лет назад.

ABI у WinRT — старый добрый COM... Господи, да я ещё школотой был, а M$ тогда всё грозилось: «Вот запилим новое ABI на основе COM (или тогда это ещё называлось OLE?) и всех порвём». Дорвались, млин!

И да:

As I mentioned in October, I have joined Microsoft and specifically the Windows team to work on the future of standard C++ for Windows. What does this mean for the Modern project? Modern C++ for the Windows Runtime is now owned by Microsoft. That means I won’t be releasing any further updates here or on GitHub. Instead, you should expect a future version to arrive directly from Microsoft. ...

Это вин, я щитаю. Вся ссущность M$ в одной цитате.

Короче говоря, никаким нормальным современным C++, тут и не пахнет. А пахнет всего лишь очередным костылём делающим боль от введения напрочь ушибленного CLR WinRT не такой пронзительной.

Macil ★★★★★ ()
Ответ на: комментарий от arturpub

к написанию программ, которые пишут программы

Слишком уж это претенциозно звучит для поиска и замены в исходном тексте. Это касается что макросов, что шаблонов, что любого другого препроцессорного говна. Метапрограммирование ко-ко-ко. реплейснуть кусок текста, ага, ехал мета через мету, lol. Современным хомячкам от программирования хочется звучать весомо. Нет в компилтайме никакого метапрограммирования, следует называть вещи своими именами — поиск и замена в тексте. Тогда и членов в головах будет поменьше, и понтов поубавится, и оффисный планктон четко осознает свое место...

callbackhell ()
Ответ на: комментарий от arturpub

А то гляди-ка, сколько тут мамкиных метапрограммистов развелось, плюнуть некуда.

callbackhell ()

Ух ты, анонiмус и в Си++ разбирается.

tailgunner ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.