Translating C preprocessor to Rust

user2665887

I'm porting some C code to Rust and that code contains a lot of things like this:

#define CONFIG_FLAG_NUMBER_23 1
#define THIS 10
#define THAT 11
#define THIS_AND_THAT (THIS + THAT)

#if CONFIG_FLAG_NUMBER_23
#define THIS_OR_THAT THIS
#else
#define THIS_OR_THAT THAT
#endif

#define ROOT_DIR "/root"
#define FILE_ONE ROOT_DIR "/file_one"
#define FILE_TWO ROOT_DIR "/file_two"

I decided to remove macros and replace them with constant expressions, but the attempts to do it in Rust weren't very successful:

static CONFIG_FLAG: bool = true;
static THIS: int = 10;
static THAT: int = 11;
static THIS_AND_THAT: int = THIS + THAT; // Okay, it works

// 1: Conditions
static THIS_OR_THAT: int = CONFIG_FLAG ? THIS : THAT; // Doesn't work, no conditional operator
static THIS_OR_THAT: int = if CONFIG_FLAG { THIS } else { THAT }; // Doesn't work, "if" is not basic enough for compile time

// 2: Strings
static ROOT_DIR: &'static str = "/root";
static FILE_ONE: &'static str = ROOT_DIR + "/file_one"; // Doesn't work, static strs can't be Added
static FILE_TWO: String = ROOT_DIR.to_string() + "/file_two"; // Don't even think about allocations in constant expressions!
static FILE_THREE: &'static str = concat!(ROOT_DIR, "/file_three"); // Doesn't work, concat! works only with literals

What would be the correct / least painful way to rewrite such code in Rust?

user2665887

Problem 1: Conditional expressions
Since configuration flags can be interpreted as integers, they can be used as indexes in arrays of variants.

// Configuration flag
static CONFIG_FLAG: uint = 1;

// Variants of any static type
type T = &'static str;
static VARIANT1: T = "True";
static VARIANT2: T = "False";

// Now you can select, but you have to invert either flag, or variants, or your brain
static SELECTED: T = [VARIANT1, VARIANT2][1 - CONFIG_FLAG];

fn main() {
    println!("{}", SELECTED);
}

Notes:

CONFIG_FLAG of type bool currently doesn't work in array index because of issue #5873,

// error: can't cast str to uint
static SELECTED: T = [VARIANT1, VARIANT2][1 - CONFIG_FLAG_BOOL as uint];

therefore you have to create one more static item and then use it in conditionals.

static CONFIG_FLAG_UINT: uint = CONFIG_FLAG_BOOL as uint;

Problem 2: Compile time string concatenation

Simple C macros nicely map into Rust macros, so you can use essentially the same approach for string concatenation as in C. The only difference is that you have to explicitly use concat! instead of just placing literals next to each other.

#![feature(macro_rules)]

// Define new macro-string with name $name assembled from arguments $arg
macro_rules! define_str (
    ($name: ident, $($arg: expr), +)
    =>
    (macro_rules! $name (
        () => (concat!($($arg), +))
    ));
)

// Some strings
define_str!(ROOT_DIR, "/root")
define_str!(SUB_DIR, ROOT_DIR!(), "/sub")
define_str!(FILE_NAME, SUB_DIR!(), "/file")
define_str!(NONSENSE, FILE_NAME!(), SUB_DIR!(), ROOT_DIR!())

fn main() {
    println!("{}", FILE_NAME!());
    println!("{}", NONSENSE!());
}

Notes:
I wanted to add !() to macro names automatically inside of define_str with additional macro like this,

macro_rules! add_bang_and_parens (
    ($arg: ident) => ($arg!());
    ($arg: expr) => ($arg);
)

but it seems like in macros pattern matching based on "types" of arguments is not currently possible.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related