2010-08-13 4 views
15

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

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

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

+0

Ну, вы не можете использовать шаблоны в C, так что это одна из причин. – detly

+7

Какой код вы читали, если чувствуете, что «все профи используют макросы»? Все здравомыслящий код на C++, который я знаю, довольно трудно избежать. В C это немного отличается, потому что у вас нет много альтернатив. Кстати, почему это помечено C вообще? Ваш вопрос * звучит * довольно специфично для C++, указывая шаблоны и наследование. C и C++ - это разные языки, вы знаете. – jalf

+0

Точно, что @jalf сказал - они могут иметь некоторые сходства, но когда дело доходит до генерации кода, они совершенно разные планеты. Например, [Идиома генерации X-макроса/таблицы] (http://stackoverflow.com/questions/2927245/looking-for-a-good-explanation-of-the-table-generation-macro-idiom) – detly

ответ

16

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

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

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

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

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

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

  • это не поможет другим проанализировать вашу программу
  • вы получаете меньший двоичный
  • программа не тратить время Ф.О. регистрации на всех
  • компилятор может производить более оптимизированный код.
+0

Также часто используется для добавления кода, который работает в режиме отладки, но не в коде режима выпуска. –

+0

Это пример, когда макрос пришел в полезный, но не пример чего-то, что невозможно было сделать без макроса.Например, вы можете поместить блок #ifdef вокруг каждого вызова DoLog(), если вы понимаете, что я имею в виду. – thomasrutter

+1

Это шаблон или встроенная функция. Нет необходимости в макросах. – Basilevs

-1

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

No.

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

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

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

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

+2

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

+1

Согласны с Дэвидом. Есть проблемы, которые могут быть решены только с помощью макросов (элегантно относительный термин). –

+0

Нет, потому что C++ с макросом и без него Тьюринг завершен? :) Но есть много случаев, когда только макросы (а не шаблоны или встроенные строки) позволяют избежать дублирования кода. Я согласен с тем, что избегать макросов, когда это возможно, должно быть намерение, но честный ответ должен быть «да». – user396672

8

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

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

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

+3

Тем более, что для регистрации/метания вы обычно хотите автоматически извлекать имя файла и номер строки. –

-4

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

+2

Вот почему мы используем функции, а не повод для использования макросов. –

+0

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

+0

Это когда функция не может этого сделать, когда вы должны связаться с препроцессами макросов – TerryP

9

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

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

1

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

4

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

  • Множественная защита включения.
  • Макросы - единственный способ подражать символу. assert macro, компактная реализация строки const & 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 
} 
0

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

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

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

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

  • Друг классов
  • Макросы
  • GOTOS И еще.

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

0

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

 
#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, когда это возможно, но мне это не удалось. Таким образом.

Однако лямбды в C++ 0x позволяет встраивать произвольный код в «пользователе или библиотеки определенных утверждений languge» петлю такой Foreach, поэтому макрос область теряет значительную часть :)

0

Может быть, это бит вне темы, но я читал post о том, как уменьшить время компиляции.

И автор заметил, что включение каждой строки кода в один файл с макросом (да, это уродливо) уменьшило время и размер компиляции на сильное соотношение (~ 70% для обоих параметров).

Любое объяснение?

+0

Автор этого у меня есть некоторые объяснения, но я бы в значительной степени объяснил это: (1) неправильные методы кодирования - почему каждый исходный файл, кажется, включает в себя каждый заголовок, а не только те, которые относятся к нему? и (2) доступ к файловой системе в Windows ужасно медленный. –

+0

Да, мой вопрос был не совсем точным, но меня особенно удивляет уменьшение размера двоичного файла. Почему включение слишком большого заголовка, чем требуется, влияет на размер файла? – Nielk

0

Макросы являются решением для условного компиляции (по 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++.

0

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

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

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

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

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

Теперь, я все для того, чтобы отбросить этот макрос, как только найду, как не нарушать СУХОЙ. Идеи, очевидно, приветствуются ... и внешний сценарий НЕ лучше;)

Мои 2 цента.

0

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

Я обычно имеют заголовочный файл с именем DebugLog.h следующим Macro

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

Использование: DEBUG ("Test") будет что-то вроде:

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

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

Смежные вопросы