Я просматривал некоторые зрелые проекты C ++ и заметил шаблон, в котором флаги препроцессора используются для включения функций во время компиляции.
Например:
#ifdef MY_WIDGET
Widget createMyWidget() {
// etc...
}
#endif
Тогда в другом месте кода:
#ifdef MY_WIDGET
widgets.push_back(createMyWidget());
// etc...
#endif
Мне это кажется ненужным, так как мы можем использовать шаблон стратегии, используя наследование или std::function
,
В настоящее время приложение пользователя может выглядеть следующим образом (где библиотека была скомпилирована с -DMY_WIDGET
):
#include <library/startApp.hpp>
int main() {
startApp(); // createMyWidget will be called by the library
return 0;
}
Но вместо этого мы могли бы перепроектировать библиотеку, чтобы пользователь мог написать это:
#include <library/startApp.hpp>
#include <my-widget-plugin/createMyWidget.hpp>
int main() {
const std::vector<Widget> widgets = { createMyWidget() };
startApp(widgets);
return 0;
}
Теперь в библиотеке нет переключателей компиляции, что значительно упрощает сборку, но мы все же можем расширить функциональность библиотеки. Это также предотвращает возможную путаницу в случае, когда кто-то компилирует библиотеку без функции, но затем пытается использовать эту функцию по ошибке.
Надлежащее использование const
позволяет скомпилированному двоичному файлу быть максимально эффективным в любом случае. Если виджеты нужно создавать лениво, мы можем передать вектор фабрик.
Являются ли переключатели функций на основе препроцессора просто менее управляемой версией шаблона стратегии?
Основное отличие заключается в том, когда выбор сделан.
Флаги прекомпилятора — это выбор, который вы делаете во время компиляции, прежде чем даже запускать свою программу один раз, тогда вы можете иметь несколько версий программы, каждая из которых делает что-то определенное и ничего более, что не может быть побито с точки зрения производительности любой другой альтернативой, которая применяется позднее. ,
Тогда мы имеем:
Шаблон стратегии (также известный как шаблон политики) является поведенческим шаблоном разработки программного обеспечения, который позволяет выбирать поведение алгоритма во время выполнения.
Википедия
Таким образом, в самой минимальной форме во время выполнения есть одно ветвление, скорее всего, в форме указателей на функции. Я согласен, что это минимальные накладные расходы, но это ненужный.
Вторым наиболее заметным отличием является семантическое, поскольку одно происходит во время компиляции, а другое — во время выполнения, когда переданное намерение не совпадает, в первом случае вы в основном говорите людям, что у вас есть функции, которые вы можете активировать или не использовать для своей программы. во втором случае вы говорите людям, что у вас есть несколько способов иметь дело с чем-то, и что один может быть выбран вместо других.
В общем, если вы рассматриваете различия как незначительные, вы можете выбрать ту, которая кажется более удобной, поскольку каждый выбор имеет свои небольшие недостатки (макроопределения имеют грубое поведение препроцессора и добавляют новый языковой уровень, шаблоны стратегии имеют ненужные накладные расходы и т. д.), но если оба подходят для вас, используйте определение, когда вы можете выбирать во время компиляции, и используйте стратегию, чтобы иметь возможность выбирать во время выполнения.
Шаблон стратегии по определению является алгоритмом времени выполнения. С другой стороны, функции препроцессора находятся во время компиляции. Это означает, что они разрешаются во время компиляции, а после компиляции их больше не существует. Я не уверен, что вы знаете об этом, но я бы сказал, что вы. Итак, ваш вопрос сводится к следующему: зачем мне нужны функции препроцессора во время компиляции?
Ну, кроме очевидного обоснования разнообразия, рассмотрим ситуацию, когда в шаблоне вашей стратегии используются две разные библиотеки. Теперь, если вы используете команду препроцессора, вам не нужно ссылаться на ветку, которая не нужна вашей программе. Если вы используете шаблон стратегии, Вы должны связать все! Ссылки на вещи, которые вам не нужны, просто плохой стиль. Это также может быть нежизнеспособным по разным причинам (как упомянул Себастьян в комментариях, из-за платформы или лицензии или других ограничений).