Когда / почему (если когда-либо) я должен думать о выполнении общего программирования / мета-программирования - PullRequest
17 голосов
/ 11 июня 2009

ИМХО, ОЙ, шаблоны проектирования имеют смысл, и я смог применить их практически.

Но когда дело доходит до "общего программирования / метапрограммирования" типа Modern C ++, я просто растерялся.

- Это новая парадигма программирования / дизайна?

- Это просто ограничено "развитием библиотеки"? Если нет, то какие ситуации проектирования / кодирования требуют использования метапрограммирования / общего программирования.

- Значит ли использование шаблонов, что я занимаюсь общим программированием?

Я много гуглил по этой теме, но не понимаю БОЛЬШОЙ КАРТИНЫ полностью. Также смотрите этот пост .


После прочтения рассуждений здесь, до сих пор, я уверен (возможно, все еще не правильно):

а) Общее программирование и метапрограммирование - это две разные концепции.

Ответы [ 9 ]

30 голосов
/ 11 июня 2009

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

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

Обычно, если вы хотите решить проблему с помощью метапрограммирования, вы, вероятно, захотите использовать соответствующие библиотеки повышения для выполнения тяжелой работы. Boost.TypeTraits и, конечно, Boost.Mpl могут действительно упростить для вас вещи. Но это не то, что вам нужно , чтобы знать, и это не то, что вам, вероятно, понадобится очень часто.

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

Каждый раз, когда вы создаете экземпляр std::vector, вы используете общее программирование. Каждый раз, когда вы используете пару итераторов для обработки последовательности значений, вы используете общее программирование. Универсальное программирование - это просто идея, что ваш код должен быть настолько универсальным, насколько это возможно, и должен работать независимо от того, какие типы включены в него. Std :: vector не требует, чтобы содержащийся тип реализовывал интерфейс «ICanBeContained» (помните, как Java требует, чтобы все было извлечено из Object для того, чтобы оно было сохранено в классе контейнера? Что означает, что примитивные типы упаковываются в блоки, и что мы теряем безопасность типов. Это не общее и бессмысленное ограничение.)

Код для итерации последовательности с использованием итераторов является общим и работает с итераторами любого типа или даже с простыми указателями.

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

Часто, когда вы используете интерфейсы в ООП, это не означает, что тип может изменяться во время выполнения (хотя, конечно, это происходит время от времени), но позволяет вам переключаться на другой тип во время компиляции ( возможно, введение фиктивного объекта во время тестов, а не использование полноценной реализации) или просто для разделения двух классов. Универсальное программирование может сделать это без необходимости выполнять утомительную работу по определению и поддержке интерфейса. В этих случаях универсальное программирование означает, что вам нужно писать и поддерживать меньше кода, и вы получаете лучшую производительность и лучшую безопасность типов. Так что да, вы обязательно должны чувствовать себя как дома с общим программированием. C ++ не очень хороший язык ООП. Если вы хотите строго придерживаться ООП, вам следует переключиться на Java или другой, более ориентированный на ООП язык. C ++ позволяет писать OO-код, но зачастую это не лучшее решение. Есть причина, по которой почти вся стандартная библиотека опирается на общее программирование, а не на ООП. В стандартной библиотеке очень мало наследования или полиморфизма. Им это не нужно, и код стал проще и мощнее без него.

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

Я думаю, что xtofl действительно прибил это: общее программирование - это то, что ваш код не знает типа. (Std :: vector не заботится , или не нужно знать , какой тип хранится в нем. Это просто работает.)

С другой стороны, метапрограммирование связано с вычислениями типов. Для типов T0 и T1 мы можем определить тип T2, точно так же, как мы можем, для целых чисел N0 и N1 мы можем определить N2, который является суммой N0 и N1.

Библиотека Boost.Mpl имеет очевидный пример этого. В вашем обычном коде, если у вас есть целые числа N0, N1 и N2, вы можете создать std :: vector, содержащий эти три значения. Затем я могу использовать другой алгоритм для вычисления индекса, а затем извлечь значение, хранящееся в этом месте в векторе.

Учитывая типы T0, T1 и T2, мы можем создать mpl :: vector, содержащий эти три типа . Теперь я могу использовать другой алгоритм для вычисления индекса во время компиляции и извлечения типа , хранящегося в этом месте в векторе.

11 голосов
/ 11 июня 2009

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

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

Метапрограммирование применимо в совершенно другой области. Этот домен, действительно, довольно новый, и требует своего собственного исследования. Это это тоже весело!

Одним из очень полезных и распространенных шаблонов метапрограммирования является концепция «черты / политика».

9 голосов
/ 11 июня 2009

C ++ Шаблонное метапрограммирование - это мощная техника запутывания кода, которая применима к ряду приложений:

  • Когда вы хотите написать код, который никто в вашей команде не может понять
  • Когда вам нужен код, который вы не сможете понять через 7 дней после его написания
  • Когда производительность кода важнее для вас, чем удобство сопровождения
  • Когда вы хотите иметь возможность перечислить «Шаблон метапрограммирования» как навык в своем резюме.
  • Когда вам нужно написать код, который вряд ли будет работать на многих реальных компиляторах
  • Если вы предпочитаете есть лезвия, а не использовать макросы препроцессора

Еще один случай:

  • Если вы хотите, чтобы вы знали, как работают библиотеки Boost, или хотите внести в них свой вклад.

Различие между «универсальным программированием» (например, контейнеры STL, auto_ptrs и т. Д.), Для которого были разработаны шаблоны C ++, и «метапрограммирование шаблонов» (использование системы шаблонов, чтобы заставить компилятор эффективно «запускать алгоритмы» для вам) важно.

Я все в пользу первого, но мне трудно увидеть реальные преимущества последнего.

5 голосов
/ 11 июня 2009

Для расширенных шаблонов и техник я рекомендую: Современный дизайн C ++ , Андрей Александреску

C ++ - жесткий язык, практически не имеющий возможностей для самоанализа во время выполнения. Многие проблемы, с которыми вы столкнетесь, можно решить с помощью шаблонов. Кроме того, язык шаблонов полон по Тьюрингу, что позволяет, среди прочего, генерировать сложные структуры данных и предварительно вычислять значения во время компиляции. Для многих сценариев это может быть больше проблем, чем оно того стоит.

3 голосов
/ 17 июня 2010

Один ответ, который еще не был дан: хотя общее программирование и метапрограммирование действительно довольно сильно отличаются друг от друга, они оба были предназначены для решения (по существу) одной и той же проблемы: как избавиться от шаблонного примера. Они также воплощены не в одном, а в двух сокращениях принципов разработки программного обеспечения: СУХОЙ (не повторяйся) и DIE (дублирование - зло) . Это современная перефразировка бритвы Оккама , то есть, что "сущности не должны умножаться сверх необходимости" ( entia non sunt multiplicanda praeter needitatem ). [Кто знал, что логики 14-го века могут придумать что-то полезное для разработки программного обеспечения 21-го века?].

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

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

3 голосов
/ 11 июня 2009

Когда:

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

  • Не нравится, что c ++ статически типизирован? Используйте boost :: any.
  • Любите лямбда-исчисление, но вы должны c ++? Используйте boost: лямбда.
  • Есть EBNF и нужен парсер? Используйте boost :: spirit.

Почему:

Это круто. Вы можете впечатлить ваши даты (возможно).

1 голос
/ 11 июня 2009

Простой пример:

template<typename T>
void
swap(T& var1, T& var2)
{
  T var3 = var1;
  var1 = var2;
  var2 = var3;
}

int a = 2;
int b = 3;
swap(a, b);

float c = 400.0f;
float d = 500.0f;
swap(c, d);

поменяйте местами значения двух переменных одного типа.

С помощью шаблонов мы избегаем детального создания функций для различных типов комбинаций , подобных этим:

void
swap(int& var1, int& var2)
{
  int var3 = var1;
  var1 = var2;
  var2 = var3;
}

void
swap(float& var1, float& var2)
{
  float var3 = var1;
  var1 = var2;
  var2 = var3;
}

Указанные выше функции будут автоматически создаваться компилятором, в моем примере, если swap () вызывается где-то в коде с int или float , переменными.

1 голос
/ 11 июня 2009

Когда у вас осталось достаточно времени и вы хотите, чтобы сыграл .

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

0 голосов
/ 11 июня 2009

Думаю, если вы не разработчик библиотеки, то лучше забыть о метапрограммировании шаблонов. Я полагаю, что он бесполезен в производственном коде, создает больше проблем, а затем дает преимущества: «Как только вы начнете его использовать, вы потеряете гибкость, будучи привязанными к этому решению. Очень трудно отказаться от кода, сваренного на основе шаблонов, после того, как вы начал использовать его. И это проблема, так как шаблоны недостаточно гибки ".

P.S. Конечно, я не имею в виду контейнеры шаблонов, свопы ... но похожий на Александреску код

...