简体   繁体   中英

Pass a constant into a procedural macro

I am making a macro to expand geometric algebra expressions, and in order for the macro to work, it needs to know to know the type of geometric algebra (3D VGA, 2D PGA, etc.) at compile time.

I could do this easily by passing the type (3D PGA) in implicitly at each call of eq:

let a = eq!((3, 0, 1): e1 + e2);
let b = eq!((3, 0, 1): 3 + e12);
println!("{:?}", eq!((3, 0, 1): a + b))

or

let a = eq!("PGA3d": e1 + e2);
let b = eq!("PGA3d": 3 + e12);
println!("{:?}", eq!("PGA3d": a + b))

or by creating a specific macro for each type:

let a = pga_3d!(e1 + e2);
let b = pga_3d!(3 + e12);
println!("{:?}", pga_3d!(a + b))

but I'd rather not have to write out the type at every single eq, call or create a specific macro for every single type someone could want (there are an infinite amount of types and I want to be able to, at least theoretically. handle them all)? Is there a way to define the type as a const or with another procedural macro in order to have all eq. calls to know the specified type, (eq: needs to know what the type actually is in order to expand ) Ideally I'd like the code to look like this

const TYPE: (usize, usize, usize) = (3, 0, 1);
// or 
set_type!(3, 0, 1);

fn main() {
    let a = eq!(e1 + e2); // eq somehow knows to use 3D PGA because of TYPE or set_type!
    let b = eq!(3 + e12);
    println!("{:?}", eq!(a + b));
}

I believe I've found a workable solution to the problem I was having. The solution I found was to have the lib.rs file containing the procedural macros, read from a json file created alongside the cargo.toml file where the library was imported:

ga_macros_test
│   cargo.toml       <-- ga_macros is imported as a dependency here
│   type.json        <-- type is specified here
└───src
│   │   main.rs      <-- eq! can be used here with the specified type
│
└───ga_macros
    │   cargo.toml   <-- proc_macro imported here as dependency
    └───src
        │   lib.rs   <-- eq defined here along with a read for "type.json"

Inside the lib.rs file:

extern crate proc_macro;
use proc_macro::*;

use lazy_static::lazy_static;
use std::{fs, collections::HashMap};

extern crate serde_json;

lazy_static! {
    static ref TYPE: (usize, usize, usize) = {
        if let Ok(str) = fs::read_to_string("type.json") {
            let json: HashMap<&str, Vec<usize>> = serde_json::from_str(str.as_str()).expect("The json file couldn't be parsed");

            (json["algebra"][0], json["algebra"][1], json["algebra"][2])
        } else {    
            (3, 0, 0) // 3D Vectorspace Geometric Algebra is the default
        }
    };
}

#[proc_macro]
pub fn eq(tokens: TokenStream) -> TokenStream {
    /*Expand expression using TYPE*/
}

This allows ga_macros to support arbitrary types, read the type while expanding the macro, and provide a single place where type is defined and can be changed. Another benefit is that more information could be added to type.json which could then better specialize eq. to the user's needs Link to example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM