По уверенности в макросах

Я чувствую, каждый раз, когда я считал C или программу C++, та половина или больше из нее являются просто макросами. Я понимаю, что макросы могут быть прохладными, но их трудно отследить, отладить, и т.д. Не говоря уже о том, что большинство языков программирования даже не определяет что-то как макросы (хотя Perl6 будет иметь что-то вроде этого).

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

Вопрос, там проблемы, которые не могут быть решены без макросов? Макросы являются в конечном счете хорошей/плохой практикой? Когда я должен рассмотреть использование макроса?

16
задан 2 revs, 2 users 50% 13 August 2010 в 07:58
поделиться

12 ответов

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

Что-то вроде:

#ifdef WITH_LOGGING
    #define LOG( x ) DoLog( x )
#else
    #define LOG( x )
#endif

теперь вы используете его так:

LOG( L"Calling blahblahblah with " + getSomeStringHardToCompute() );

и в конфигурации с WITH_LOGGING у вас есть этот код, а иначе он полностью опущен - даже не присутствует в бинарном файле, и поэтому

  • он не помогает другим анализировать вашу программу
  • вы получаете более компактный бинарник
  • программа вообще не тратит время на ведение журнала
  • компилятор может производить более оптимизированный код.
18
ответ дан 30 November 2019 в 16:00
поделиться

Поведение отладки можно контролировать с помощью постоянных флагов или функций отладки. Итак, вот мой список неизбежных:

  • Защита от многократного включения.
  • Макросы - единственный способ преобразования символов в строку. макрос assert, компактная реализация 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
}
4
ответ дан 30 November 2019 в 16:00
поделиться

Макросы - это решение для условной компиляции ( 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)
    {
        //...
    }
}

Но встроенные функции и шаблоны заменяют другие варианты использования макросов в C ++.

1
ответ дан 30 November 2019 в 16:00
поделиться

Прямо из Эффективного C ++ Скотта Майера -> 1

Учитывая доступность констант и встроенных строк, потребность в препроцессоре уменьшается, но полностью не устраняется. Далеко не тот день, когда вы сможете отказаться от #include, а # ifdef / # ifndef продолжат играть важную роль в управлении компиляцией. Еще не время отказываться от препроцессора, но вам определенно стоит запланировать его более длительные и частые отпуска.

10
ответ дан 30 November 2019 в 16:00
поделиться

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

#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). Другой пример - диспетчеризация сообщений с использованием карты сообщений, т.е. генерация переключателя типа this:

switch(message_type)
{
case msg1: on_msg1(msg); break;
case msg2: on_msg2(msg); break;
...
}

и одновременная генерация объявлений методов обработчика on_msgX(msg) с использованием некоторой таблицы описания сообщений ("map")

Лично я стараюсь избегать макросов, когда это возможно, но в данном случае мне это не удалось.

Однако, лямбды в c++0x позволяют встраивать произвольный код в "user-or-library-defined languge statements", такие как циклы foreach, так что макрореальность теряет значительную часть :)

1
ответ дан 30 November 2019 в 16:00
поделиться

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

Например, я создал класс Enum, который оборачивает перечисление в struct (область видимости) и добавляет некоторую функциональность:

  • возможность итерации (что подразумевает порядок значений)
  • преобразование в/из строки (удобно для чтения/записи в файл, записи в журналы)

Для создания перечисления я использую макрос, который автоматически генерирует преобразователь (в/из) и вектор для итерации.

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

Итак, я за то, чтобы отказаться от этого макроса, как только я найду, как не нарушать DRY. Идеи приветствуются... и внешний скрипт НЕ лучше ;)

Мои 2 цента.

1
ответ дан 30 November 2019 в 16:00
поделиться

Вопрос в том, есть ли проблемы, которые нельзя решить без макросов?

Нет.

являются ли макросы в конечном итоге хорошей/хорошей практикой? Когда я должен рассмотреть возможность использования макроса?

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

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

Обратите внимание, что начиная с C99, C теперь может делать явные встроенные функции, используя ключевое слово inline, что уменьшает необходимость в макросах и даже имеет преимущества перед макросами.

-1
ответ дан 30 November 2019 в 16:00
поделиться

Макросы языка программирования хороши для того, для чего хороши все макросы: чтобы не набирать одно и то же снова и снова. Поэтому, если вы пишете одни и те же фрагменты кода во многих местах, почему бы не сделать из них макрос? Особенно если вы пишете библиотеку, использование макросов может облегчить жизнь тому, кто пытается использовать эту библиотеку. Посмотрите почти на любой набор инструментов GUI (Qt - один из примеров). Все они широко используют макросы.

-3
ответ дан 30 November 2019 в 16:00
поделиться

Вы смотрели на плохой код C++. Я использую макросы только в следующих местах:

  • защита заголовков
  • очень редкая условная компиляция
  • общий макрос выброса исключений
  • общий макрос отладки/записи вывода

Я не думаю, что этих четырех можно избежать.

9
ответ дан 30 November 2019 в 16:00
поделиться

Я думаю, что шаблоны и встроенные функции C ++ позволяют избежать макросов.

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

C (и, следовательно, C ++) - чрезвычайно гибкий язык программирования. Это здорово, потому что каждый может выработать свой собственный стиль и решить большинство проблем несколькими способами. Однако это тоже можно считать проблемой. На мой взгляд, проблема должна решаться не языком, а путем установления условностей.

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

  • классов друзей
  • макросов
  • GOTOs И больше.

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

1
ответ дан 30 November 2019 в 16:00
поделиться

Макросы, конечно, также полезны, когда вы хотите генерировать код во время предварительной обработки. Хотя этого можно избежать, используя шаблоны (см. вопрос и обсуждение в SO - Are C++ Templates just Macros in disguise?), вы можете использовать макросы, если это облегчит жизнь вашим пользователям - см. как проект 'googletest' (https://github.com/google/googletest/) эффективно использует макросы. Вы явно не хотите использовать макросы для генерации кода, который нуждается в отладке, вместо этого используйте шаблоны.

2
ответ дан 30 November 2019 в 16:00
поделиться

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

Обычно у меня есть файл заголовка DebugLog.h со следующим макросом

#define DEBUG(debugMessage) \
   printf("%s | %s [%d] - %s\n", __FILE__, __PRETTY_FUNCTION___, debugMessage);

Использование: ОТЛАДКА («Тест») выведет что-то вроде:

main.cpp | foo(void)[20] - Test

Вы можете настроить макрос для C ++ и других операторов отладки. Также можно изменить макрос для отправки результирующей строки в регистратор.

1
ответ дан 30 November 2019 в 16:00
поделиться