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