Подтвердить что ты не робот

Каков сценарий подходящего использования #define в С++?

Я знаю основные правила, используйте inline, enum и const вместо #define, это не то, что я после этого вопроса. Я хочу знать, что считается приемлемым сценарием, в котором вы бы использовали макрос #define и как, на С++.

Пожалуйста, не ставьте вопрос или ссылки на вопросы "define vs const" или "препроцессор против компилятора", и я уже прошел через эффективный С++ Скоттом Мейерсом, и я знаю преимущества одного над другим.

Однако после часов и часов серфинга в сети я чувствую, что #define рассматривается как некоторый аутсайдер на С++, но я уверен, что должен быть случай, когда он может быть приемлемым, даже желательно, используй это.

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

4b9b3361

Ответ 1

Вот несколько сценариев, в которых использование #define является хорошим решением:

Добавление диагностической информации при сохранении сигнатуры функции:

#ifdef _DEBUG
#define Log(MSG)  Log((MSG), __FILE__, __LINE__);
#endif

Условная компиляция и включение охранников также являются хорошим примером (пример не указан, так как вы должны это понимать:)).

Код котла - еще один пример, но это можно легко злоупотреблять. Хорошим примером использования макросов для шаблонного кода является макрос BOOST_AUTO_TEST_CASE в Boost.UnitTest(худшим примером является набор макросов WinAPI, который сопоставляет Windows API с их макросами CHAR или WCHAR).

Другим хорошим примером является предоставление ключевых слов и настроек для компилятора:

#if (defined _WIN32) && (defined LIB_SHARED)
#   ifdef LIB_EXPORTS
#       define LIB_EXPORT __declspec(dllexport)
#   else
#       define LIB_EXPORT __declspec(dllimport)
#   endif /* LIB_EXPORTS */
#else
#   define LIB_EXPORT extern
#endif /* _WIN32 && LIB_SHARED */

Использование:

// forward declaration of API function:
LIB_EXPORT void MyFunction(int);

Ответ 2

Простая настройка для отладки/выпуска или для кроссплатформенного кода. Вот пример моей программы:

void Painter::render()
{
    if (m_needsSorting)
    {
        _sort();
    }
    for (GameObject* o : m_objects)
    {
        o->render();
#ifdef _DEBUG
        o->renderDebug();
#endif
    }
}

и еще один для win/ios:

#ifdef _WIN32

#include "EGL/egl.h"
#include "GLES2/gl2.h"
#include <Windows.h>

#ifdef _ANALYZE
#include <vld.h>
#endif

#else // IOS
#import <Availability.h>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import <Foundation/Foundation.h>
#endif

другое для библиотек:

#ifdef VECTRY_INLINE
#define vinline inline
#else
#define vinline 
#endif

и некоторые полезные вещи вроде этого:

#define MakeShared(T) \
    class T; \
    typedef shared_ptr<T> T##Ptr

Ответ 3

Один из немногих полезных случаев в С++ включает защитные устройства:

// A.h
#ifndef _A_H_
#define _A_H_

class A
{ /* ... */ };

#endif /* _A_H_ */

Ответ 4

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

Одним из примеров, где макросы являются "обязательными", является Boost.TTI (интроспекция типов объектов). Основной механизм каким-то образом злоупотребляет языком, чтобы создать пару мощных метафайлов, но нуждается в большом количестве шаблонов. Например, макрос BOOST_TTI_HAS_MEMBER_FUNCTION создает функцию mate, которая проверяет, имеет ли класс заданную функцию-член. Для этого требуется создать новый класс и не может быть коротким без макроса (примеры немакровых решений для решения проблемы здесь).

Также есть несколько раз, когда вам нужно будет использовать X-macro для генерации вашего кода. Очень полезно связывать вещи во время компиляции. Я не уверен, можно ли их полностью заменить или нет, но в любом случае вы можете найти некоторые действительно интересные примеры приложений здесь.

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

Ответ 5

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

Защиты включения заголовка - это одна из областей, где вы не можете использовать consts

И пример:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif /* GRANDFATHER_H */

Вы также можете проверить Почему кто-то использует #define для определения констант?

Еще одна вещь, которая добавляет, что #defines не относится к областям, поэтому нет способа создать пространство имен с ограниченным классом, где константные переменные могут быть охвачены классами. (Я знаю, что вы знаете разницу, но думали добавить его как это важно.)

Также, чтобы показать один пример, где используется #define:

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}