Is it possible for rust adding a PreCompiler

Notice #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
I have to write it 4 times in such a short source....

C's precompiler has huge advantage here, and I think it is easy to add it ----- it can be added by using a shell script to call cc before running cargo build, ------- the only problems I guess are editor issues, syntax colors, line indent, rust-fmt things.

#![allow(dead_code)]

use std::net::*;

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
#[derive(Debug)]
pub struct RouteItem {
    pfx: u8,
    target: IpAddr,
    ip: IpAddr,
    mac: Vec<u8>,
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
#[cfg_attr(not(target_os = "windows"), path = "posix/posix.rs")]
mod platform;
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
mod platform {}
pub use platform::*;

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
#[test]
fn test() {
    let v = none_host_route_get().unwrap();
    println!("{v:?}");
}


You might be looking for a crate like cfg_if - Rust

Rust's macro system is generally more powerful than the C preprocessor already.

5 Likes

What about organizing the code differently?

mod posix {}
mod windows {}

mod platform {
    #[cfg(any(target_os = "linux", target_os = "macos"))]
    pub use super::posix::*;

    #[cfg(target_os = "windows")]
    pub use super::windows::*;
}

mod common {
    use std::net::IpAddr;

    #[derive(Debug)]
    pub struct RouteItem {
        pfx: u8,
        target: IpAddr,
        ip: IpAddr,
        mac: Vec<u8>,
    }

    #[test]
    fn test() {
        let v = none_host_route_get().unwrap();
        println!("{v:?}");
    }
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub use common::*;
pub use platform::*;
7 Likes

For what it's worth, I think a third-party attribute macro would be able to declare module-level "config variables" , something like

#[cfg_let(LMW = any(target_os = "linux", target_os = "macos", target_os = "windows"))]
mod foo {
    #[cfg(LMW)] // this is expanded by the `cfg_let` macro
    fn bar() {}
}

You can't just write these without cfg, for any environment, only either can be compiled

Fair enough. Then you would have to add

#![cfg(any(target_os = "linux", target_os = "macos"))]

resp.

#![cfg(target_os = "windows")]

To the top of the respective modules.
I guess you cannot avoid some duplication of attributes in this case.

A cute trick I've used a couple times:

macro_rules! m {( $($tt:tt)* ) => { $($tt)* }};

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
m! {

#[derive(Debug)]
pub struct RouteItem {
    pfx: u8,
    target: IpAddr,
    ip: IpAddr,
    mac: Vec<u8>,
}

#[cfg_attr(not(target_os = "windows"), path = "posix/posix.rs")]
mod platform;
pub use platform::*;

#[test]
fn test() {
    let v = none_host_route_get().unwrap();
    println!("{v:?}");
}

}

The macro which just takes any source and emits it unchanged enables a single #[cfg] to apply to multiple items without putting them into a mod.

6 Likes