I know that they are implemented extremely unsafely in C/C++. Can't they be implemented in a safer way? Are the disadvantages of macros really bad enough to outweigh the massive power they provide?
|
I think the main reason is that macros are lexical. This has several consequences:
Like I said, these are all consequences of the fact that macros are lexical. When you try to turn them into something more proper, you end up with functions and constants. |
|||||||||||||||||||||
|
|
Macros can, as Scott notes, allow you hide logic. Of course, so do functions, classes, libraries, and many other common devices. But a powerful macro system can go further, enabling you to design and utilize syntax and structures not normally found in the language. This can be a wonderful tool indeed: domain-specific languages, code generators and more, all within the comfort of a single language environment... However, it can be abused. It can make code harder to read, understand and debug, increase the time necessary for new programmers to become familiar with a codebase, and lead to costly mistakes and delays. So for languages intended to simplify programming (like Java or Python), such a system is an anathema. |
|||||||||||||||||||||
|
|
But yes, macros can be designed and implemented better than in C/C++. The problem with macros is that they are effectively a language syntax extension mechanism that rewrites your code into something else.
Brief Historical ContextMacros (short for macro-instructions) first appeared in the context of assembly language. According to Wikipedia, macros were available in some IBM assemblers in the 1950s. The original LISP didn't have macros, but they were first introduced into MacLisp in the mid 1960s: http://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user-defined-code-transformation-appear. http://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf. Prior to that, "fexprs" provided macro-like functionality. The earliest versions of C didn't have macros (http://cm.bell-labs.com/cm/cs/who/dmr/chist.html). These were added circa 1972-73 via a preprocessor. Prior to that, C only supported The M4 macro-preprocessor originated in circa 1977. More recent languages apparently implement macros where the model of operation is syntactic rather than textual. So when someone talks about the primacy of a particular definition of the term "macro", it is important to note that the meaning has evolved over time. |
|||||||||||||
|
|
To answer your questions, think about what macros are predominantly used for (Warning: brain-compiled code).
This can easily be replaced with:
In any language that supports function overloading, this can be emulated in a much more type-safe manner by having overloaded functions of the correct type, or, in a language that supports generics, by a generic function. The macro will happily attempt to compare anything including pointers or strings, which might compile, but is almost certainly not what you wanted. On the other hand, if you made macros type-safe, they offer no benefits or convenience over overloaded functions.
This is easily replaced by a function Supporting these features within a language rather than in a special macro language is simpler, less error prone and far less confusing to others reading the code. In fact, I can't think of a single use-case for macros that can't easily be duplicated in another way. The only place where macros are truly useful is when they are tied to conditional compilation constructs like On that point, I won't argue with you, since I believe that non-preprocessor solutions to conditional compilation in popular languages are extremely cumbersome (like bytecode injection in Java). But languages like D have come up with solutions that do not require a preprocessor and are no more cumbersome than using preprocessor conditionals, while being far less error-prone. |
|||||||||||||||
|
|
Macros can be implemented very safely in some circumstances - in Lisp for example, macros are just functions that return transformed code as a data structure (s-expression). Of course, Lisp benefits significantly from the fact that it is homoiconic and the fact that "code is data". An example of how easy macros can be is this Clojure example which specifies a default value to be used in the case of an exception:
Even in Lisps though, the general advice is "don't use macros unless you have to". If you aren't using a homoiconic language, then macros get much trickier and the various other options all have some pitfalls:
Furthermore, everything a macro can do can ultimately be achieved in some other way in a turing complete language (even if this means writing a lot of boilerplate). As a result of all this trickiness, it's not surprising that many languages decide that macros aren't really worth the all effort to implement. |
|||||||
|
|
Lest start by noting that MACROs in C/C++ are very limited, error prone, and not really that useful. MACROs as implemented in say LISP, or z/OS assembler language can be reliable and incredibly useful. But because of the abuse of the limited functionality in C they have a gathered a bad reputation. So nobody implements macros any more, instead you get things like Templates which do some of the simple stuff macros used to do and things like Java's annotations which do some of the more complex stuff macro's used to do. |
|||
|
|
|
The biggest problem I have seen with macros is that when heavily used they can make code very difficult to read and maintain since they allow you to hide logic in the macro that may or may not be easy to find (and may or may not be trivial). |
|||||||||
|

WithEventsmodifier? You'd need something like semantic macros. – Jeffrey Hantin Sep 18 '10 at 4:14