Какие самые крутые примеры метапрограммирования вы видели в C++?
Какие практические применения метапрограммирования вы видели в C++?
Какие самые крутые примеры метапрограммирования вы видели в C++?
Ответы (10)
Лично я думаю, что Boost.Spirit — довольно удивительный пример метапрограммирования. Это полный генератор синтаксического анализатора, который позволяет вам выражать грамматики с использованием синтаксиса C++.
Наиболее практичным применением метапрограммирования является превращение ошибки времени выполнения в ошибку времени компиляции.
Пример: позвоним интерфейсу IFoo. Одна из моих программ работала с COM-объектом, у которого было несколько путей к IFoo (очень сложная иерархия наследования). К сожалению, базовая реализация COM-объекта не понимала, что у них есть несколько путей к IFoo. Они предполагали, что это всегда был самый левый. Итак, внутри их кода очень часто встречался следующий шаблон
void SomeMethod(IFoo* pFoo) {
CFooImpl *p = (CFooImpl)pFoo;
}
Однако второй IFoo привел к тому, что результирующий указатель «p» стал полностью недействительным (множественное наследование опасно).
Долгосрочное решение заключалось в том, чтобы владелец COM-объекта исправил эту проблему. В краткосрочной перспективе мне нужно было убедиться, что я всегда возвращал правильный IFoo. Я мог гарантировать, что у меня есть соответствующий IFoo, используя QI и избегая любых неявных приведений к IFoo. Поэтому я создал новую реализацию CComPtr‹> и добавил следующее переопределение в метод equal.
template <typename T>
CComPtr<T>& operator=(const T* pT) {
// CComPTr Assign logic
}
template <>
CComPtr<IFoo> operator=<IFoo>(const IFoo* pT) {
COMPILE_ERROR();
}
Это быстро выявило каждое место, которое я неявно привёл к IFoo.
Не имеет практического применения (за исключением, возможно, тестирования компилятора), но metatrace является стилем Уиттеда (т.е. рекурсивным и детерминированным) трассировщик лучей, который генерирует подобные изображения во время компиляции:

Некоторые более сложные части кода можно увидеть в fixp.hh, в котором реализована реализация sqrt с фиксированной точкой с использованием метода Heron, или sphere.hh, который показывает расчет пересечения луча/сферы.
Blitz++ делает впечатляющие вещи с шаблонами (например, одну читаемую строку кода можно превратить в набор циклов над многомерным массивом, автоматически оптимизированный для наилучшего порядка обхода).
Самый крутой пример метапрограммирования: заставить компилятор вычислить список простых чисел. Не очень практично, но впечатляюще.
Одним из практических применений являются операторы assert во время компиляции, т. е. вызывающие ошибку компиляции, если логическое условие не выполняется.
Я бы сказал Boost.Lambda, Boost.Function и Boost.Bind и то, как они все работают без проблем вместе. Они обеспечивают действительно приятный интерфейс и максимально упрощают функциональное программирование на языке, который на самом деле не был создан для этого.
luabind — довольно классный практический пример, неплохой связующий dsl для привязки классов C++ к lua.
Статическое утверждение (ускоряет версию здесь)
(Примечание: встроенная поддержка циклов for на основе диапазона и статических утверждений введена в C++11)
Недавно я задал вопрос: Классы, и ответ, который я получил от пользователя StackOverflow "Denice", был URL-адресом веб-сайта Meatspace: регистрация класса среды выполнения C++.
Я думаю, что это действительно классный способ использовать шаблоны и создавать экземпляры объектов, которые все являются производными от базового класса, так что, когда у меня есть 10 файлов C++, все они могут просто добавить AUTO_REGISTER_BASE() внизу, а когда все готово сделано и связано, будут зарегистрированы только те классы/файлы, которые сделали это, поэтому во время выполнения вы можете переключаться между различными доступными классами, а те, которые недоступны, не регистрируются и, следовательно, не могут быть вызваны случайно.
Существует множество различных способов, зависящих от ОС, для уведомления о событиях (select(), kqueue(), /dev/epoll, в Solaris есть своя функция, poll()), и мне нужен был способ, чтобы все файлы классов существовали в каталог, но в зависимости от того, в какой ОС запускался Makefile, он компилировал только определенные файлы. Мне нужен был способ узнать во время выполнения, какие из них доступны, и иметь возможность для программиста, использующего библиотеку, выбрать свои предпочтения, однако, если это было недоступно, просто использовать тот, который имел наиболее логичный смысл для платформы (каждый из них им присвоены веса).
Приведенный выше код помог мне достичь этой цели с некоторыми значительными изменениями, но, тем не менее, он помог мне!