Недавно начал изучение "Современного проектирования на С++" Александреску. Соответственно, смотрел Loki, немного boost::mpl. Местами сложно, местами непривычно, но очень интересно.
Возникло несколько вопросов:
1. С чего начать изучение boost::mpl с надеждой на практическое применение?
Открыв бустовскую документацию по mpl вырвалось что-то вроде "<censored> и за что же браться?". Очень много всего, но 90% того, что я увидел, с моей точки зрения (могу ошибаться по неопытности) неприменимо в коммерческом проекте — либо слишком сложно для написания, понимания и сопровождения. Например, вывод всех символов 16-ричной системы счисления в константную строку во время компиляции я сделаю за 10 минут вручную (static const std::wstring A::digits="..."), или за 120 минут на mpl. Когда я уйду в отпуск, если это придётся подправить другому программисту — "хардкод" он подправит за 10 минут, а boost::mpl за 60. Если единственный аргумент в этом примере за mpl — это "идеологическая правильность", то выбор инструмента для души и для продакшена очевиден. Для души — mpl дома на коленке, поигрался и забыл, для продакшена — хардкод и беремся за следующую фичу.
Из того, что попалось на глаза, полезным показался if_ и if_c, sequence (для оптимизации) и семпл из .../libs/mpl/examples/integer.cpp — битовая маска заданного размера.
Само собой, boost/std::function, bind, lambda я как mpl не рассматриваю. Это вещи для меня очень приятные и полезные.
2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?
Поиск по форуму выдал огромную кучу информации, но ознакомившись с ней, я так и не понял, кто получил от использования mpl или Loki радость и облегчение, а кто — гордость за то, что "мы сами не верим, что смогли это сделать, не дай бог еще раз такой дизайн...".
Если кто-то поделится опытом не только "как надо", а и как "не надо" — буду тоже очень признателен, тема для меня актуальна и очень интересна.
Особенно интересует опыт использования в небиблиотечном коде. То есть, в обычном проекте, в котором немного повторно использованного кода, написанного рукастым или головастым девелопером, немного говнокода (куда ж без него? Работает и ладно, не досуг, заказчик просит новые фичи), заказчики сами не девелоперы и красоту настоящего С++ им не понять
Здравствуйте, SpiritFire, Вы писали:
SF>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?
Написал свой смартпоинтер, часто использую в реальных проектах. Очень сильно облегчает жизнь и экономит массу времени.
Каплю Policy-based desigh: Шаблонная структура с парой политик CopyAble и NonCopyable, выбор алгоритма через boost::if_ (точнее, через свой велоif);
Traits и похожие обёртки, например, LockedReference — обертка над ссылкой, "склеенная" с мьютексом;
Немного удаления квалификаторов: Убирание const-ов, volatile, *, &;
На данный момент это всё, что увидел, пронравилось, потом реализовал и снова понравилось (причем не только мне).
Особый вопрос насчет tumple... Не понимаю я эту идею. Насколько я понял, смысл такой же, как в std::pair<>, только с бОльшим числом членов. Может, я что-то не понимаю, но последний у меня аллергия:
std::pair<std::string, std::string> user = collector->getUser(name);
cout << user.first << ": " << user.second << std::endl; // WTF?!! А если бы было еще и ->third и ->fourth
TUser user = collector->getUser(name);
cout << user.fullName << ": " << user.email << std::endl; // всё понятно
Re[2]: Практическое применения метапрограммирования
Здравствуйте, opener, Вы писали: SF>>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь? O>Написал свой смартпоинтер, часто использую в реальных проектах. Очень сильно облегчает жизнь и экономит массу времени.
Если не секрет, чем стандартные не понравились? Понадобилась межпоточная блокировка внутри, что-то еще, или просто boost не хотелось тянуть в проект?
Здравствуйте, SpiritFire, Вы писали:
SF>Если кто-то поделится опытом не только "как надо", а и как "не надо" — буду тоже очень признателен, тема для меня актуальна и очень интересна.
Надо хорошо, не надо плохо Ну и меру надо знать.
В общем, каких-то специальных советов тут нету.
SF>Особенно интересует опыт использования в небиблиотечном коде. То есть, в обычном проекте, в котором немного повторно использованного кода, написанного рукастым или головастым девелопером, немного говнокода (куда ж без него? Работает и ладно, не досуг, заказчик просит новые фичи), заказчики сами не девелоперы и красоту настоящего С++ им не понять
это то, что я публиковал, а так в рабочем коде еще мульон примеров.
Например, использование switch_ от Стивена Ватанабе для свича по типам для диспатча сообщений согласно их типу, boost::fusion и boost::variant прямо в интерфейсе элементы MPL содержат, плюс у меня есть мегадевайс с блэкджеком и сериализацией, в котором намешано и шаблонного, и препроцессорного метапрограммирования (последнее — тоже вполне себе метапрограммирование, не одними шаблонами живы).
Не говоря уже о примитивных вещах типа вычисления правильного типа чего-нть в зависимости от других типов и их свойств.
А, вот, специальный совет для шаблонного програмирования (не обязательно мета-).
Надо инстанцировать как можно меньше шаблонов и ловить ошибки как можно раньше, причем отлавливать их так, чтобы после них ничего не компилировалось, потому что иначе компилятор попытается покомпилировать то, что уже заведомо неверно и нагенерит мегабайтный листинг с ошибками.
Т.е. вместо
template<class T> void f(T x) {
BOOST_MPL_ASSERT(boost::is_intergral<T>);
// код, который использует Т и нагенерит кучу ненужных ошибок
// в дополнение к сообщению об ошибке от ассерта выше
}
надо писать так:
template<class T> void f_impl(T x, boost::false_type) {
BOOST_MPL_ASSERT_MSG(false,BAD_TYPE,(T));
// больше никакого кода!
// зато можно (и нужно) поставить комментарий, как пофиксить
}
template<class T> void f_impl(T x, boost::true_type) {
// код, который использует правильный Т и никаких ошибок не нагенерит
}
template<class T> void f(T x) {
f_impl( x, boost::is_intergral<T>() );
}
Аналогичный прием используется и в самом бусте (примет из Спирита):
static void define(rule& lhs, Expr const& expr, mpl::false_)
{
// Report invalid expression error as early as possible.// If you got an error_invalid_expression error message here,
// then the expression (expr) is not a valid spirit qi expression.
BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
}
Здравствуйте, SpiritFire, Вы писали:
SF>Недавно начал изучение "Современного проектирования на С++" Александреску. Соответственно, смотрел Loki, немного boost::mpl. Местами сложно, местами непривычно, но очень интересно.
SF>Возникло несколько вопросов:
SF>1. С чего начать изучение boost::mpl с надеждой на практическое применение?
хорошая книга:
Aleksey Gurtovoy "C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond"
Re[2]: Практическое применения метапрограммирования
Здравствуйте, SpiritFire, Вы писали:
SF>Что использовал в проекте сам:
SF>Особый вопрос насчет tumple... Не понимаю я эту идею. Насколько я понял, смысл такой же, как в std::pair<>, только с бОльшим числом членов. Может, я что-то не понимаю, но последний у меня аллергия:
по кортежам можно итерироваться иногда это очень удобно (например можно написать функцию для сериализации и все сериализуемые данные держать в кортежах)
а чтобы не было WTF можно писать так:
Здравствуйте, SpiritFire, Вы писали:
SF>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь?
Когда писал вот эту библиотеку https://sourceforge.net/projects/pybind/
Здравствуйте, SpiritFire, Вы писали:
SF>Здравствуйте, opener, Вы писали: SF>>>2. Хотелось бы спросить у более опытных коллег, в каких случаях (можно с примерами) метапрограммирование на плюсах упростило вам жизнь? O>>Написал свой смартпоинтер, часто использую в реальных проектах. Очень сильно облегчает жизнь и экономит массу времени.
SF>Если не секрет, чем стандартные не понравились? Понадобилась межпоточная блокировка внутри, что-то еще, или просто boost не хотелось тянуть в проект?
Проще было написать свой, чем разбираться, как прикрутить стандартный.
Re[2]: Практическое применения метапрограммирования
Здравствуйте, jazzer, Вы писали:
J> // код, который использует Т и нагенерит кучу ненужных ошибок J> // в дополнение к сообщению об ошибке от ассерта выше
А если static_assert, то он брякнет компилятор сразу же или будет дальше компилировать с генерацией "кучи ненужных ошибок"?
Re[3]: Практическое применения метапрограммирования
Здравствуйте, cppnick, Вы писали:
C>Здравствуйте, jazzer, Вы писали:
J>> // код, который использует Т и нагенерит кучу ненужных ошибок J>> // в дополнение к сообщению об ошибке от ассерта выше C>А если static_assert, то он брякнет компилятор сразу же или будет дальше компилировать с генерацией "кучи ненужных ошибок"?
Обычно компиляторы стараются выдать за раз максимум ошибок, чтоб ты их сразу мог поправить, а не останвливаются после первой же.
Так что даже со статик-ассертом они пытаются компилировать дальше насколько возможно и генерят сообщения об ошибках.