Зависит от макросов
Я чувствую, что каждый раз, когда я читаю программу на C или С++, половина или несколько из них - это просто макросы. Я понимаю, что макросы могут быть классными, но их сложно отследить, отладить и т.д. Не говоря уже о том, что большинство языков программирования даже не определяют что-то вроде макросов (хотя Perl6 будет иметь что-то подобное).
Я лично всегда нашел способ написать свой код без использования макросов, будь то с шаблонами, множественным наследованием и т.д. Я даже чувствовал, что я не хороший программист, потому что все профи используют макросы, и я стараюсь избегать их как можно больше.
Вопрос в том, есть ли проблемы, которые не могут быть решены без макросов? Являются ли макросы в конечном итоге хорошей/плохой практикой? Когда следует рассмотреть использование макроса?
Ответы
Ответ 1
Да, вот один. Когда вам нужно добавить трассировочный код в свою программу таким образом, чтобы одна конфигурация содержала его, а другая полностью опустила, вы должны использовать макросы.
Что-то вроде:
#ifdef WITH_LOGGING
#define LOG( x ) DoLog( x )
#else
#define LOG( x )
#endif
теперь вы используете его следующим образом:
LOG( L"Calling blahblahblah with " + getSomeStringHardToCompute() );
и в конфигурации с WITH_LOGGING
у вас есть этот код, и в противном случае он полностью опускается - даже не присутствует в двоичном формате, и поэтому
- это не поможет другим проанализировать вашу программу.
- вы получаете меньший двоичный файл
- программа не тратит время на регистрацию вообще
- компилятор может создать улучшенный оптимизированный код.
Ответ 2
Прямо от Скотта Майера Эффективный С++ → 1
Учитывая наличие констант и встроенных строк, ваша потребность в препроцессоре снижается, но не полностью устраняется. День далеко не близок, когда вы можете отказаться от #include, а # ifdef/# ifndef продолжать играть важную роль в управлении компиляцией. Это еще не время, чтобы уйти в отставку препроцессора, но вы обязательно должны планировать начать давать более продолжительные и более частые каникулы.
Ответ 3
Вы искали плохой код на С++. Места, в которых я использую макросы, ограничены:
- защита заголовков
- очень условная условная компиляция
- общий макрос исключения броска
- общий макрос вывода отладки/записи
Я не думаю, что этих четырех можно избежать.
Ответ 4
Отладочное поведение может контролироваться с помощью постоянных флагов или функций отладки. Итак, вот мой список неустранимых:
- Множественная защита включения.
- Макросы - единственный способ строкования символов. assert macro, компактная реализация const string и stringify (значение категории перечисления);
Пример:
const char* stringify(enum category value)
{
#define c(x) case x: return #x;
switch(value) {
c(CIRCLE)
c(RECTANGLE)
c(TRIANGLE)
default: return "UNKNOWN";
}
#undef c // the most important part
}
Ответ 5
Макросы, конечно, также полезны, когда вы хотите генерировать код во время предварительной обработки. Хотя этого можно избежать с помощью шаблонов (см. Этот вопрос и обсуждение - Являются ли шаблоны С++ только маскируемыми?, вы можете использовать макросы, если это делает жизнь вашего пользователям проще - посмотрите, как проект googletest (https://github.com/google/googletest/) использует макросы эффективно. Очевидно, вы не хотите использовать макросы для генерации кода, который требует отладки, вместо этого используйте шаблоны.
Ответ 6
Я думаю, что С++-шаблоны и встроенные функции делают макросы в значительной степени предотвратимыми.
Вездесущность макросов, вероятно, связана с тем, что существует много программистов на С++, которые раньше были программистами на C. Такие люди, вероятно, будут хорошо разбираться в использовании макросов (потому что иногда это действительно лучшее или единственное решение в чистом C), и, возможно, они не видят смысла в изучении более сложных функций С++, если они уже знают, как решить проблему. По крайней мере, в мире с открытым исходным кодом существует много конвертеров C, поэтому вы, естественно, встречаетесь с парадигмами C. Я не думаю, что вы плохой программист, если избегаете такой функции, многие люди делают это, как GOTO.
C (и поэтому С++) - чрезвычайно гибкий язык программирования. Это здорово, потому что каждый может развить свой собственный стиль и решить большинство проблем несколькими способами. Это, однако, также можно считать проблемой. На мой взгляд, это не проблема, которая должна решаться языком, а путем создания конвенций.
В С++ есть много функций, которые можно безопасно игнорировать. Возможно, есть странные особые случаи, когда такая функция действительно будет лучшим подходом, но в большинстве случаев вы можете жить без:
- Классы друзей
- Макросы
- GOTOS
И еще.
IMO, старший программист на С++ должен быть в состоянии, по крайней мере, читать их все свободно, но я ожидаю, что хороший программист внимательно рассмотрит, когда и если использовать печально известную функцию.
Ответ 7
Есть много проблем, которые я не могу решить без макросов.
Например, сериализация/десериализация некоторых структур
#define STRUCT_DESCRIPTION structname(MyStruct) member(int,a) member(double,b) member(long, c)
#include "declare_serializable_struct.h" // declares struct itself and generates serialization/deserializaton code
#undef STRUCT_DESCRIPTION
(BOOST_PP_SEQUENCE также может использоваться)
Другой пример - отправка сообщений с использованием карты сообщений, т.е. Создание такого переключателя:
switch(message_type)
{
case msg1: on_msg1(msg); break;
case msg2: on_msg2(msg); break;
...
}
и генерировать объявления метода обработчика on_msgX (msg) в одно и то же время, используя некоторую таблицу описания сообщений ( "map" )
Лично я пытаюсь использовать макросы avoiod, когда это возможно, но мне это не удалось.
Однако, lambdas в С++ 0x позволяет встраивать произвольный код в "пользовательские или библиотечно-определенные выражения языка" такие петли foreach, поэтому макрообласть теряет значительную часть:)
Ответ 8
Возможно, это немного не по теме, но я читал post о том, как уменьшить время компиляции.
И автор заметил, что включение каждой строки кода в один файл с макросом (да это уродливо) уменьшило время и размер компиляции на сильное соотношение (~ 70% для обоих параметров).
Любое объяснение?
Ответ 9
Макросы - это решение для условного компиляции (через ifdef
и ifndef
). Вот примеры:
1)
#ifndef MY_HEADER_HPP
#define MY_HEADER_HPP
//...
#endif
2)
#ifdef __cplusplus
#define BEGIN extern "C" {
#define END }
#define NULL (0);
#else
#define BEGIN
#define END
#define NULL ((void*)0);
#endif
//-------------------------
BEGIN
void my_function(char* str);
END
//-------------------------
void my_function(char* str)
{
if(str != NULL)
{
//...
}
}
Но встроенные функции и шаблоны заменяют другие применения макросов в С++.
Ответ 10
Я стараюсь избегать использования макросов в максимально возможной степени из-за их очевидных проблем с безопасностью/отладкой, однако есть моменты, когда макросы предлагают что-то, что нет в других средствах языка, как изящно, и в этом случае я предпочитаю использовать макрос просто потому, что это облегчает мою жизнь (и работу моих коллег-разработчиков).
Например, я создал класс Enum
, который переносит перечисление в struct
(область) и добавляет некоторые функции:
- возможность итерации (что подразумевает порядок значений)
- преобразование в/из строки (удобно читать/записывать в файл, записывать в журналы)
Чтобы создать перечисление, я использую макрос, который будет автоматически генерировать конвертер (от и до) и вектор для итерации.
Конечно, я мог обойтись без него, ведь макрос только для генерации кода. Но без него можно было бы нарушить DRY, а в моих собственных предпочтениях "DRY" > "Не использовать макросы". Поскольку после отладки макрос безопасен, тогда как нарушение DRY является кошмаром для обслуживания.
Теперь, я все для того, чтобы отбросить этот макрос, как только я нахожу, как не нарушать DRY. Идеи, очевидно, приветствуются... и внешний script НЕ лучше;)
Мои 2 цента.
Ответ 11
Я также пытаюсь избежать макросов, но для расширения отладки я не нашел способ распечатать имя файла, имя функции и номер строки при отладке.
Обычно у меня есть заголовочный файл DebugLog.h со следующим макросом
#define DEBUG(debugMessage) \
printf("%s | %s [%d] - %s\n", __FILE__, __PRETTY_FUNCTION___, debugMessage);
Использование: DEBUG ( "Test" )
выведет что-то вроде:
main.cpp | foo(void)[20] - Test
Вы можете настроить макрос для С++ и других операторов отладки. Также можно изменить макрос, чтобы отправить результирующую строку в журнал.
Ответ 12
Вопрос в том, есть ли проблемы, которые не могут быть решены без макросов?
Нет.
являются макросами в конечном счете хорошей/обратной практикой? Когда следует использовать макрос?
В языках, которые не поддерживают или не соблюдают ключевое слово inline
, макросы - отличный способ повторного использования кода, но в то же время избежать накладных расходов на вызов функции в любом коде, достаточно плотно заколенном для что имеет огромное значение.
Ваше разглашение кода, забитого макросами, вероятно, оправдано. Откачать и в некоторых случаях читать трудно. Но они действительно полезны в очень небольшом числе случаев, когда такая оптимизация действительно оправдана.
Обратите внимание, что с C99 C теперь может выполнять явные встроенные функции с помощью ключевого слова inline
, что уменьшает потребность в макросах и даже имеет преимущества перед используя макросы.
Ответ 13
Макросы языка программирования хороши для того, для чего подходят все макросы: избегать повторного ввода одних и тех же вещей. Итак, если вы обнаруживаете, что пишете одни и те же фрагменты кода во многих местах, почему бы не сделать из него макрос? Особенно, если вы пишете библиотеку, использование макросов может облегчить жизнь тем, кто пытается использовать эту библиотеку. Взгляните на почти любой инструментарий GUI (например, Qt). Все они широко используют макросы.