#ifdef like feature


#1

I am in great need of conditional compilation of pieces of code here and there. Is there a way to do something like this:

pub fn(&mut self,
            type:
           #ifdef _YES_
           Type0
           #else
           Type1
           #endif
           ) {
}

etc … as i am continuously testing against local db and network and need to do this to toggle between both etc.

How do i do this ?


#2

https://doc.rust-lang.org/stable/book/conditional-compilation.html may be helpful.


#3

i saw that link but that didn’t seem to do what i wanted. Can you pls code the above (my pseudo-code) in proper Rust if possible ? That will help me understand better i think.


#4

Rust does not do cut-and-paste like meta programming, so I don’t think an exact equivalent is possible, but you can get something similar using a type alias and the above mentioned conditional compiling:

#[cfg(_YES_)]
type Type = Type0;

#[cfg(not(_YES_))]
type Type = Type1;

pub fn do_something(&mut self, t: Type) {
    //...
}

I haven’t tested it, but it should work. It should select an alias, depending on if _YES_ is defined or not.


#5

Ah ok … The construct is not as i was hoping so it won’t be a bullet for all battles. Here’s more then:

pub fn funct(&self
#ifdef _YES_
, t: Type0
#endif
) {
#ifdef _YES_
let abc = some_func(SomeType::new());
#else
let abc = some_other_func(SomeOtherType::new());
#endif
}

// ------------ This i think I can do as explained in the Rust-Book -----------
#ifdef __YES_
fn some_func(...) { ... }
#else
fn some_other_func(...) { ... }
#endif
// ---------------------------------------

Hopefully after this is converted to proper Rust i’ll have covered most scenarios where i require this. Thanks !!


#6

That’s quite different. It’s still possible, but you won’t be able to “mix” them like that. You have to make two versions of funct, where one has the input and one doesn’t. These two versions has to be individually implemented, so it may look something like this:

#[cfg(_YES_)]
pub fn func(&self, t: Type0) {
    let abc = some_func(SomeType::new());
}

#[cfg(not(_YES_))]
pub fn func(&self) {
    let abc = some_other_func(SomeOtherType::new());
}


#7

Exactly … That is what i don’t want to do. It’s a huge function and then I’ve to maintain multiple versions of it. Is there no other way ? And is there going to be no other way ?


#8

There are three “obvious” solutions:

  1. Add separate functions for the common parts of the funct versions.
  2. Make a macro that implements the two versions for you.
  3. Connect a separate code generator or pre processor that works with strings instead of token trees.

I don’t think there is going to be an other way, when it comes to rustc, but I bet some third party tools will show up.


#9

Ah ok … i never investigated a lot but do you mean to say there is not going to be a full blown C/C++ like preprocessor/preprocessing-step ? And would this be provided by the third party (your 3rd point) ?


#10

Attributes in rust are essentially macros and macros are hygienic and operates on token trees, and I don’t think that is going to change any time soon, for various reasons. That (obviously) doesn’t stop anyone from wanting to use a more C/C++ like system, so I bet someone is going to build it, but I really doubt that it will be included in rustc. We have build scripts for cargo and those can do practically anything with your code files, so the preprocessor possibilities are just as endless.

I can’t say anything about your case, so take this with a grain of salt, but C/C++ style preprocessing can be quite messy when done wrong and comes with a number of pitfalls and foot guns. I would recommend that you try an other approach first and see if it works. It may be worth chopping the function into smaller ones and factor out parts that can be generalized. The two versions shouldn’t be too different, after all, if they are supposed to do the same thing, but slightly differently, right?


#11

Note: you are not using t, so I’m assuming you want to pass it to some_func

have you been introduced to closures?

pub fn funct(&self, abc: Box<Fn() -> AbcType>) {
    let abc = abc();
}

// do this anywhere...
// even possible at runtime to decide, but feel free to replace with cfgs
if yes {
    x.funct(|| some_func(t, ...))
} else {
    x.funct(|| some_other_func(...))
}

#12

IMHO, if you think you need such messy code somewhere, you’ve got serious problems with your architecture. I would step back and rethink your approach here. Do you really think it’s good to make other developers read spagetti code like this?


#13

I actually just recently ran into the same desire to have something like this, so I wrote a macro for it! Perhaps this could help out?


#14

Since it wasn’t mentioned, you can also do this using the cfg! macro:

if cfg!(_YES_) {
    let abc = some_func(SomeType::new());
} else {
    let abc = some_other_func(SomeOtherType::new());
}

#15

That will only work if the functions/types exist in all configurations.