Rust 宏

标准库提供的宏

env! 硬编码方式读取包信息

@see https://github.com/mookid/broken_utils/blob/master/rust/ff/src/main.rs

const BIN_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

json!

macro_rules! json {
    ($($key: ident : $value: expr), *) => {{
        let mut map = std::collections::HashMap::new();
        $(
            map.insert(stringify!($key), $value.clone());
        )*
        map
    }};
}

fn main() {
    let user = json! {
        name: "张三",
        age: "18岁"
    };
    println!("{:?}", user);

    let obj = json! {
        id: 0.3_f64,
        value: 0.1+0.2
    };
    println!("{:?}", obj);
}

json! 允许逗号可有可无

macro_rules! json {
    ($($key: ident : $value: expr$(,)*)*) => {{
        let mut map = std::collections::HashMap::new();
        $(
            map.insert(stringify!($key), $value.clone());
        )*
        map
    }};
}

fn main() {
    let user = json! {
        name: "张三"
        age: "18岁"
    };
    println!("{:?}", user);

    let obj = json! {
        id: 0.3_f64,,,,
        value: 0.1+0.2
    };
    println!("{:?}", obj);
}

json! 允许最后一行逗号可有可无

macro_rules! json {
    ($($key: ident : $value: expr),* $(,)*) => {{
        let mut map = std::collections::HashMap::new();
        $(
            map.insert(stringify!($key), $value.clone());
        )*
        map
    }};
}

fn main() {
    let user = json! {
        name: "张三",
        age: "18岁",
    };
    println!("{:?}", user);

    let obj = json! {
        id: 0.3_f64,
        value: 0.1+0.2,,,
    };
    println!("{:?}", obj);
}

安卓 NDK 日志宏

#![allow(unused)]
pub const ANDROID_LOG_VERBOSE: i32 = 2;
pub const ANDROID_LOG_DEBUG: i32 = 3;
pub const ANDROID_LOG_INFO: i32 = 4;
pub const ANDROID_LOG_WARN: i32 = 5;
pub const ANDROID_LOG_ERROR: i32 = 6;
pub const FMT: *const u8 = "%s\0".as_ptr();

extern {
    // int __android_log_write(int prio, const char* tag, const char* text);
    // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
    pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
}

#[macro_export]
macro_rules! log {
    ($($arg: tt)+) => {{
        let tag = format!("{}:{}\0", file!(), line!());
        let mut str = format!($($arg)+);
        str.push('\0');
        unsafe {
            log::__android_log_print(log::ANDROID_LOG_DEBUG, tag.as_ptr(), log::FMT, str.as_ptr());
        }
    }}
}

进化版本:

#![allow(unused)]
pub const FMT: *const u8 = "%s\0".as_ptr();

#[allow(non_camel_case_types)]
pub enum Level {
    ANDROID_LOG_VERBOSE = 2,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
}

extern {
    // int __android_log_write(int prio, const char* tag, const char* text);
    // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
    pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
}

#[macro_export]
macro_rules! log {
    ($type: expr, $($arg: tt)+) => {{
        let level: log::Level = $type;
        let tag = format!("{}:{}\0", file!(), line!());
        let mut str = format!($($arg)+);
        str.push('\0');
        unsafe {
            log::__android_log_print(level as i32, tag.as_ptr(), log::FMT, str.as_ptr());
        }
    }}
}

#[macro_export]
macro_rules! error {
    ($($arg: tt)+) => {{
        log!(log::Level::ANDROID_LOG_ERROR, $($arg)+);
    }}
}

#[macro_export]
macro_rules! debug {
    ($($arg: tt)+) => {{
        log!(log::Level::ANDROID_LOG_DEBUG, $($arg)+);
    }}
}

为了代码可移植性,修改为:

#![allow(unused)]
pub const FMT: *const u8 = "%s\0".as_ptr();

#[allow(non_camel_case_types)]
pub enum Level {
    ANDROID_LOG_VERBOSE = 2,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
}

extern {
    // int __android_log_write(int prio, const char* tag, const char* text);
    // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
    pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
}

#[macro_export]
#[cfg(target_os = "android")]
macro_rules! log {
    ($type: expr, $($arg: tt)+) => {{
        let level: log::Level = $type;
        let tag = format!("{}:{}\0", file!(), line!());
        let mut str = format!($($arg)+);
        str.push('\0');
        unsafe {
            log::__android_log_print(level as i32, tag.as_ptr(), log::FMT, str.as_ptr());
        }
    }}
}

#[macro_export]
#[cfg(not(target_os = "android"))]
macro_rules! log {
    ($type: expr, $($arg: tt)+) => {{
        let level: log::Level = $type;
        let str = format!($($arg)+);
        match level {
            log::Level::ANDROID_LOG_ERROR => {
                eprintln!("{}:{} -> {}\0", file!(), line!(), str);
            },
            _ => {
                println!("{}:{} -> {}\0", file!(), line!(), str);
            },
        }
    }}
}

#[macro_export]
macro_rules! error {
    ($($arg: tt)+) => {{
        log!(log::Level::ANDROID_LOG_ERROR, $($arg)+);
    }}
}

#[macro_export]
macro_rules! debug {
    ($($arg: tt)+) => {{
        log!(log::Level::ANDROID_LOG_DEBUG, $($arg)+);
    }}
}


// Using by log::d!() and log::e!()
// @see: https://stackoverflow.com/questions/26731243/how-do-i-use-a-macro-across-module-files
pub use debug as d;
pub(crate) use error as e; // If not use #[macro_export] to export macro, must be use pub(crate)

哈哈,没想到吧,还能再增加特性!

#![allow(unused)]
pub const FMT: *const u8 = "%s\0".as_ptr();

#[allow(non_camel_case_types)]
pub enum Level {
    VERBOSE = 2,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    WTF,
}

#[cfg(target_os = "android")]
extern {
    // int __android_log_write(int prio, const char* tag, const char* text);
    // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
    pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
}

#[macro_export]
#[cfg(target_os = "android")]
macro_rules! log {
    ($type: expr, $($arg: tt)+) => {{
        let level: log::Level = $type;
        let tag = format!("{}:{}\0", file!(), line!());
        let mut str = format!($($arg)+);
        str.push('\0');
        unsafe {
            log::__android_log_print(level as i32, tag.as_ptr(), log::FMT, str.as_ptr());
        }
    }}
}

#[macro_export]
#[cfg(not(target_os = "android"))]
macro_rules! log {
    ($type: expr, $($arg: tt)+) => {{
        let level: log::Level = $type;
        let str = format!($($arg)+);
        match level {
            $crate::log::Level::ERROR => {
                eprintln!("{}:{} -> {}\0", file!(), line!(), str);
            },
            _ => {
                println!("{}:{} -> {}\0", file!(), line!(), str);
            },
        }
    }}
}

#[macro_export]
macro_rules! error {
    ($($arg: tt)+) => {{
        log!(log::Level::ERROR, $($arg)+);
    }}
}

#[macro_export]
macro_rules! debug {
    ($($arg: tt)+) => {{
        log!(log::Level::DEBUG, $($arg)+);
    }}
}

macro_rules! wtf {
    ($($arg: tt)+) => {{
        #[cfg(target_os = "android")]
        log!(log::Level::ERROR, $($arg)+);
        #[cfg(not(target_os = "android"))]
        dbg!($($arg)+);
    }}
}

macro_rules! empty {
    ($($arg: tt)+) => (())
}

// Using by log::d!() and log::e!()
// @see: https://stackoverflow.com/questions/26731243/how-do-i-use-a-macro-across-module-files
#[cfg(debug_assertions)]
pub use debug as d;
#[cfg(not(debug_assertions))]
pub use empty as d;
#[cfg(debug_assertions)]
pub(crate) use error as e; // If not use #[macro_export] to export macro, must be use pub(crate)
#[cfg(not(debug_assertions))]
pub(crate) use empty as e;
// wtf using dbg!()
#[cfg(debug_assertions)]
pub(crate) use wtf;
#[cfg(not(debug_assertions))]
pub(crate) use empty as wtf;

上面代码太乱,还有个Bug:android下的wtf!()无法通过编译!
修改:

#![allow(unused)]

#[allow(non_camel_case_types)]
pub enum Level {
    VERBOSE = 2,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    WTF,
}

#[cfg(not(target_os = "android"))]
pub mod log {
    macro_rules! log {
        ($type: expr, $($arg: tt)+) => {{
            use $crate::log::Level;

            let level: Level = $type;
            let str = format!($($arg)+);
            match level {
                Level::ERROR => {
                    eprintln!("{}:{} -> {}\0", file!(), line!(), str);
                },
                _ => {
                    println!("{}:{} -> {}\0", file!(), line!(), str);
                },
            }
        }}
    }

    macro_rules! wtf {
        ($($arg: expr $(,)?)+) => {{
            dbg!($($arg,)+);
        }}
    }

    pub(crate) use log;
    pub(crate) use wtf;
}

#[cfg(target_os = "android")]
pub mod log {
    extern {
        // int __android_log_write(int prio, const char* tag, const char* text);
        // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
        pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
    }

    macro_rules! log {
        ($type: expr, $($arg: tt)+) => {{
            use $crate::log::Level;

            let level: Level = $type;
            let tag = format!("{}:{}\0", file!(), line!());
            let mut str = format!($($arg)+);
            str.push('\0');
            unsafe {
                log::__android_log_print(level as i32, tag.as_ptr(), log::FMT, str.as_ptr());
            }
        }}
    }
    
    macro_rules! wtf {
        ($($arg: expr $(,)?)+) => {{
            $(
                $crate::log::log!(Level::ERROR, "{} = {:?}", stringify!($arg), $arg);
            )+
        }}
    }

    pub(crate) use log;
    pub(crate) use wtf;
}

#[cfg(debug_assertions)]
pub mod inner {
    macro_rules! d {
        ($($arg: tt)+) => {{
            $crate::log::inner::log!(Level::DEBUG, $($arg)+);
        }}
    }

    macro_rules! e {
        ($($arg: tt)+) => {{
            $crate::log::inner::log!(Level::ERROR, $($arg)+);
        }}
    }

    pub(crate) use super::log::{log, wtf};
    pub(crate) use d;
    pub(crate) use e;
}

#[cfg(not(debug_assertions))]
pub mod inner {
    macro_rules! empty {
        ($($arg: tt)+) => (())
    }

    pub(crate) use empty as log;
    pub(crate) use empty as d;
    pub(crate) use empty as e;
    pub(crate) use empty as wtf;
}

pub use inner::*;

更新:不删除i和log宏,修复无法在其它crate使用的问题:

#![allow(unused)]

#[allow(non_camel_case_types)]
pub enum Level {
    VERBOSE = 2,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    WTF,
}

#[cfg(not(target_os = "android"))]
pub mod log {
    #[macro_export]
    macro_rules! log {
        ($type: expr, $($arg: tt)+) => {{
            use $crate::log::Level;

            let level: Level = $type;
            let str = format!($($arg)+);
            match level {
                Level::ERROR => {
                    eprintln!("{}:{} -> {}\0", file!(), line!(), str);
                },
                _ => {
                    println!("{}:{} -> {}\0", file!(), line!(), str);
                },
            }
        }}
    }

    #[macro_export]
    macro_rules! wtf {
        ($($arg: expr $(,)?)+) => {{
            dbg!($($arg,)+);
        }}
    }

    pub use log;
    pub use wtf;
}

#[cfg(target_os = "android")]
pub mod log {
    extern {
        // int __android_log_write(int prio, const char* tag, const char* text);
        // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
        pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
    }

    #[macro_export]
    macro_rules! log {
        ($type: expr, $($arg: tt)+) => {{
            use $crate::log::Level;

            let level: Level = $type;
            let tag = format!("{}:{}\0", file!(), line!());
            let mut str = format!($($arg)+);
            str.push('\0');
            unsafe {
                log::__android_log_print(level as i32, tag.as_ptr(), log::FMT, str.as_ptr());
            }
        }}
    }
    
    #[macro_export]
    macro_rules! wtf {
        ($($arg: expr $(,)?)+) => {{
            $(
                $crate::log::e!("{} = {:?}", stringify!($arg), $arg);
            )+
        }}
    }

    pub use log;
    pub use wtf;
}

#[cfg(debug_assertions)]
pub mod inner {
    #[macro_export]
    macro_rules! d {
        ($($arg: tt)+) => {{
            $crate::log::log::log!(Level::DEBUG, $($arg)+);
        }}
    }

    pub use super::log::wtf;
    pub use d;
}

#[cfg(not(debug_assertions))]
pub mod inner {
    #[macro_export]
    macro_rules! empty {
        ($($arg: tt)+) => (())
    }

    pub use empty as d;
    pub use empty as wtf;
}

pub mod common {
    #[macro_export]
    macro_rules! i {
        ($($arg: tt)+) => {{
            $crate::log::log::log!(Level::INFO, $($arg)+);
        }}
    }

    #[macro_export]
    macro_rules! e {
        ($($arg: tt)+) => {{
            $crate::log::log::log!(Level::ERROR, $($arg)+);
        }}
    }

    pub use super::log::log;
    pub use i;
    pub use e;
}

pub use inner::*; // d!() and wtf!() will be delete in release version.
pub use common::*;

安卓修复:

#![allow(unused)]

#[allow(non_camel_case_types)]
pub enum Level {
    VERBOSE = 2,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    WTF,
}

#[cfg(not(target_os = "android"))]
pub mod log {
    #[macro_export]
    macro_rules! log {
        ($type: expr, $($arg: tt)+) => {{
            use $crate::log::Level;

            let level: Level = $type;
            let str = format!($($arg)+);
            match level {
                Level::ERROR => {
                    eprintln!("{}:{} -> {}\0", file!(), line!(), str);
                },
                _ => {
                    println!("{}:{} -> {}\0", file!(), line!(), str);
                },
            }
        }}
    }

    #[macro_export]
    macro_rules! wtf {
        ($($arg: expr $(,)?)+) => {{
            dbg!($($arg,)+);
        }}
    }

    pub use log;
    pub use wtf;
}

#[cfg(target_os = "android")]
pub mod log {
    pub const FMT: *const u8 = "%s\0".as_ptr();

    extern {
        // int __android_log_write(int prio, const char* tag, const char* text);
        // int __android_log_print(int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 3, 4)));
        pub fn __android_log_print(level: i32, tag: *const u8, fmt: *const u8, ...);
    }

    #[macro_export]
    macro_rules! log {
        ($type: expr, $($arg: tt)+) => {{
            use $crate::log::{Level, log};

            let level: Level = $type;
            let tag = format!("{}:{}\0", file!(), line!());
            let mut str = format!($($arg)+);
            str.push('\0');
            unsafe {
                log::__android_log_print(level as i32, tag.as_ptr(), log::FMT, str.as_ptr());
            }
        }}
    }
    
    #[macro_export]
    macro_rules! wtf {
        ($($arg: expr $(,)?)+) => {{
            $(
                $crate::log::e!("{} = {:?}", stringify!($arg), $arg);
            )+
        }}
    }

    pub use log;
    pub use wtf;
}

#[cfg(debug_assertions)]
pub mod inner {
    #[macro_export]
    macro_rules! d {
        ($($arg: tt)+) => {{
            $crate::log::log::log!(Level::DEBUG, $($arg)+);
        }}
    }

    pub use super::log::wtf;
    pub use d;
}

#[cfg(not(debug_assertions))]
pub mod inner {
    #[macro_export]
    macro_rules! empty {
        ($($arg: tt)+) => (())
    }

    pub use empty as d;
    pub use empty as wtf;
}

pub mod common {
    #[macro_export]
    macro_rules! i {
        ($($arg: tt)+) => {{
            $crate::log::log::log!(Level::INFO, $($arg)+);
        }}
    }

    #[macro_export]
    macro_rules! e {
        ($($arg: tt)+) => {{
            $crate::log::log::log!(Level::ERROR, $($arg)+);
        }}
    }

    pub use super::log::log;
    pub use i;
    pub use e;
}

pub use inner::*; // d!() and wtf!() will be delete in release version.
pub use common::*;

文档

https://doc.rust-lang.org/reference/macros.html

END

posted @ 2021-05-27 16:49  develon  阅读(213)  评论(1编辑  收藏  举报