4

I'm looking to create a Rust macro that can do what this does in C.

#define V(a,b,c,d) 0x##a##b##c##d

Which when called with:

V(7B,B0,B0,CB)

Will simply have the following hexadecimal number placed in the code at compile time:

0x7BaB0bB0cCDd

Trying something like this:

macro_rules! gen_hex_num {
    ($a:expr , $b:expr , $c:expr , $d:expr) => (
        0x($a)a($b)b($c)c($d)d
    )
}

Produces an error:

error: macro expansion ignores token `a` and any following
 --> src/main.rs:3:16
  |
3 |         0x($a)a($b)b($c)c($d)d
  |               ^
  |
note: caused by the macro expansion here; the usage of `gen_hex_num!` is 
likely invalid in expression context

Documentation and other questions don't seem to cover this scenario.

6ft Dan
  • 2,365
  • 1
  • 33
  • 46
  • For the record why do you need this ? What is the use case ? – Stargateur Sep 06 '18 at 17:23
  • To generate forward-tables and reverse-tables to be hardcoded in a binary for AES 256 bit encryption/decryption. See https://github.com/paulej/AESCrypt/blob/17e557fc5c263ac1a8488a0339a4177e7e583743/Linux/src/aes.c#L182-L267 for the C version. – 6ft Dan Sep 06 '18 at 17:28
  • For the record I was mistaken about how the C macro worked. The output is a u32 number like `0x7BB0B0CD`. But the answer given still shows me what I need to know. – 6ft Dan Sep 06 '18 at 21:30
  • I was thinking the same but I was too lazy to verify ;) as you said it doesn't change anything – Stargateur Sep 06 '18 at 22:15

1 Answers1

8

Nope.

Rust macros can only process and generate whole tokens, not token fragments. Something like 7b is not a token, so you won't be able to write a macro that would match it.

It seems like the main reason to use this macro in C is to make the number more readable. That isn't really necessary in Rust, because underscores are allowed in numeric literals. So you can just write 0x7b_b0_b0_cb instead.

You can approximate the original macro by accepting numbers and doing math, something like the following:

macro_rules! gen_hex_num {
    ($a:expr, $b:expr, $c:expr, $d:expr) => {
        ($a << 24) | ($b << 16) | ($c << 8) | ($d)
    }
}

But you have to call it like gen_hex_num!(0x7B, 0xB0, 0xB0, 0xCB), which probably defeats the purpose.

trent
  • 25,033
  • 7
  • 51
  • 90
  • I'd put the numeric literals up front; readability is the only reason to use a macro in C that I can see. – Matthieu M. Sep 07 '18 at 06:43
  • Actually it is needed for much more than being readable. There are four patterns for the for parts of hex in abcd, dabc, cdab, and bcda. Then each of these orders are used for generating both forward and reverse tables. So with the above number `0x7BB0B0CD` being the `abcd` form the `0xCD7BB0B0` would be the `dabc` pattern. I created the code example here for hex generating as individual macros for each of the 4 variations and another macro for the hex numbers which takes just the macro name `$mcro:ident` of any one of the 4 macros to be called for all 256 hex numbers for each table. – 6ft Dan Sep 08 '18 at 23:42