Inventing unique variables in macros?

I’ve got a macro that I’d like to look like this:

-define(_decode_bool(Variable, Input, Rest),
    {Variable, Rest} = case Input of
        <<0:8/big, Rest_/binary>> -> {false, Rest_};
        <<1:8/big, Rest_/binary>> -> {true, Rest_}
    end
).

It’s used in generated code that looks like this:

decode_preferences(Bin) ->
    ?_decode_bool(LikesCats, Bin, Rest1),
    ?_decode_bool(LikesDogs, Rest1, Rest2),
    {#{likes_cats => LikesCats, likes_dogs => LikesDogs}, Rest2}.

But when I write it like that, I get a warning: “variable ‘Rest1’ exported from ‘case’”. This is similar to my earlier question.

So, to force scoping, I’ve had to introduce a function, like this anonymous one, here:

-define(_decode_bool(Variable, Input, Rest),
    {Variable, Rest} = (fun
        (<<0:8/big, Rest/binary>>) -> {false, Rest};
        (<<1:8/big, Rest/binary>>) -> {true, Rest}
    end)(
        Input
    )
).

I’m wondering if this is the best I can do. Specifically: is there any way to do some token-pasting magic with Rest_ in the original macro (using ?LINE, for example) to make those variables unique, so the compiler doesn’t warn about exporting them?

That is essentially what assert.hrl does, additionally wrapping all expressions in a begin … end block (not sure why).

Does it have to be a macro? If it is for performance reasons, perhaps you can inline a function instead?

1 Like

Now this was interesting and I had to know why :grin:

From the way back machine :

Hello,

This makes typos such as missing commas between two assertions (e.g. ?assert(true) ?assert(true)) syntax errors instead of silently compiling and failing with a badfun error at runtime.

This won’t break any existing code as parenthesed expressions and blocks have the same precedence and none of these macros can be used as patterns.

git fetch https://github.com/nox/otp.git eunit-macros-blocks

https://github.com/nox/otp/compare/erlang:maint...eunit-macros-blocks
https://github.com/nox/otp/compare/erlang:maint...eunit-macros-blocks.patch

Regards,


Anthony Ramine

2 Likes

I don’t understand why you want a macro.
What’s wrong with

1 Like

The thrice-accursed markup scheme seems to have deleted my sample code, which went something like this:

decode_bool(<<Bit:8/big, Rest/binary>>) →
{ Bit =/= 0, Rest }.

whatever_it_was(Bin0) →
{ Likes_Cats, Bin1 } = decode_bool(Bin0),
{ Likes_Dogs, Bin2 } = decode_bool(Bin1),
{ #{likes_cats => Likes_Cats, likes_dogs => Likes_Dogs}, Bin2 }.

decode_bool/1 can of course be inlined.
My further comment, also deleted by the ever-helpful site (gosh I so miss the old mailing list), was that there must be something we’ve not been told about the actual problem that makes a macro seem like a good idea.

way back in the 90s the idea was that
begin end
would block the export of variables from .
Why wasn’t that followed up?

But will it be inlined?

The decoders are machine-generated; the macros (also for int8, int16, int32, length-prefixed arrays, etc.) are in a header file. This allows them to be swapped out for variants that do logging without causing churn in the machine-generated files.

I guess, however, that the primitive helper functions could also be in a header file; that’ll probably work.

1 Like