Я обнаружил метапрограммирование шаблона более 5 лет назад и получил огромное удовольствие от чтения Современный дизайн C++, но я так и не нашел возможности использовать его в реальной жизни.
ты когда-нибудь использовал эту технику в реальном коде?
Contributors to Boost need not apply ;o)





Я использовал его во внутренних циклах графического кода игры, где вам нужен некоторый уровень абстракции и модульности, но вы не можете оплачивать затраты на ветвления или виртуальные вызовы. В целом это было лучшее решение, чем распространение рукописных функций для особых случаев.
Однажды я использовал метапрограммирование шаблонов в C++ для реализации техники, называемой «символическое возмущение», для работы с вырожденными входными данными в геометрических алгоритмах. Представляя арифметические выражения в виде вложенных шаблонов (то есть в основном путем написания деревьев синтаксического анализа вручную), я смог передать весь анализ выражений процессору шаблонов.
Выполнение такого рода действий с шаблонами более эффективно, чем, скажем, написание деревьев выражений с использованием объектов и выполнение анализа во время выполнения. Это быстрее, потому что модифицированное (возмущенное) дерево выражений затем доступно оптимизатору на том же уровне, что и остальная часть вашего кода, поэтому вы получаете все преимущества оптимизации как внутри ваших выражений, так и (где возможно) между вашими выражениями и окружающий код.
Конечно, вы могли бы добиться того же, реализовав небольшой DSL (предметно-ориентированный язык) для ваших выражений и вставив переведенный код C++ в вашу обычную программу. Это дало бы вам все те же преимущества оптимизации, а также было бы более разборчивым, но компромисс в том, что вам нужно поддерживать синтаксический анализатор.
Я предпочитаю поддерживать парсер лично, чем поддерживать вложенные шаблоны C++.
Вам не нужен парсер. Используйте систему метапрограммирования JetBrains. Вы можете определить язык, редактор и генератор кода. Вам вообще не нужен синтаксический анализатор, потому что вы редактируете дерево синтаксиса напрямую. И это не реклама, это программа FOSS.
Да, в основном для того, чтобы делать некоторые вещи, которые напоминают утиную типизацию, когда я оборачивал устаревший API в более современный интерфейс C++.
Нет, я не использовал его в производственном коде.
Почему?
К сожалению, это относится и к нам.
Тот факт, что ВЫ не можете использовать TMP из-за вашей рабочей среды, не является хорошей причиной не использовать его. Кроме того, OP хотел использовать случаи, а не ... this.
Многие программисты нечасто используют шаблоны из-за плохой поддержки компилятора до недавнего времени. Однако, хотя раньше у шаблонов было много проблем, новые компиляторы имеют гораздо лучшую поддержку. Я пишу код, который должен работать с GCC на Mac и Linux, а также с Microsoft Visual C++, и только с GCC 4 и VC++ 2005 этот компилятор действительно хорошо поддерживает стандарт.
Общее программирование с помощью шаблонов - это не то, что вам нужно постоянно, но, безусловно, полезный код, который нужно иметь в своем наборе инструментов.
Очевидный пример контейнерных классов, но шаблоны также полезны для многих других вещей. Два примера из моей собственной работы:
Вопрос касался шаблонов метаprogramming, а не простых старых шаблонов, которые используются гораздо чаще.
Метапрограммирование шаблонов и шаблоны выражений становятся все более популярными в научном сообществе как методы оптимизации, которые перекладывают часть вычислительных усилий на компилятор, сохраняя при этом некоторую абстракцию. Полученный код больше и менее читабелен, но я использовал эти методы для ускорения библиотек линейной алгебры и квадратурных методов в библиотеках FEM.
Для чтения, специфичного для приложения, Тодд Вельдхёйзен - громкое имя в этой области. Популярная книга - C++ и объектно-ориентированные числовые вычисления для ученых и инженеров Даоци Яна.
Что ж, судя по оглавлению, книга Д. Янга содержит 7 страниц материала по expr. шаблоны и метапрограммирование шаблонов. Остальное, похоже, представляет собой учебник по общему программированию с несколькими примерами численных методов. Конечно, ничего плохого в этом нет.
Справедливо. Для более полной справки я бы обратился к книге Александреску. Ссылки, которые я процитировал, относились к приложениям для научных вычислений и могут быть ограничены моими знаниями литературы. В книге Бартон / Накман тоже немного материала.
Большинство программистов, использующих метапрограммирование шаблонов, используют его косвенно, через библиотеки, такие как boost. Они, вероятно, даже не знают, что происходит за кулисами, только то, что это значительно упрощает синтаксис определенных операций.
Не делай этого. Причина этого заключается в следующем: по природе метапрограммирования шаблонов, если некоторая часть вашей логики выполняется во время компиляции, каждая логика, от которой она зависит, также должна выполняться во время компиляции. Как только вы запустите его, выполните одну часть своей логики во время компиляции, возврата нет. Снежный ком будет продолжать катиться, и его невозможно остановить.
Например, вы не можете выполнять итерацию по элементам boost :: tuple <>, потому что вы можете получить к ним доступ только во время компиляции. Вы должны использовать метапрограммирование шаблонов, чтобы достичь того, что было бы простым и понятным C++, и это всегда происходит, когда пользователи C++ недостаточно осторожны, чтобы не перенести слишком много вещей на время компиляции. Иногда трудно увидеть, когда определенное использование логики времени компиляции станет проблематичным, а иногда программисты стремятся попробовать и проверить то, что они прочитали в книге Александреску. В любом случае, на мой взгляд, это очень плохая идея.
mpl :: for_each, boost.fusion, умные cons <car, cdr> специализация - все это позволяет довольно легко переходить от компиляции к времени выполнения v
Что ж, не так уж и сложно определить предел, на котором вы хотите перейти от программирования времени компиляции к программированию во время выполнения. Очевидно, что не весь код, использующий шаблон, должен быть выполнен с использованием шаблонного программирования. И обычно у вас не так много уровней между универсальным кодом многократного использования и кодом конкретного приложения.
Я все время использую метапрограммирование шаблонов, но в D, а не в C++. Метаязык шаблонов C++ изначально был разработан для простой параметризации типов и почти случайно стал полным метаязыком Тьюринга. Таким образом, это брезент Тьюринга, который может использовать только Андрей Александреску, а не простые смертные.
С другой стороны, подъязык шаблонов D был разработан для метапрограммирования, выходящего за рамки простой параметризации типов. Андрей Александреску кажется, любит это,, но другие люди действительно могут понять его D-шаблоны. Он также достаточно мощный, чтобы кто-то написал в нем трассировщик лучей во время компиляции в качестве доказательства концепции.
Я думаю, что наиболее полезной / нетривиальной метапрограммой, которую я когда-либо писал на D, был шаблон функции, который, учитывая тип структуры в качестве параметра шаблона и список имен заголовков столбцов в порядке, соответствующем объявлениям переменных в структуре в качестве среды выполнения параметр, будет читать в CSV-файл и возвращать массив структур, по одной для каждой строки, с каждым полем структуры, соответствующим столбцу. Все преобразования типов (строка в число с плавающей запятой, int и т. д.) Выполняются автоматически в зависимости от типов полей шаблона.
Еще один хороший, который в основном работает, но все еще не обрабатывает несколько случаев должным образом, - это шаблон функции глубокого копирования, который правильно обрабатывает структуры, классы и массивы. Он использует только отражение / интроспекцию во время компиляции, так что он может работать со структурами, которые, в отличие от полноценных классов, не имеют возможностей отражения / интроспекции во время выполнения в D, потому что они должны быть легковесными.
Полезно знать о D, но я думаю, что речь идет о C++, а не о D.
Тем не менее, полезно знать, чего вы можете достичь с помощью метапрограммирования, когда вам не нужно быть гуру, чтобы понять это :)
Вроде ссылка на трассировщик лучей битая :(
Я обнаружил, что политики, описанные в Modern C++ Design, действительно полезны в двух ситуациях:
Когда я разрабатываю компонент, который, как я ожидаю, будет повторно использован, но немного по-другому. Предложение Александреску об использовании политики, отражающей дизайн, очень хорошо подходит здесь - это помогает мне ответить на вопросы вроде: «Я мог бы сделать это с помощью фонового потока, но что, если кто-то позже захочет сделать это во временных отрезках?» Хорошо, хорошо, я просто пишу свой класс, чтобы принять ConcurrencyPolicy и реализовать тот, который мне нужен в данный момент. Тогда, по крайней мере, я знаю, что человек, который придет за мной, может написать и подключить новую политику, когда ему это нужно, без необходимости полностью переделывать мой дизайн. Предостережение: иногда мне нужно управлять собой, иначе это может выйти из-под контроля - помните принцип ЯГНИ!
Когда я пытаюсь преобразовать несколько похожих блоков кода в один. Обычно код будет скопирован и немного изменен, потому что в противном случае в нем было бы слишком много логики if / else или потому что задействованные типы были слишком разными. Я обнаружил, что политики часто позволяют создать чистую универсальную версию, в отличие от традиционной логики или множественного наследования.
Спустя почти 8 месяцев после того, как я спросил об этом, я, наконец, использовал некоторый TMP, я использую ТипСписок интерфейсов, чтобы реализовать QueryInterface в базовом классе.
Я хотел сделать то же самое, но пока не удосужился. У нас есть много устаревшего кода, обернутого как объекты COM, поэтому к нему можно получить доступ из C#, но никто не хочет работать с ним или расширять его, потому что шаблон COM очень утомителен. Я уверен, что метапрограммирование шаблонов действительно может помочь, но простое программирование шаблонов не подходит из-за того, что правила COM отличаются от правил C++.
Я использую его с boost :: statechart для больших машин состояний.
Вы должны попробовать предстоящий boost: msm - мета-конечный автомат (надеюсь, в boost 2.0). Это основная структура конечного автомата, основанная на метапрограммировании. И его совместимость с UML2. Мы использовали его для нескольких проектов, и это просто потрясающе. Ну, с другой стороны, это долгое время компиляции ...
Так чем же он по функциям отличается от sc? Единственные концепции uml sc, которых не хватает, - это разливание и соединение, которые легко подражать.
@fmuecke Просто обновление. Я посмотрел на мсм теперь, когда он в самом разгаре. Это ни в коем случае не замена диаграмме состояний, поскольку в ней отсутствует какая-либо иерархия. MSM выглядит как диаграмма перехода состояний, где диаграмма состояний больше похожа на диаграмму состояний из потока состояний / simulink.
Я довольно часто использовал его с кодом DSP, особенно с БПФ, кольцевыми буферами фиксированного размера, преобразованиями Хадамара и т.п.
Мета-программирование шаблонов - прекрасный и мощный прием при написании C++ библиотеки. Я использовал его несколько раз в пользовательских решениях, но обычно менее элегантное решение C++ в старом стиле легче пройти через проверку кода и его легче поддерживать для других пользователей.
Тем не менее, при написании повторно используемых компонентов / библиотек у меня много опыта в метапрограммировании шаблонов. Я не говорю о чем-то таком большом, о некоторых вещах Boost, просто о небольших компонентах, которые будут часто использоваться повторно.
Я использовал TMP для одноэлементной системы, где пользователь мог указать, какой тип синглтона им нужен. Интерфейс был очень простым. Под ним был приведен в действие тяжелый ТМЗ.
template< typename T >
T& singleton();
template< typename T >
T& zombie_singleton();
template< typename T >
T& phoenix_singleton();
Еще одним успешным применением было упрощение нашего уровня IPC. Он построен в классическом стиле OO. Каждое сообщение должно быть производным от абстрактного базового класса и переопределять некоторые методы сериализации. Ничего особенного, но он генерирует много шаблонного кода.
Мы добавили в него немного TMP и автоматизировали генерацию всего кода для простого случая сообщений, содержащих только данные POD. В сообщениях TMP по-прежнему используется объектно-ориентированный бэкэнд, но они значительно сокращают объем стандартного кода. TMP также использовался для создания вистора сообщений. Со временем все наше сообщение перешло на метод TMP. Было проще и меньше кода создать простую структуру POD только для передачи сообщений и добавить несколько (возможно, 3) строк, необходимых для того, чтобы TMP сгенерировал классы, чем вывести новое сообщение для отправки обычного класса через IPC. рамки.
Для тех, кто знаком с библиотекой шаблонов Oracle (OTL), boost :: any и библиотекой Локи (описанной в Modern C++ Design), вот доказательство концепции TMP-кода, который позволяет хранить одну строку otl_stream в контейнере vector<boost::any> и получать доступ к данным с помощью номер столбца. И «Да», я собираюсь включить это в производственный код.
#include <iostream>
#include <vector>
#include <string>
#include <Loki/Typelist.h>
#include <Loki/TypeTraits.h>
#include <Loki/TypeManip.h>
#include <boost/any.hpp>
#define OTL_ORA10G_R2
#define OTL_ORA_UTF8
#include <otlv4.h>
using namespace Loki;
/* Auxiliary structs */
template <int T1, int T2>
struct IsIntTemplateEqualsTo{
static const int value = ( T1 == T2 );
};
template <int T1>
struct ZeroIntTemplateWorkaround{
static const int value = ( 0 == T1? 1 : T1 );
};
/* Wrapper class for data row */
template <class TList>
class T_DataRow;
template <>
class T_DataRow<NullType>{
protected:
std::vector<boost::any> _data;
public:
void Populate( otl_stream& ){};
};
/* Note the inheritance trick that enables to traverse Typelist */
template <class T, class U>
class T_DataRow< Typelist<T, U> >:public T_DataRow<U>{
public:
void Populate( otl_stream& aInputStream ){
T value;
aInputStream >> value;
boost::any anyValue = value;
_data.push_back( anyValue );
T_DataRow<U>::Populate( aInputStream );
}
template <int TIdx>
/* return type */
Select<
IsIntTemplateEqualsTo<TIdx, 0>::value,
typename T,
typename TL::TypeAt<
U,
ZeroIntTemplateWorkaround<TIdx>::value - 1
>::Result
>::Result
/* sig */
GetValue(){
/* body */
return boost::any_cast<
Select<
IsIntTemplateEqualsTo<TIdx, 0>::value,
typename T,
typename TL::TypeAt<
U,
ZeroIntTemplateWorkaround<TIdx>::value - 1
>::Result
>::Result
>( _data[ TIdx ] );
}
};
int main(int argc, char* argv[])
{
db.rlogon( "AMONRAWMS/[email protected]" ); // connect to Oracle
std::cout<<"Connected to oracle DB"<<std::endl;
otl_stream o( 1, "select * from blockstatuslist", db );
T_DataRow< TYPELIST_3( int, int, std::string )> c;
c.Populate( o );
typedef enum{ rcnum, id, name } e_fields;
/* After declaring enum you can actually acess columns by name */
std::cout << c.GetValue<rcnum>() << std::endl;
std::cout << c.GetValue<id>() << std::endl;
std::cout << c.GetValue<name>() << std::endl;
return 0;
};
Для тех, кто не знаком с указанными библиотеками.
Проблема с контейнером otl_stream OTL заключается в том, что можно получить доступ к данным столбцов только в последовательном порядке, объявив переменные соответствующего типа и применив operator >> к объекту otl_stream следующим образом:
otl_stream o( 1, "select * from blockstatuslist", db );
int rcnum;
int id;
std::string name;
o >> rcnum >> id >> name;
Это не всегда удобно. Обходной путь - написать класс-оболочку и заполнить его данными из otl_stream. Желание состоит в том, чтобы иметь возможность объявить список типов столбцов, а затем:
olt_stream::operator >>(T&)Все это можно сделать с помощью структуры Loki Typelist, специализации шаблонов и наследования.
С помощью библиотечных конструкций Loki вы также можете сгенерировать набор функций GetValue, которые возвращают значения соответствующего типа, выводя их из номера столбца (на самом деле номера типа в Typelist).
В этом проекте используется шаблонное программирование (инструмент-график): git.skewed.de/count0/graph-tool/wikis/…