Всем привет.
После того как yeputons разочаровал меня сказав, что эту задачу нельзя решить адекватным способом, и дал ссылку на полезные данные по макросам, я решил, что этим всем нужно поделиться со всеми.
Значит приступим.
Для тех, кто не в курсе, макрос — это фрагмент кода, которому дается имя. Когда это имя используется — оно заменяется на содержание макроса. Есть два типа макросов: обычные и функциональные(на английском они называются object-like и function-like, но я бы назвал их именно так).
Обычный макрос — это простой идентификатор, который будет заменен фрагментом кода. В основном используется как константа, или чтобы дать имя списку чисел и/или константным значениям других типов.
Пример 1.#define MAXN 1000
Теперь всегда когда мы будем писать MAXN
будет использоваться число 1000
.
Пример 2.#define nums 1, 2, 3
Теперь вместо слова nums
будет подставляться 1, 2, 3
Пример 3.#define mc my_class_with_very_very_long_name
Теперь вместо написания иногда очень долгих имен типов мы просто напишем mc
.
Функциональный макрос — который будет использоваться как функция. Имеет очень много возможностей.
Пример 4.#define abs(x) ((x)>=0?(x):-(x))
Теперь мы можем использовать функцию abs(x), при чем x
может быть любого типа для которого будет работать сравнение x>=0
.
Пример 5.#define max(a,b) ((a)>(b)?(a):(b))
Такой макрос можно спокойно (ну почти спокойно... См. Предупреждение 3.) использовать для нахождения максимума, и как и в прошлом примере для корректности работы необходимо, чтобы выражение (a)>(b)
имело смысл.
Предостережение 1.
Все переменные имеет смысл, а иногда даже нужно, брать во скобки, чтобы избежать проблем со старшинством операций. Например, если в качестве аргумента пойдет выражение с битовыми операциями.
Макросы могут вызывать друг друга, и в отличии от функций макрос может вызывать макрос, который описан позже него. Но с рекурсией макросы не дружат — ни с прямой, ни с непрямой.
<a name="stringification>Перевод имени переменной в строку (англ. stringification. Кто-то может перевести это слово?). Можно получить имя переменной как строку.
Пример 6.#define id(x) #x
Тут я немного о ней рассуждал. Единственное скажу — поистине магическая штука...
Конкатенация строки к имени переменной. Сразу перейдем к примеру.
Пример 7.#define get(name) (get_##name())
Очень полезно в том же самом ООП, когда методов геттеров много. Хотя по сути жизнь и без этого прекрасна.
Очень много стандартных макросов в С++. Думаю очень много внимания этому уделять не стоит. Кто хочет — почитайте тут.
Переопределение и удаление макросов. Переопределение производится новым вызовом директивы #define name
, где name
имя уже используемого макроса. Макрос можно переопределить в обычный или функциональный независимо от того, каким он был до этого. Удаление макроса используется при помощи директивы #undef name
, где name
имя макроса.
Пример 8.#define func 2
— обычный макрос, который возвращает 2#undef func
— теперь func
это обычное имя переменной#define func 5
— снова обычный макрос#define func(x) ((x)+5)
— переопределение в функциональный
Макрос может быть переопределен даже во время его использования.
Пример 9.
#define f(a,b) ((a)*(b))
...
f(2,
#undef f
#define f 3
f)
Такой код спокойно возвратит 6. Компилятор при этом будет молчать.
Макросы с переменным числом аргументов. Принцип работы примерно такой же, как и в функциях и/или шаблонах с переменным числом аргументов. Примеры тут. От себя, к сожалению, ничего не добавлю.
Предостережение 2.
Не желательно передавать аргументами макроса функции. Лучше передать ее результат. Ибо если та переменная в макросе используется больше раза — то функция вызовется очень много раз.
Пример 10.
Вспомним макрос max
и попробуем запустить такой код max(x, func(y))
. Этот код после окончательной замены будет выглядеть вот так: ((x)>(func(y))?(x):(func(y))
. Как видим func(y)
вызывается два раза. В худшем случае это может существенно повлиять на время работы.
Перевод строки в макросе. Если нужно записать макрос в нескольких строках можно использовать перевод строки \
. Думаю к этому можно обойтись и без примера.
Борьба с нежелательной точкой с запятой.
Пример 11.
Допустим у нас был такой код:
#define cnt(x, y)\
{\
y = 0;\
while (x > 0) {\
++y;\
x /= 10;\
}
Этот код узнает сколько цифр в числе x
и записывает результат в y
(кстати в этом примере скобки не нужны, так как отправь мы в этот макрос не переменную, код не будет компилироваться). Тогда, если вставить его перед else
, компилятор заругается на синтаксическую ошибку. Эта проблема решается вот так:
#define cnt(x, y)\
do {\
y = 0;\
while (x > 0) {\
++y;\
x /= 10;\
} while (0)
И теперь после него можно всегда ставить точку с запятой.
И еще несколько примеров.
Пример 12.#define forn(i,a,b) for(int i = (a); i < (b); ++i)
Самый частый макрос, для упрощения написания циклов.
Пример 13.#define sum(a,b) ((a)+(b))
Бесполезный, но прикольный макрос, для нахождения оператора +
двух переменных. Сумма для чисел, конкатенация для строк и т.д.
Надеюсь эта запись будет полезна для вас.
UPD. Предупреждение 3.
Спасибо hellman_ за пример с инкрементом.
Даже скобки не всегда могут от всего спасти. Поэтому можно либо следить за всеми переменными, или более реальный вариант — использовать такой или похожий макрос:#define max(a,b) ((___x = (a)) > (___y = (b)) ? x : y)
Но тогда переменные ___x
и ___y
придется описать сразу. И тогда вылезают проблемы, ибо ___x
и ___y
у нас статистического типа и т.д. Поэтому полноценного варианта с помощью макроса я не знаю. Но в большинстве случаев первый работает очень хорошо. Могу посоветовать просто не кидать туда никакие выражения.