Macro systems inevitably wind up being used to create a specialized undocumented language that nobody but its creator understands.
I know how enticing they are, I designed and implemented one myself for the ABEL programming language. I used lots of clever C macros in my C programming, and was proud of them.
But, eventually, I removed all the macro usage, and quite preferred the resulting code. It was cleaner and easier to read.
It's not just C macros. It's the same for assembler macros. I've heard from others it's the same for other languages that rely on macros.
Essentially, macros are a cheap way to add power to a language. A better way is to add proper metaprogramming features. This is the route we chose to go with D, and it is satisfyingly successful.
I have seen this kind of ”flip-flop” behaviour people have with macros a few times. First you go all in, burn yourself, and then go to the other extreme.
Personally, i think macros are a good way to automate some common tasks, but you have to be carefull to keep them short. Also it is a good idea to prune macros periodically to remove what you dont need.
In Cpp, If you find yourself choosing weather to use a macro or a template; Choose the one which is more terse!
Also macros will always inline in debug while templates will generate functions in debug builds, without optimizations. This may be an important performance consideration at times.
I can hear the sound of a thousand LISP devs hurting in parens reading this comment lol.
Macros, as with most things (including even goto!) have their place, the problem is when they’re abused. But to say they’re never useful ever and you should instead always rely on language features is not something I agree with, and could even lead to language bloat if you need a full fledged feature for every little thing which would be trivially solved with a macro.
My unfettered opinion is that Lisp has not really caught on because it relies on macros to make it useful. Every project invents their own language on top of Lisp, incompatible with anyone else's.
It's like the problem with C++ before C++98. It had no string class, so everybody invented their own, all incompatible with everyone else's.
BTW, everyone says that they understand my point and use macros modestly and responsibly. Nearly all of them go on to create their own undocumented impenetrable language out of those macros.
It takes a programmer about 10 years of creating and using macros and dealing with other peoples' macros to come to the conclusion that the whole feature needs to be scrapped. Sadly, there aren't any shortcuts to this realization :-)
> My unfettered opinion is that Lisp has not really caught on because it relies on macros to make it useful. Every project invents their own language on top of Lisp, incompatible with anyone else's.
Been saying this for years - this way of programming is powerful for the lone hacker, but lethal for team efforts. I will never forget the guy who ported some weird function evaluation framework from Clojure to a Java app and then left for greener pastures, what he left behind was the gnarliest of mindfucks.
It also took the C++ community about 10 years to realize that the way iostreams was doing operator overloading to do pipelining was an abomination as well.
In the D community, we also strongly discourage operator overloading for any purpose other than creating arithmetic types.
I am doubtful that even WG21 as originally constituted would have accepted I/O Streams with its "Look at me, I've got operator overloading" operator abuse if it wasn't Stroustrup's own code. If some outsider had come along and said "Look at this slower, clumsier, operator abusing alternative to C's stdio" the committee might have quoted Stroustrups' own words condemning such abuse. "the ability to define new meanings for old operators can be used to write programs that are well nigh incomprehensible".
I'm with you up to a point on overloading, if it were up to me for example Rust would not implement Add and AddAssign on String, and certainly Java wouldn't special case += but we are where we are.
However Rust has several operators (fewer than C++ but still several) that aren't just for arithmetic types. Deref and DerefMut of course (used to implement smart pointers such as Arc), Index and IndexMut (for the indexing operator []) but also Try (implementation of the ? operator) and (though rather more distant into your future than Try if you write Stable Rust) the Function operator traits Fn, FnMut and FnOnce which represent callables.
Of course arguably Rust isn't overloading operators at all. Rust has no subtyping, and so whether you can Add or Multiply or Try something is a matter only of whether that type implements the associated Trait.
I think Rust's hygienic and declarative "by example" macros are very nice actually. You could of course do the same things with its procedural macros but that's messy and harder to maintain. Appropriate tools for the job, don't use a chainsaw to trim your rosebush.
I don't know D but it sounds like you do a lot of work at compilation which is good. I never understood why people took away the preprocessor but then forced the use of reflection which then breaks at runtime instead of breaking at compile time. When I write C# there would be so many opportunities for short preprocessor macros. Instead you either have to create a reflection monstrosity or copy/paste the same piece of code dozens of times.
I know how enticing they are, I designed and implemented one myself for the ABEL programming language. I used lots of clever C macros in my C programming, and was proud of them.
But, eventually, I removed all the macro usage, and quite preferred the resulting code. It was cleaner and easier to read.
It's not just C macros. It's the same for assembler macros. I've heard from others it's the same for other languages that rely on macros.
Essentially, macros are a cheap way to add power to a language. A better way is to add proper metaprogramming features. This is the route we chose to go with D, and it is satisfyingly successful.
Macros - just say no.