On 23/04/2025 01:01, Kaz Kylheku wrote:
On 2025-04-22, bart <bc@freeuk.com> wrote:
If you're going to criticize the macros, then hold the code constant.
>
If it is a given requirement that some code is to be written or
generated, and we have some macros which do that, are those macros a
"good" or "bad" solution?
The fact that Lua has some grotty macros in its VM doesn't speak
to a nice use of macros, like what I was talking about.
You comitted a slippery slope type fallacy, or similar, by bringing
that up. "Macros /can/ be layered to 10 layers deeper and make the logic
inscrutable, therefore all macros are a bad idea, including a simple,
single-expansion iteration macro for sqlite hashes."
Here, it produces neither faster code nor smaller.
That's neither here nor there; all that matters is whether it's a good
way to produce the code that the author wanted.
IMV, macros generally are a bad idea. They are especially bad with how they are implemented in C:
* Their implementation is complex and poorly defined.
* It is a language within a language: a quite different one that can be very challenging to code in, and even more challenging trying to understand or debug it
* They make hard to port software or to convert APIs if they are full of C macros (GTK2 headers use 4000 macros)
* Compiler error messages can get weird and confusing
* In C, macros can generate random, unbalanced bits of syntax
* They cause problems for tools such as smart editors
* Macros don't obey normal scope rules.
* Macros tend to get abused or over-used, and people try to be too clever
* Macro solutions tend to be ugly and tacky (see X-macros)
* Macro expansion can do too much:
#define length 200
#define width 100
struct {char* str; int length;} s;
s.length = 0; // oops!
Additionally, it may be possible to replace the implementation of the
macro such that it generates some other code that is faster, and such
that it remains compatible; all the calls to the macro remain valid, and
so new code for handling all the opcodes is generated.
The macro is called like this:
op_arith(L, l_addi, luai_numadd);
that's pretty abstract; chances are it could be preserved if
the implementation strategy were substantially altered.
What's the problem with using a function here?
>> Why use macros at all?
>
> You need to repeat some verbose coding pattern without copy-and-paste
> mistakes. If that coding pattern has a bug or has to be revised for any
> reason, you want to do it in one place.
>
> You need to control the evaluation of an argument expression, rather
> than just receive its value.
> You need to set up some code that declares variables or interacts
> with declared identifiers in scope.
>> They might be a big deal in your own language, but in my experience,
>> largely in C, they seem to cause nothing but grief.
>
> But:
>
> for_sqlite_hash(p, hash) { ... }
>
> has only one simple level of expansion; saves typing, no grief.
Most languages don't have C-style macros; how do they manage?
Some may not have macros at all; I think D, Java, Zig, Go.
I admit there are some use-cases that cannot easily be achieved by other means. For that reason I experimented with macros in my own two languages a few years ago. But those have a much smaller scope than C macros, while also being more integrated:
* A macro, which can take parameters, can only be an alias for an expression term, not random syntax
* Macro expansion takes place at a late stage, so it cannot define new symbol names
* Macro expansion only occurs for top-level names, so the M in M.A.B is expanded, but not in A.M.B.
* Macro names have normal scope rules
* Macro names can be imported and exported like any other named entity
Even with such a limited scheme, it can cover your examples, except for abstracting loops. But, given that the loop body in this case is only a single function call, that can easily be handled by a function.