Я знаю, что они реализованы крайне небезопасно в C / C ++. Разве они не могут быть реализованы более безопасным способом? Действительно ли недостатки макросов достаточно серьезны, чтобы перевесить огромную мощность, которую они предоставляют?
programming-languages
macros
Casebash
источник
источник
WithEvents
модификатору Visual Basic ? Вам нужно что-то вроде семантических макросов.Ответы:
Я думаю, что главная причина в том, что макросы лексические . Это имеет несколько последствий:
Компилятор не может проверить, является ли макрос семантически закрытым, то есть он представляет «единицу смысла», как функция. (Подумайте
#define TWO 1+1
- чтоTWO*TWO
равно? 3.)Макросы не печатаются как функции. Компилятор не может проверить, что параметры и возвращаемый тип имеют смысл. Он может проверять только расширенное выражение, использующее макрос.
Если код не компилируется, компилятор не может узнать, находится ли ошибка в самом макросе или в месте, где используется макрос. Компилятор либо сообщит о неправильном месте половину времени, либо он должен сообщить обоим, даже если один из них, вероятно, в порядке. (Подумайте
#define min(x,y) (((x)<(y))?(x):(y))
: Что следует делать , если компилятор типовx
иy
не совпадают или не реализоватьoperator<
?)Автоматизированные инструменты не могут работать с ними семантически полезными способами. В частности, вы не можете иметь такие вещи, как IntelliSense для макросов, которые работают как функции, но расширяются до выражения. (Опять
min
пример.)Побочные эффекты макроса не такие явные, как у функций, что может привести к путанице у программиста. (Рассмотрим снова
min
пример: при вызове функции вы знаете, что выражение дляx
вычисляется только один раз, но здесь вы не можете узнать, не глядя на макрос.)Как я уже сказал, это все последствия того, что макросы лексические. Когда вы пытаетесь превратить их во что-то более правильное, вы получаете функции и константы.
источник
Но да, макросы могут быть разработаны и реализованы лучше, чем в C / C ++.
Проблема с макросами заключается в том, что они по сути являются механизмом расширения синтаксиса языка , который переписывает ваш код во что-то другое.
В случае C / C ++ нет никакой фундаментальной проверки работоспособности. Если вы осторожны, все в порядке. Если вы допустили ошибку или злоупотребили макросами, вы можете столкнуться с большими проблемами.
Добавьте к этому, что многие простые вещи, которые вы можете делать с макросами (в стиле C / C ++), можно выполнять другими способами на других языках.
В других языках, таких как различные диалекты Лисп, макросы лучше интегрированы с синтаксисом основного языка, но вы все равно можете столкнуться с проблемами при объявлении в макросе «утечка». Об этом говорят гигиенические макросы .
Краткий исторический контекст
Макросы (сокращение от макро-инструкций) впервые появились в контексте ассемблера. Согласно Википедии , макросы были доступны в некоторых ассемблерах IBM в 1950-х годах.
В оригинальном LISP не было макросов, но они были впервые представлены в MacLisp в середине 1960-х годов: https://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user-defined-code -преобразование-появиться . http://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf . До этого fexprs предоставлял макроподобную функциональность.
Самые ранние версии C не имели макросов ( http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ). Они были добавлены около 1972-73 через препроцессор. До этого C поддерживал только
#include
и#define
.Макропроцессор M4 возник примерно в 1977 году.
Более поздние языки, по-видимому, реализуют макросы, в которых модель работы скорее синтаксическая, чем текстовая.
Поэтому, когда кто-то говорит о первичности конкретного определения термина «макро», важно отметить, что значение эволюционировало со временем.
источник
Как отмечает Скотт , макросы могут скрывать логику. Конечно, так же как и функции, классы, библиотеки и многие другие распространенные устройства.
Но мощная система макросов может пойти дальше, позволяя вам разрабатывать и использовать синтаксис и структуры, обычно не встречающиеся в языке. Это действительно может быть замечательным инструментом: специфичные для предметной области языки, генераторы кода и многое другое, и все это в комфортной среде одного языка ...
Однако этим можно злоупотреблять. Это может затруднить чтение, понимание и отладку кода, увеличить время, необходимое новым программистам для ознакомления с базой кода, и привести к дорогостоящим ошибкам и задержкам.
Таким образом, для языков, предназначенных для упрощения программирования (таких как Java или Python), такая система является анафемой.
источник
Макрос может быть реализован очень безопасно при некоторых обстоятельствах - например, в Лиспе макросы - это просто функции, которые возвращают преобразованный код в виде структуры данных (s-выражение). Конечно, Lisp значительно выигрывает от того, что он гомоичен, и от того, что «код - это данные».
Примером того, насколько простыми могут быть макросы, является пример Clojure, который задает значение по умолчанию, которое будет использоваться в случае исключения:
Тем не менее, даже в Лиспе общий совет: «Не используйте макросы, если это не нужно».
Если вы не используете гомоиконический язык, то макросы становятся намного сложнее, а различные другие варианты имеют некоторые подводные камни:
Кроме того, все, что может сделать макрос, в конечном итоге может быть достигнуто каким-либо другим способом на языке полного набора (даже если это означает написание большого количества шаблонов). В результате всей этой хитрости неудивительно, что многие языки решают, что макросы действительно не стоят всех усилий, чтобы их реализовать.
источник
Чтобы ответить на ваши вопросы, подумайте, для чего преимущественно используются макросы (Предупреждение: скомпилированный мозг).
#define X 100
Это можно легко заменить на:
const int X = 100;
#define max(X,Y) (X>Y?X:Y)
В любом языке, который поддерживает перегрузку функций, это можно эмулировать гораздо более безопасным для типов способом, перегружая функции правильного типа, или, в языке, который поддерживает обобщенные значения, обобщенной функцией. Макрос с радостью попытается сравнить что угодно, включая указатели или строки, которые могут скомпилироваться, но это почти не то, что вы хотели. С другой стороны, если вы сделали макросы безопасными по типу, они не дадут никаких преимуществ или удобства по сравнению с перегруженными функциями.
#define p printf
Это легко заменить функцией,
p()
которая делает то же самое. Это довольно сложно в C (требует использованияva_arg()
семейства функций), но во многих других языках, которые поддерживают переменное число аргументов функций, это намного проще.Поддержка этих функций в языке, а не в специальном макроязыке, проще, менее подвержена ошибкам и намного меньше сбивает с толку других, читающих код. На самом деле, я не могу придумать ни одного варианта использования макросов, который нельзя легко скопировать другим способом. Только место , где макросы действительно полезны, когда они привязаны к условной компиляции конструкций , таких как
#if
( и т.д.).По этому поводу я не буду спорить с вами, поскольку считаю, что непроцессорные решения условной компиляции в популярных языках чрезвычайно громоздки (например, внедрение байт-кода в Java). Но такие языки, как D, предлагают решения, которые не требуют препроцессора и являются не более громоздкими, чем использование условных выражений препроцессора, но при этом гораздо менее подвержены ошибкам.
источник
In fact, I can't think of a single use-case for macros that can't easily be duplicated in another way
: по крайней мере, в C вы не можете формировать идентификаторы (имена переменных), используя конкатенацию токенов, не используя макросы.enum
для определения условий и массив строк с константой для определения сообщений может привести к проблемам, если enum и массив вышли из синхронизации. Использование макроса для определения всех пар (enum, string) и последующее включение этого макроса дважды с другими правильными определениями в области видимости позволяет каждый раз помещать каждое значение перечисления рядом со своей строкой.Самая большая проблема, с которой я столкнулся при работе с макросами, заключается в том, что при интенсивном использовании они могут сделать код очень трудным для чтения и обслуживания, поскольку они позволяют скрыть логику в макросе, которую может или не может быть легко найти (и может быть, а может и нет) ).
источник
Начнем с того, что отметим, что MACRO в C / C ++ очень ограничены, подвержены ошибкам и не очень полезны.
MACRO, реализованные в, скажем, LISP или языке ассемблера z / OS, могут быть надежными и невероятно полезными.
Но из-за злоупотребления ограниченным функционалом в C они получили плохую репутацию. Так что никто больше не реализует макросы, вместо этого вы получаете такие вещи, как шаблоны, которые делают некоторые простые вещи, используемые макросами, и такие вещи, как аннотации Java, которые выполняют некоторые более сложные вещи, используемые макросами.
источник