Кто-нибудь использует шаблонное метапрограммирование в реальной жизни?[закрыто]

StackOverflow https://stackoverflow.com/questions/63494

Вопрос

Я обнаружил метапрограммирование шаблонов более 5 лет назад и получил огромное удовольствие от чтения Современный дизайн на C ++ но я так и не нашел возможности использовать это в реальной жизни.

Иметь ты вы когда-нибудь использовали эту технику в реальном коде?

Вкладчики в Повышение не нужно применять;o)

Это было полезно?

Решение

Однажды я использовал шаблонное метапрограммирование в C ++ для реализации метода под названием "символическое возмущение" для работы с вырожденными входными данными в геометрических алгоритмах.Путем представления арифметических выражений в виде вложенных шаблонов (т. е.в основном, выписывая деревья синтаксического анализа вручную) Я смог передать весь анализ выражений процессору шаблонов.

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

Конечно, вы могли бы сделать то же самое, внедрив небольшой DSL (domain specific language) для ваших выражений и вставив переведенный код C ++ в вашу обычную программу.Это дало бы вам все те же преимущества в оптимизации, а также было бы более разборчивым - но компромисс заключается в том, что вы должны поддерживать синтаксический анализатор.

Другие советы

Я обнаружил, что политики, описанные в Modern C ++ Design, действительно полезны в двух ситуациях:

  1. Когда я разрабатываю компонент, который, как я ожидаю, будет использован повторно, но немного по-другому.Предложение Александреску об использовании политики для отражения дизайна очень хорошо подходит здесь - это помогает мне избавиться от вопросов типа: "Я мог бы сделать это с помощью фонового потока, но что, если кто-то позже захочет сделать это срезами времени?" Хорошо, я просто пишу свой класс, чтобы принять concurrencyPolicy и реализовать тот, который мне нужен в данный момент.Тогда, по крайней мере, я буду знать, что человек, который стоит за моей спиной, может написать и подключить новую политику, когда ему это понадобится, без необходимости полностью переделывать мой дизайн.Предостережение:Иногда я должен контролировать себя, иначе это может выйти из-под контроля - помните ЯГНИ принцип!

  2. Когда я пытаюсь реорганизовать несколько похожих блоков кода в один.Обычно код копируется и слегка модифицируется, потому что в противном случае в нем было бы слишком много логики if / else или потому, что задействованные типы были слишком разными.Я обнаружил, что политики часто допускают чистую универсальную версию там, где традиционная логика или множественное наследование невозможны.

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

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

Для чтения в зависимости от конкретного приложения, Тодд Вельдхейзен это громкое имя в этой области.Популярная книга - это C ++ и объектно-ориентированные числовые вычисления для ученых и инженеров автор: Даоци Ян.

Шаблонное метапрограммирование - замечательный и мощный метод при написании c ++ библиотеки.Я использовал его несколько раз в пользовательских решениях, но обычно менее элегантное решение в старом стиле c ++ легче получить с помощью code review и проще поддерживать для других пользователей.

Тем не менее, я получил большой опыт использования шаблонного метапрограммирования при написании повторно используемых компонентов / библиотек.Я не говорю о чем-то таком большом, что некоторые материалы Boost - это просто небольшие компоненты, которые будут часто использоваться повторно.

Я использовал TMP для одноэлементной системы, где пользователь мог указать, какой тип синглтона ему нужен.Интерфейс был очень простым.Под ним находилось питание от тяжелого ТМП.

template< typename T >
T& singleton();

template< typename T >
T& zombie_singleton();

template< typename T >
T& phoenix_singleton();

Другим успешным применением было упрощение нашего уровня IPC.Он построен в классическом стиле OO.Каждое сообщение должно быть производным от абстрактного базового класса и переопределять некоторые методы сериализации.Ничего слишком экстремального, но это генерирует много кода котельной плиты.

Мы добавили в него немного TMP и автоматизировали генерацию всего кода для простого случая сообщений, содержащих только POD-данные.Сообщения TMP по-прежнему использовали серверную часть OO, но они значительно уменьшают объем кода на котельной плите.TMP также использовался для генерации сообщения vistor.Со временем все наши сообщения перекочевали в метод TMP.Было проще и меньше кода создать простую структуру POD только для передачи сообщений и добавить несколько (возможно, 3) строк, необходимых для получения TMP для генерации классов, чем для получения нового сообщения для отправки обычного класса через платформу IPC.

Я постоянно использую шаблонное метапрограммирование, но на D, а не на C ++.Шаблонный метаязык C ++ изначально был разработан для простой параметризации типов и почти случайно стал полным метаязыком по Тьюрингу.Таким образом, это тарпит Тьюринга, которым может пользоваться только Андрей Александреску, а не простые смертные.

Шаблонный подъязык D, с другой стороны, на самом деле был разработан для метапрограммирования, выходящего за рамки простой параметризации типов.Андрей Александреску кажется, ему это нравится, но другие люди действительно могут понять его шаблоны D.Это также достаточно мощно, чтобы кто-то написал трассировщик лучей во время компиляции в нем в качестве доказательства концепции.

Я думаю, что самой полезной / нетривиальной метапрограммой, которую я когда-либо писал в D, был шаблон функции, который, учитывая тип структуры в качестве параметра шаблона и список имен заголовков столбцов в порядке, соответствующем объявлениям переменных в struct в качестве параметра времени выполнения, будет считываться в CSV-файле и возвращать массив структур, по одной для каждой строки, с каждым полем struct, соответствующим столбцу.Все преобразования типов (string в float, int и т.д.) Выполняются автоматически, в зависимости от типов полей шаблона.

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

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

Я довольно часто использовал это с DSP-кодом, особенно с БПФ, циклическими буферами фиксированного размера, преобразованиями Адамара и тому подобным.

Для тех, кто знаком с библиотекой шаблонов Oracle (ОТЛ), boost::любой и Локи библиотека (та, что описана в 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/WMS@amohpadb.world" ); // 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.Желание состоит в том, чтобы иметь возможность объявить список типов столбцов, а затем:

  • возьмем тип T столбца
  • объявить переменную этого типа
  • применять olt_stream::operator >>(T&)
  • сохраните результат (в векторе boost::any)
  • выберите тип следующего столбца и повторяйте до тех пор, пока не будут обработаны все столбцы

Вы можете сделать все это с помощью Локи Typelist структура, специализация шаблонов и наследование.

С помощью библиотечных конструкций Loki вы также можете сгенерировать множество функций GetValue, которые возвращают значения соответствующего типа, выводя их из номера столбца (фактически номера типа в Typelist).

Нет, я не использовал это в производственном коде.

Почему?

  1. Мы должны поддерживать более 6 платформ с родной Платформа компиляторы. Достаточно сложно использовать STL в этой среде, не говоря уже о современных методах создания шаблонов .
  2. Разработчики, похоже, больше не следят за достижениями C ++.Мы используем C ++ когда приходится.У нас есть устаревший код с устаревшим дизайном.Новый код выполнен на чем-то другом, например, Java, Javascript, Flash.

Почти через 8 месяцев после того, как я задал этот вопрос, я, наконец, использовал какой-то TMP, я использую Список типов из интерфейсов для реализации QueryInterface в базовом классе.

Я использую его с boost:: statechart для больших statemachines.

Да, у меня есть, в основном для того, чтобы делать некоторые вещи, которые напоминают утиный ввод, когда я оборачивал устаревший API в более современный интерфейс C ++.

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

Например, вы не можете выполнять итерацию по элементам boost::tuple<>, потому что вы можете получить к ним доступ только во время компиляции.Вы должны использовать шаблонное метапрограммирование для достижения того, что было бы простым и прямолинейным на C ++, и это всегда это происходит, когда пользователи C ++ недостаточно осторожны, чтобы не переносить слишком много вещей во время компиляции.Иногда трудно понять, когда определенное использование логики времени компиляции станет проблематичным, и иногда программистам не терпится попробовать и протестировать то, что они прочитали у Александреску.В любом случае, на мой взгляд, это очень плохая идея.

Многие программисты до недавнего времени практически не использовали шаблоны из-за плохой поддержки компилятора.Однако, хотя с шаблонами было много проблем в pas, новые компиляторы имеют гораздо лучшую поддержку.Я пишу код, который должен работать с GCC на Mac и Linux, а также с Microsoft Visual C ++, и только с GCC 4 и VC ++ 2005 эти компиляторы действительно хорошо поддерживают стандарт.

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

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

  • Интеллектуальные указатели ( например ,Подсчитанные ссылки, копирование при записи и т.д.)
  • Математика поддерживает такие классы, как матрицы, векторы, сплайны и т.д.которые должны поддерживать различные типы данных и при этом оставаться эффективными.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top